Package org.openquark.cal.compiler

Source Code of org.openquark.cal.compiler.ModuleCollector

/*
* 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();
        }
       
    }
   
}
TOP

Related Classes of org.openquark.cal.compiler.ModuleCollector

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.