/* * Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of Business Objects nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Packager.java * Creation date: (March 14th 2000 7:32:25 PM) * By: Luke Evans */ package org.openquark.cal.compiler; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import org.openquark.cal.compiler.ExpressionAnalyzer.Visitor; import org.openquark.cal.compiler.io.EntryPoint; import org.openquark.cal.compiler.io.EntryPointSpec; import org.openquark.cal.foreignsupport.module.Prelude.AlgebraicValue; import org.openquark.cal.internal.machine.MachineFunctionImpl; import org.openquark.cal.internal.module.Cal.Internal.CAL_Optimizer; import org.openquark.cal.internal.module.Cal.Internal.CAL_Optimizer_internal; import org.openquark.cal.machine.CALExecutor; import org.openquark.cal.machine.CodeGenerator; import org.openquark.cal.machine.MachineFunction; import org.openquark.cal.machine.Module; import org.openquark.cal.machine.Program; import org.openquark.cal.machine.StatusListener; import org.openquark.cal.module.Cal.Core.CAL_Prelude; import org.openquark.cal.runtime.CALExecutorException; import org.openquark.cal.runtime.ExecutionContext; import org.openquark.cal.services.BasicCALServices; import org.openquark.cal.services.WorkspaceManager; import org.openquark.util.UnsafeCast; /** * Warning- this class should only be used by the CAL compiler implementation. It is not part of the * external API of the CAL platform. * <P> * The Packager currently is used to instrument analysis phases that occur after the compiler * generates Expression values for the core functions in a module. This includes applying the global CAL optimizer, * anti-alias transformations (and other optimizations done in the ExpressionAnalyzer, such as simple inlining of a * few well known functions). Its end output can be used by the machines for code generation. * @author LWE */ public abstract class Packager { public static final String OPTIMIZER_LEVEL = "org.openquark.cal.optimizer_level"; /** Percentage of process completed by packaging a module **/ private double moduleIncrement = 0.5; /** * Exception raised on a packaging error */ public static final class PackagerException extends Exception { private static final long serialVersionUID = 4401713179185214061L; /** * Construct a PackagerException from a message */ public PackagerException(String message) { super(message); } } private Module currentModule; /** * If this flag is false the inliner will not be used no matter what the client requests. Otherwise * this inliner will be used if requested. This is temporary until I add a command line flag. */ private static int optimizerLevel = 0; { String ol = System.getProperty(OPTIMIZER_LEVEL); if (ol == null){ // leave it at zero } else{ // Let the error propagate up. optimizerLevel = Integer.parseInt(ol); } } /** * This is needed to access functions defined in the current module since * the .cmi file is not yet created. * * QualifiedName -> CoreFunction */ private Map<QualifiedName, CoreFunction> inlinableFunctions = new HashMap<QualifiedName, CoreFunction>(); private final Program program; private final CodeGenerator codeGenerator; private final Set<StatusListener> statusListeners = new HashSet<StatusListener>(); /** * The CAL optimizer. */ private CALExecutor optimizer_executor = null; private EntryPoint optimizer_entryPoint = null; /** * Construct a RunPackager which packages directly to a Program then executes it. * @param program Program. */ public Packager(Program program, CodeGenerator cg) { this.program = program; this.codeGenerator = cg; if (program.useOptimizer()) { useOptimizer(); } } /** * Abort this package. * @param logger the logger to use for logging error messages. */ public void abort(CompilerMessageLogger logger) { // Wait for the code generator to finish any file i/o. codeGenerator.finishedGeneratingCode(logger); } /** * Close the package. * @param logger the logger to use for logging error messages. */ public void close(CompilerMessageLogger logger) throws PackagerException { // Wait for the code generator to finish any file i/o. codeGenerator.finishedGeneratingCode(logger); } /** * Gets the ModuleTypeInfo for modules that have already been wrapped by the Packager. Returns null * if the ModuleTypeInfo doesn't exist. * @return ModuleTypeInfo * @param moduleName */ public ModuleTypeInfo getModuleTypeInfo(ModuleName moduleName) { Module requestedModule = program.getModule(moduleName); if (requestedModule == null) { return null; } return requestedModule.getModuleTypeInfo(); } /** * Create a new module object into which future 'stores' will place program objects. * @param foreignClassLoader the classloader to use to resolve foreign classes for the module. */ public void newModule(ModuleName name, ClassLoader foreignClassLoader) throws PackagerException { // Add it to the Program program.addModule(name, foreignClassLoader); for (final StatusListener l : statusListeners) { l.setModuleStatus(StatusListener.SM_NEWMODULE, name); l.incrementCompleted(moduleIncrement); } } /** * Adds an existing module to the program. * Used by the serialization code to set up the packager * since the compiler accesses module type info via the packager. * @param m */ void addModule(Module m) { program.addModule(m); // This module has been successfully comiled/packaged. Update the associated timestamp in // the program. This prevents re-compilation if the module hasn't changed. program.putCompilationTimestamp(m.getName(), new Long(System.currentTimeMillis())); // Inform any status listeners that this module is now loaded. for (final StatusListener statusListener : statusListeners) { statusListener.setModuleStatus(StatusListener.SM_LOADED, m.getName()); } } /** * Store this core function in the package into the current module. * @param coreFunction - the function to be stored * @throws PackagerException */ public void store(CoreFunction coreFunction) throws PackagerException { // save function info for inlining. if (optimizerLevel > 0) { inlinableFunctions.put(coreFunction.getName(), coreFunction); } storeItem(coreFunction); } /** * Store this core function in the package into the current module. * @param coreFunction * @throws Packager.PackagerException */ private void storeItem(CoreFunction coreFunction) throws Packager.PackagerException { // Save code objects (currently only one for a supercombinator - labels may refer to offsets) try { coreFunction.setForAdjunct(codeGenerator.isForAdjunct()); // At this point we want to package the expression form of the super combinator, along // with a description of the initial environment (i.e. parameters). MachineFunction machineFunction = getMachineFunction(coreFunction); getCurrentModule().addFunction (machineFunction); for (final StatusListener l : getStatusListeners()) { l.setEntityStatus(StatusListener.SM_COMPILED, machineFunction.getName()); } } catch (NullPointerException e) { // No module to copy into (or something worse) throw new Packager.PackagerException("No active module in which to store the core function " + coreFunction); } } /** * @param coreFunction a machine-independent representation of the function. * @return the machine-specific implementation for the core function */ public abstract MachineFunction getMachineFunction(CoreFunction coreFunction); /** * Switches the current module to be the specified module. * The module must have already been added to the package. * @param name the name of the module to switch to */ public void switchModule(ModuleName name) throws PackagerException { currentModule = program.getModule(name); if (currentModule == null) { throw new PackagerException("switchModule: module " + name + " does not exist."); } } /** * Return the compiled program object. */ public Program getProgram() { return program; } /** * The packager will load the optimizer so that for all modules subsequently * compiled the optimizer will be run. */ public void useOptimizer() { if (optimizerLevel < 1) { return; } final String defaultWorkspaceFile = "cal.optimizer.cws"; BasicCALServices calServices = BasicCALServices.make(defaultWorkspaceFile); calServices.getWorkspaceManager().useOptimizer(false); CompilerMessageLogger messageLogger = new MessageLogger(); if (!calServices.compileWorkspace(null, messageLogger)) { System.err.println(messageLogger.toString()); } final ExecutionContext executionContext = calServices.getWorkspaceManager().makeExecutionContextWithDefaultProperties(); WorkspaceManager workspaceManager = calServices.getWorkspaceManager(); Compiler compiler = calServices.getCompiler(); optimizer_entryPoint = compiler.getEntryPoint( EntryPointSpec.make(CAL_Optimizer_internal.Functions.optimize), CAL_Optimizer.MODULE_NAME, messageLogger); if (messageLogger.getNMessages() > 0) { System.err.println(messageLogger.toString()); } optimizer_executor = workspaceManager.makeExecutor(executionContext); // Initialize preludeTypeConstants { ModuleTypeInfo moduleTypeInfo = workspaceManager.getModuleTypeInfo(CAL_Prelude.MODULE_NAME); preludeTypeConstants = new PreludeTypeConstants(moduleTypeInfo); OptimizerHelper.setPreludeTypeConstants(preludeTypeConstants); } } /** * Return the current module. * @return Module */ public Module getCurrentModule() { return currentModule; } public Set<StatusListener> getStatusListeners() { return statusListeners; } public void addStatusListener (StatusListener listener) { if (!statusListeners.contains (listener)) { statusListeners.add (listener); } } public void removeStatusListener (StatusListener listener) { statusListeners.remove (listener); } public void setModuleIncrement (double d) { moduleIncrement = d; } /** * Determine which functions can be treated as alias to another function. * e.g. foo x y = blah x y; foo is an alias of blah. * @param functionsList */ private void determineFunctionAliases (List<MachineFunction> functionsList) { // The first step is to determine aliases based on function definitions // for all the code labels. // Once this is done we can do a deep alias resolution. i.e. handle cases // like a is an alias of b is an alias of c. // Once the deep alias resolution is done we can check for incompatibilities // caused by argument strictness. // Step 1. Set aliasOf members based on function definitions. Iterator<MachineFunction> functions = functionsList.iterator(); while (functions.hasNext()) { MachineFunction mf = functions.next(); // Get the name of the aliased function. (may be null) QualifiedName aliasOf = aliasOf (mf); mf.setAliasOf(aliasOf); } // Steps 2 and 3. Do deep resolution of aliases and // check strictness compatability. functions = functionsList.iterator(); while (functions.hasNext()) { MachineFunction mf = functions.next(); // Get the name of the aliased function. (may be null) QualifiedName aliasOf = compatibleStrictnessWithAliasedFunction(mf); if (aliasOf != null) { // Follow any alias chains to the end. MachineFunction aliasFunction = getCurrentModule().getFunction(aliasOf); while (aliasFunction != null && aliasFunction.getAliasOf() != null) { aliasOf = aliasFunction.getAliasOf(); aliasFunction = getCurrentModule().getFunction(aliasOf); } // The function which is being aliased may represent a literal value if (aliasFunction != null && aliasFunction.getLiteralValue() != null) { // Set the expression of the current function to be the literal. mf.setExpression(new Expression.Literal(aliasFunction.getLiteralValue())); } else { // Mark the current function as being an alias. mf.setAliasOf(aliasOf); } } else { // Just null out the alias field of the current function mf.setAliasOf(null); } // if (aliasOf != null) { // // // Need to update the strongly connected components // Set ccc = mf.getStronglyConnectedComponents(); // if (ccc != null) { // Set newSet = new HashSet(); // Iterator it = ccc.iterator(); // while (it.hasNext()) { // String name = (String)it.next(); // MachineFunction cmf = getCurrentModule().getFunction(name); // if (cmf.getAliasOf() != null) { // newSet.add(cmf.getAliasOf().getUnqualifiedName()); // } else { // newSet.add(name); // } // } // // it = newSet.iterator(); // while (it.hasNext ()) { // String name = (String)it.next(); // MachineFunction cmf = getCurrentModule().getFunction(name); // if (cmf != null) { // cmf.getCoreFunction().setStronglyConnectedComponents(newSet); // } // } // } // } } } /** * If the given function is simply an alias for another function return the name * of the other function. * * One function is an alias of another if all references to the alias can be * safely replaced by references to the aliased function. This is true when: * * 1. the body of the alias function consists solely of a call to the aliased * function. * 2. All the arguments to the alias are passed to the aliased function in the * same order as the alias's argument list * 3. the aliased function and the alias have the same arity * 4. the aliased function and the alias have compatible strictness (see comment * for compatibleStrictnessWithAliasedFunction for details of strictness * compatibility). * 5. the aliased function and the alias aren't defined in terms of each other * (ie, there is no cycle between the two) * 6. the aliased function is not a 0-arity foreign function * * Ex, in the following: * * foo x y = bar x y; * * bar a b = baz a b; * * baz i j k = quux i j k; * * quux m n o = quux2 m n o; * * quux2 p q r = baz p q r + baz r q p; * * foo is an alias of bar. * bar is _not_ an alias of baz, because bar and baz have different arities. * baz is _not_ an alias of quux, because quux is defined in terms of quux2, * which is defined in terms of baz. * * @param cl * @return the name of the function the code label is an alias of, may be null. */ private QualifiedName aliasOf(MachineFunction cl) { Expression e = cl.getExpressionForm(); int arity = cl.getArity(); QualifiedName aliasOf = null; if (e != null) { // Check for simplest mapping (e.g. foo = blah;) if (arity == 0 && e.asVar() != null && !e.asVar().getName().equals(cl.getQualifiedName())) { aliasOf = e.asVar().getName(); } else // Check for: foo x y z = blah x y z; if (e.asAppl() != null) { // Break the application chain into an array of nodes. List<Expression> nodes = new ArrayList<Expression>(); Expression node = e; while (node.asAppl() != null) { if (node.asAppl().getE2().asVar() == null) { // The function body contains something other than the arguments and another supercombinator. return null; } nodes.add(0, node.asAppl().getE2()); node = node.asAppl().getE1(); } // Left hand side of chain must be a function. if (node.asVar() == null) { return null; } MachineFunction aliasFunction = getCurrentModule().getFunction(node.asVar().getName()); if (aliasFunction == null) { return null; } // The aliased function must have the same arity and be fully saturated. if (arity != aliasFunction.getArity() || arity != nodes.size()) { return null; } // The arguments to the aliased function must consist soley of the arguments to // this function, in the same order. String argNames[] = cl.getParameterNames(); for (int i = 0; i < argNames.length; ++i) { QualifiedName qn = nodes.get(i).asVar().getName(); if (!qn.getUnqualifiedName().equals(argNames[i]) || !qn.getModuleName().equals(getCurrentModule().getName())) { return null; } } // This function is an alias of another function. aliasOf = node.asVar().getName(); // Some functions, notably built-in functions have and expression form // of foo x y = foo x y; This isn't really an alias. if (aliasOf.equals(cl.getQualifiedName())) { return null; } } } // Don't want to use an alias that is strongly connected to this function. if (aliasOf != null && aliasOf.getModuleName().equals(cl.getQualifiedName().getModuleName()) && cl.isStronglyConnectedTo (aliasOf.getUnqualifiedName())) { return null; } return aliasOf; } /** * Check aliased function for compatability of argument strictness. * * The strictness of an alias is compatible with that of an aliased function * when the same arguments are plinged, up to the last plinged argument of the * alias. In other words, it's okay for an aliased function to have extra * plinged arguments to the right of the last pling on the alias, but otherwise * they must match exactly. Ex: * * alpha x y z = delta x y z; * * beta x !y z = delta x y z; * * gamma !x y z = delta x y z; * * delta x !y !z = ...; * * * epsilon x y !z = zeta x y z; * * zeta !x y !z = ...; * * In the above code, alpha has compatible strictness with delta, because * alpha doesn't have any plinged arguments. * * beta has compatible strictness with delta, because the first and second * arguments have the same plings; the third argument doesn't need to have * the same strictness because the second argument is beta's last plinged * argument. * * gamma and delta do _not_ have compatible strictness, because gamma's * first argument is plinged and delta's is not. * * epsilon and zeta also don't have compatible strictness, because the * first argument's strictness does not match (which is important because * the third argument is epsilon's rightmost strict argument). * * @param mf * @return name of the aliased function. May be null. */ private QualifiedName compatibleStrictnessWithAliasedFunction (MachineFunction mf) { // Now we need to check issues of differences in argument strictness between the two functions. // If the order of argument evaluation is changed by substituting the aliased function then // it can't be considered an alias. QualifiedName aliasOf = mf.getAliasOf(); if (aliasOf == null) { return null; } // Follow any alias chains to the end. MachineFunction aliasFunction = getCurrentModule().getFunction(aliasOf); while (aliasFunction != null && aliasFunction.getAliasOf() != null) { aliasOf = aliasFunction.getAliasOf(); aliasFunction = getCurrentModule().getFunction(aliasOf); } // System.out.println ("second stage alias " + cl.getQualifiedName() + " -> " + aliasOf); if (aliasFunction == null || aliasOf == null) { return null; } else { // Get the strictness of the two functions. boolean thisStrictness[] = mf.getParameterStrictness(); boolean aliasStrictness[] = aliasFunction.getParameterStrictness(); // If the current function is a CAF. if (mf.getArity() == 0) { if (aliasFunction.getArity() == 0) { // Check for a CAF which is defined as a zero arity foreign function. // This is not an alias as a CAF has different behaviour than // a zero arity foreign function. if (aliasFunction.isForeignFunction()) { return null; } // If the aliasFunction is a CAF defined as a literal value // set the current function to be defined as a literal. Expression expressionForm = aliasFunction.getExpressionForm(); if (expressionForm != null && expressionForm.asLiteral() != null) { mf.setExpression(expressionForm); return null; } } } // Count the number of strict arguments for each function. int thisStrictCount = 0; int aliasStrictCount = 0; for (int i = 0; i < thisStrictness.length; ++i) { if (thisStrictness[i]) { thisStrictCount++; } if (aliasStrictness[i]) { aliasStrictCount++; } } if (thisStrictCount == 0) { // This function makes no guarantees of order of argument evaluation // so it doesn't matter what the aliased function does. return aliasOf; } // Right trim non-strict arguments from this function. int lastStrictIndex = thisStrictness.length - 1; for (; lastStrictIndex >= 0; lastStrictIndex--) { if (thisStrictness[lastStrictIndex]) { break; } } // If the sub-arrays match then this is an alias. for (int i = 0; i <= lastStrictIndex; ++i) { if (thisStrictness[i] != aliasStrictness[i]) { return null; } } } return aliasOf; } /** * Wrap the current module: do any cleanup processing on the entire packaged contents. * @param logger * @throws Packager.PackagerException */ public void wrapModule (CompilerMessageLogger logger) throws Packager.PackagerException, UnableToResolveForeignEntityException { doTransformOptimizations (logger); // Since the module is complete we can now generate machine specific code for it. CompilerMessage.Severity resultCode = codeGenerator.generateSCCode(getCurrentModule(), logger); if (resultCode.equals(CompilerMessage.Severity.INFO) || resultCode.equals(CompilerMessage.Severity.INFO)) { // This module has been successfully comiled/packaged. Update the associated timestamp in // the program. This prevents re-compilation if the module hasn't changed. program.putCompilationTimestamp(currentModule.getName(), new Long(System.currentTimeMillis())); } // Inform any status listeners that this module is now loaded. for (final StatusListener statusListener : statusListeners) { statusListener.setModuleStatus(StatusListener.SM_LOADED, currentModule.getName()); } } /** * Initialize packaging of the current adjunct. * @param adjunctModuleName * @throws PackagerException */ public void initAdjunct (ModuleName adjunctModuleName) throws PackagerException { // Switch to the adjunct module. switchModule(adjunctModuleName); // Remove any previous adjunct functions. currentModule.clearAdjunctFunctions(); } /** * Wrap the current adjunct: do any cleanup processing on the entire packaged contents. * @param logger * @throws PackagerException */ public void wrapAdjunct (CompilerMessageLogger logger) throws Packager.PackagerException, UnableToResolveForeignEntityException { doTransformOptimizations (logger); } /** * @return the current level of optimization. Zero means the Global Optimizer is not run. */ public static int getOptimizerLevel(){ return optimizerLevel; } /** * Build a set of the module names in the given expression. * * @param expr The expression to get all the modules in. * @param modules */ private static void modules(Expression expr, Set<ModuleName> modules) { class ModuleCollector extends Visitor { private final Set<ModuleName> modules; ModuleCollector(Set<ModuleName> modules){ this.modules = modules; } @Override void enterVar(Expression.Var var) { QualifiedName name = var.getName(); if (name != null) { modules.add(name.getModuleName()); } } } ModuleCollector collector = new ModuleCollector(modules); expr.walk(collector); } /** * This is thrown when the function being optimized access (directly or indirectly) too many functions. * * @author GMCCLEMENT * */ static class TooManyHelperFunctionsException extends IllegalStateException{ /** * Make a warning go away. */ private static final long serialVersionUID = 3042774494441022983L; } static class HasBadStructureException extends IllegalStateException{ private static final long serialVersionUID = 7405717617059083752L; } /** * This is thrown when the function being optimized is too deep and so would generate code that * could not be compiled by the java compiler. * * @author GMCCLEMENT * */ static class TooDeepException extends IllegalStateException{ private static final long serialVersionUID = 3642636632176914397L; } /** * This is thrown when the function being optimized is too deep and so would generate code that * could not be compiled by the java compiler. * * @author GMCCLEMENT * */ static class TooMuchTimeException extends IllegalStateException{ private static final long serialVersionUID = 9184516831783875647L; } /** * This will only be initialized if the optimizer is enabled. */ private PreludeTypeConstants preludeTypeConstants = null; /** * This function applies to CAL based optimized to the given expression. If * the optimizer is not available then the expression is returned unchanged. * * @param timeStamp * @param cf * @param moreFunctions * @return The optimized version of the expression if the optimizer is on. * @throws Throwable */ private Expression applyCALOptimizer(long timeStamp, CoreFunction cf, List<MachineFunction> moreFunctions) throws Throwable { Expression body = cf.getExpression(); if (OptimizerHelper.hasExcludedTypes(body)){ return body; } final QualifiedName name = cf.getName(); if (optimizer_executor != null) { // Don't optimize the commands from the console. This just makes things look slower // and has no practical benefit since the code is already optimized. if (name.getUnqualifiedName().indexOf("iceruntarget") != -1){ return body; } try { /** * Type information for the optimizer. */ List<List<Object>> nameToTypeExpr = new LinkedList<List<Object>>(); // Get the functions that are references by the expression being // optimized. These will // be passed to the CAL optimizer for use during optimization. LinkedList<AlgebraicValue> helperFunctions = new LinkedList<AlgebraicValue>(); List<QualifiedName> nonCalFunctions = new LinkedList<QualifiedName>(); OptimizerHelper.getHelperFunctions(program, null, inlinableFunctions, cf, nameToTypeExpr, helperFunctions, nonCalFunctions); // TODO GREGM, Fix this limitation if (helperFunctions.size() > 200){ throw new TooManyHelperFunctionsException(); } // Convert the parameters into a list instead of an array. ArrayList<QualifiedName> formalParametersList = new ArrayList<QualifiedName>(); { String[] formalParametersArray = cf.getFormalParameters(); formalParametersList.ensureCapacity(formalParametersArray.length); for(int i = 0; i < formalParametersArray.length; ++i){ formalParametersList.add(i, QualifiedName.make(name.getModuleName(), formalParametersArray[i])); } } // Convert the parameter strictness into a list instead of an array. ArrayList<Boolean> formalParametersStrictnessList = new ArrayList<Boolean>(); { boolean[] formalParametersStrictnessArray = cf.getParameterStrictness(); formalParametersStrictnessList.ensureCapacity(formalParametersStrictnessArray.length); for(int i = 0; i < formalParametersStrictnessArray.length; ++i){ formalParametersStrictnessList.add(i, Boolean.valueOf(formalParametersStrictnessArray[i])); } } long startTime = System.currentTimeMillis(); List<Object> result = UnsafeCast.unsafeCast(optimizer_executor.exec( optimizer_entryPoint, new Object[] { name, formalParametersList, formalParametersStrictnessList, new Long(startTime), preludeTypeConstants, nameToTypeExpr, helperFunctions, nonCalFunctions, body })); List<AlgebraicValue> newFunctionDefs = UnsafeCast.unsafeCast(result.get(0)); List<Boolean> strictnessList = UnsafeCast.unsafeCast(result.get(1)); // Convert the list of booleans to an array. The optimizer does not know // what the original strictness is so 'or' that into the final result. { boolean strictnessArray[] = new boolean[strictnessList.size()]; boolean extantStrictnessArray[] = cf.getParameterStrictness(); { Iterator<Boolean> iList = strictnessList.iterator(); for(int iArray = 0; iArray < strictnessArray.length; ++iArray){ Boolean bValue = iList.next(); strictnessArray[iArray] = bValue.booleanValue() || extantStrictnessArray[iArray]; } } cf.setParameterStrictness(strictnessArray); } Expression resultExpr = (Expression) result.get(2); // Convert lambda expression into top level functions { for (final AlgebraicValue coreFunction : newFunctionDefs) { QualifiedName newFunctionName = (QualifiedName) coreFunction.getNthArgument(0); // System.out.println(" NewFunctionName: " + newFunctionName); final String[] formalParameters; { List<QualifiedName> calFormalParameters = UnsafeCast.unsafeCast(coreFunction.getNthArgument(1)); formalParameters = new String[calFormalParameters.size()]; Iterator<QualifiedName> iList = calFormalParameters.iterator(); for(int i = 0; i < formalParameters.length; ++i){ QualifiedName qualifiedName = iList.next(); formalParameters[i] = qualifiedName.getUnqualifiedName(); } } Expression newFunctionDef = (Expression) coreFunction.getNthArgument(2); // can be overridden in the if below Expression newFunctionBody = newFunctionDef; // Initialize the return and arg types TypeExpr[] argTypes = {}; TypeExpr resultType; { List<TypeExpr> calTypesList = UnsafeCast.unsafeCast(coreFunction.getNthArgument(3)); argTypes = new TypeExpr[calTypesList.size() - 1]; Iterator<TypeExpr> icalTypesList = calTypesList.iterator(); for (int iargTypes = 0; iargTypes < argTypes.length; ++iargTypes) { argTypes[iargTypes] = icalTypesList.next(); } resultType = icalTypesList.next(); } // Initialize strictness array final boolean[] argStrictness; { List<Boolean> calStrictness = UnsafeCast.unsafeCast(coreFunction.getNthArgument(4)); argStrictness = new boolean[calStrictness.size()]; Iterator<Boolean> iNextStrictness = calStrictness.iterator(); for(int i = 0; i < argStrictness.length; ++i){ argStrictness[i] = iNextStrictness.next().booleanValue(); } } CoreFunction newCF = CoreFunction.makeCALCoreFunction(newFunctionName, formalParameters, argStrictness, argTypes, resultType, timeStamp); newCF.setExpression(newFunctionBody); MachineFunction mf = getMachineFunction(newCF); moreFunctions.add(mf); // in case functions in the same module use the definition. inlinableFunctions.put(newCF.getName(), newCF); } } return resultExpr; } catch (CALExecutorException executorException) { // TODO this is for nested cases that need to be lifted. // System.out.println(executorException); if (executorException.getCause() instanceof OptimizerHelper.UnsupportedExpressionTypeException){ throw executorException.getCause(); } if (executorException.getCause().toString().indexOf("Has bad structure") != -1){ throw new HasBadStructureException(); } if (executorException.getCause().toString().indexOf("Too deep") != -1){ throw new TooDeepException(); } if (executorException.getCause().toString().indexOf("Too much time") != -1){ throw new TooMuchTimeException(); } throw new IllegalStateException(); } } else { return body; } } private void doTransformOptimizations(CompilerMessageLogger logger) throws Packager.PackagerException, UnableToResolveForeignEntityException { // First build up a list of functions that haven't already been optimized. List<MachineFunction> functionsList = new ArrayList<MachineFunction>(); Module m = getCurrentModule(); for (final MachineFunction mf : m.getFunctions()) { if (!mf.isOptimized()) { functionsList.add (mf); } } // Run the CAL optimizer on the functions before other optimization are // performed. if (optimizer_executor != null) { Iterator<MachineFunction> functions = functionsList.iterator(); List<MachineFunction> moreFunctions = new LinkedList<MachineFunction>(); // List of modules used by the module. Inlining expression can cause name from // non-imported module to be used. The type info has to be updated with these new // modules. Set<ModuleName> moreModules = new HashSet<ModuleName>(); int skipped_otherCounter = 0; int skipped_tooManyHelperFunctionsCounter = 0; int skipped_unsupportedExpressionType = 0; int skipped_hasBadStructure = 0; int skipped_tooDeep = 0; int skipped_tooMuchTime = 0; int changedCounter = 0; while (functions.hasNext()) { MachineFunctionImpl cl = (MachineFunctionImpl) functions.next(); CoreFunction coreFunction = cl.getCoreFunction(); if (cl.getExpressionForm() instanceof Expression.PackCons) { continue; // can't be optimized so skip these } if (cl.getName().startsWith("$")) { continue; // skip compiler generated helper functions } try { Expression newBody = applyCALOptimizer(cl.getTimeStamp(), coreFunction, moreFunctions); if (!newBody.toString().equals(cl.getExpressionForm().toString())) { ++changedCounter; } modules(newBody, moreModules); cl.setExpression(newBody); } catch(TooManyHelperFunctionsException e){ skipped_tooManyHelperFunctionsCounter++; } catch (OptimizerHelper.UnsupportedExpressionTypeException e) { skipped_unsupportedExpressionType++; } catch (HasBadStructureException e){ skipped_hasBadStructure++; } catch (TooDeepException e){ skipped_tooDeep++; } catch (TooMuchTimeException e){ skipped_tooMuchTime++; } catch (IllegalStateException e) { // TODO fix this so all the types of expressions are // handled. skipped_otherCounter++; } catch (UnsupportedOperationException e){ skipped_unsupportedExpressionType++; } catch (IllegalArgumentException e) { // TODO figure out why this is happening and fix it. skipped_otherCounter++; } catch (Throwable e) { // TODO figure out why this is happening and fix it. skipped_otherCounter++; } } if (moreFunctions.size() > 0 || skipped_otherCounter > 0 || skipped_hasBadStructure > 0 || skipped_tooDeep > 0 || skipped_tooMuchTime > 0 || skipped_tooManyHelperFunctionsCounter > 0 || skipped_unsupportedExpressionType > 0 || changedCounter > 0) { System.out.print(m.getName()); System.out.println(": "); if (moreFunctions.size() > 0) { System.out.println(" Added " + moreFunctions.size() + " more functions."); } if (changedCounter > 0) { System.out.println(" Changed " + changedCounter + " of " + functionsList.size() + " expressions."); } if ( skipped_unsupportedExpressionType > 0 || skipped_tooManyHelperFunctionsCounter > 0 || skipped_hasBadStructure > 0 || skipped_tooDeep > 0 || skipped_tooMuchTime > 0 || skipped_otherCounter > 0) { System.out.println(" Skipped " + (skipped_hasBadStructure + skipped_otherCounter + skipped_tooManyHelperFunctionsCounter + skipped_unsupportedExpressionType) + " of " + functionsList.size() + " expressions."); if (skipped_tooManyHelperFunctionsCounter > 0) { System.out.println(" Too many helpers " + skipped_tooManyHelperFunctionsCounter + " of " + functionsList.size() + " expressions."); } if (skipped_unsupportedExpressionType > 0) { System.out.println(" Unsupported expression types " + skipped_unsupportedExpressionType + " of " + functionsList.size() + " expressions."); } if (skipped_hasBadStructure > 0) { System.out.println(" Incomplete optimization " + skipped_hasBadStructure + " of " + functionsList.size() + " expressions."); } if (skipped_tooDeep > 0) { System.out.println(" Optimization too deep " + skipped_tooDeep + " of " + functionsList.size() + " expressions."); } if (skipped_tooMuchTime > 0) { System.out.println(" Too much time " + skipped_tooMuchTime + " of " + functionsList.size() + " expressions."); } if (skipped_otherCounter > 0) { System.out.println(" Other " + skipped_otherCounter + " of " + functionsList.size() + " expressions."); } } } // add the helper functions to the list of functions for the module. { for (final MachineFunction mf : moreFunctions) { m.addFunction(mf); functionsList.add(mf); } } } Iterator<MachineFunction> functions = functionsList.iterator(); Map<String, Expression> nameToExpressionMap = new HashMap<String, Expression>(); while (functions.hasNext()) { MachineFunction cl = functions.next (); nameToExpressionMap.put (cl.getName(), cl.getExpressionForm()); } // Determine the strongly connected components and put the information into the code labels. List<Set<String>> sets = ExpressionAnalyzer.determineStronglyConnectedComponents(m.getName(), nameToExpressionMap); for (int i = 0; i < sets.size(); ++i) { Set<String> set = sets.get(i); Iterator<String> items = set.iterator(); while (items.hasNext()) { String name = items.next(); MachineFunctionImpl mf = (MachineFunctionImpl)m.getFunction(name); if (mf != null) { mf.setStronglyConnectedComponents(set); } } } // Now that we've determined the closely connected components we can use // an ExpressionAnalyzer to do optimizing transformations to the expression. functions = functionsList.iterator(); while (functions.hasNext()) { MachineFunction cl = functions.next (); ExpressionAnalyzer ea = new ExpressionAnalyzer (m.getModuleTypeInfo(), cl); cl.setExpression(ea.transformExpression(cl.getExpressionForm())); if (ea.getHadUnsafeCoerce()){ cl.setHadUnsafeCoerce(); } cl.setIsTailRecursive(ExpressionAnalyzer.isTailRecursive(cl.getExpressionForm())); } // At this point we want to figure out whether any functions are // simply an alias for another function and put the name of the aliased function //in the MachineFunction. determineFunctionAliases(functionsList); functions = functionsList.iterator(); while (functions.hasNext ()) { MachineFunction mf = functions.next (); ExpressionAnalyzer.antiAlias (mf, m, logger); mf.setOptimized(); } } }