]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/statements/LetApply.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.scl.compiler / src / org / simantics / scl / compiler / internal / codegen / ssa / statements / LetApply.java
diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/statements/LetApply.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/statements/LetApply.java
new file mode 100644 (file)
index 0000000..8d5d24e
--- /dev/null
@@ -0,0 +1,463 @@
+package org.simantics.scl.compiler.internal.codegen.ssa.statements;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Arrays;\r
+\r
+import org.simantics.scl.compiler.common.exceptions.InternalCompilerError;\r
+import org.simantics.scl.compiler.constants.Constant;\r
+import org.simantics.scl.compiler.internal.codegen.continuations.ContRef;\r
+import org.simantics.scl.compiler.internal.codegen.references.BoundVar;\r
+import org.simantics.scl.compiler.internal.codegen.references.Val;\r
+import org.simantics.scl.compiler.internal.codegen.references.ValRef;\r
+import org.simantics.scl.compiler.internal.codegen.ssa.SSABlock;\r
+import org.simantics.scl.compiler.internal.codegen.ssa.SSAExit;\r
+import org.simantics.scl.compiler.internal.codegen.ssa.SSAFunction;\r
+import org.simantics.scl.compiler.internal.codegen.ssa.SSAStatement;\r
+import org.simantics.scl.compiler.internal.codegen.ssa.binders.BoundVarBinder;\r
+import org.simantics.scl.compiler.internal.codegen.ssa.binders.ValRefBinder;\r
+import org.simantics.scl.compiler.internal.codegen.ssa.exits.Jump;\r
+import org.simantics.scl.compiler.internal.codegen.ssa.exits.Switch;\r
+import org.simantics.scl.compiler.internal.codegen.utils.CopyContext;\r
+import org.simantics.scl.compiler.internal.codegen.utils.MethodBuilder;\r
+import org.simantics.scl.compiler.internal.codegen.utils.PrintingContext;\r
+import org.simantics.scl.compiler.internal.codegen.utils.SSASimplificationContext;\r
+import org.simantics.scl.compiler.internal.codegen.utils.SSAValidationContext;\r
+import org.simantics.scl.compiler.top.SCLCompilerConfiguration;\r
+import org.simantics.scl.compiler.types.TVar;\r
+import org.simantics.scl.compiler.types.Type;\r
+import org.simantics.scl.compiler.types.Types;\r
+import org.simantics.scl.compiler.types.exceptions.MatchException;\r
+import org.simantics.scl.compiler.types.util.MultiFunction;\r
+import org.simantics.scl.compiler.types.util.TypeUnparsingContext;\r
+\r
+public class LetApply extends LetStatement implements ValRefBinder {\r
+    private ValRef function;\r
+    private ValRef[] parameters;\r
+    Type effect;\r
+    \r
+    public LetApply(BoundVar target, Type effect, ValRef function, ValRef ... parameters) {\r
+        super(target);\r
+        if(SCLCompilerConfiguration.DEBUG) {\r
+            if(effect == null)\r
+                throw new InternalCompilerError();\r
+            if(function.getBinding() == null)\r
+                throw new InternalCompilerError();\r
+        }\r
+        this.setFunction(function);\r
+        this.setParameters(parameters);\r
+        this.effect = Types.canonical(effect);\r
+    }\r
+\r
+    public void push(MethodBuilder mb) {\r
+        //mb.getCodeBuilder().mapLineNumber(lineNumber);\r
+        Val f = getFunction().getBinding();\r
+        Val[] ps = ValRef.getBindings(getParameters());\r
+        if(f instanceof Constant) {\r
+            Constant cf = (Constant)f;\r
+            Type returnType = cf.apply(mb, getFunction().getTypeParameters(), ps);\r
+            if(Types.isBoxed(returnType))\r
+                mb.unbox(target.getType());\r
+        }\r
+        else {            \r
+            mb.push(f, f.getType());\r
+            mb.pushBoxed(ps);\r
+            mb.genericApply(ps.length);\r
+            mb.unbox(target.getType());\r
+        }\r
+    }\r
+    \r
+    @Override\r
+    public void generateCode(MethodBuilder mb) {\r
+        if(!target.generateOnFly) {\r
+            push(mb);\r
+            mb.store(target);\r
+        }\r
+    }\r
+\r
+    @Override\r
+    public void toString(PrintingContext context) {\r
+        if(determineGenerateOnFly())\r
+            context.addInlineExpression(target, this);\r
+        else\r
+            toStringAux(context);\r
+    }\r
+    \r
+    private void toStringAux(PrintingContext context) {\r
+        context.indentation();\r
+        context.append(target);\r
+        context.append("(" + target.occurrenceCount() + ")");\r
+        context.append(" = ");\r
+        bodyToString(context);\r
+        context.append('\n');\r
+        \r
+    }\r
+    \r
+    public void bodyToString(PrintingContext context) {\r
+        if(context.getErrorMarker() == this)\r
+            context.append("!> ");\r
+        if(hasEffect()) {\r
+            context.append("<");\r
+            context.append(effect);\r
+            context.append("> ");\r
+        }\r
+        context.append(getFunction());\r
+        for(ValRef parameter : getParameters()) {\r
+            context.append(' ');\r
+            context.append(parameter);\r
+        }\r
+    }\r
+    \r
+    @Override\r
+    public String toString() {\r
+        PrintingContext context = new PrintingContext();\r
+        toStringAux(context);\r
+        return context.toString();\r
+    }\r
+\r
+    @Override\r
+    public void validate(SSAValidationContext context) {\r
+        context.validate(target);\r
+        if(target.getParent() != this)\r
+            throw new InternalCompilerError();\r
+        context.validate(function);\r
+        if(function.getParent() != this)\r
+            throw new InternalCompilerError();\r
+        for(ValRef parameter : parameters) {\r
+            context.validate(parameter);\r
+            if(parameter.getParent() != this)\r
+                throw new InternalCompilerError();\r
+        }\r
+        //if(parameters.length == 0)\r
+        //    throw new InternalCompilerError();\r
+        \r
+        \r
+        MultiFunction mFun;\r
+        try {\r
+            mFun = Types.matchFunction(getFunction().getType(), getParameters().length);\r
+        } catch (MatchException e) {\r
+            context.setErrorMarker(this);\r
+            throw new InternalCompilerError();\r
+        }\r
+        for(int i=0;i<getParameters().length;++i)\r
+            context.assertSubsumes(this, getParameters()[i].getType(), mFun.parameterTypes[i]);\r
+        context.assertSubsumes(this, target.getType(), mFun.returnType);\r
+        context.assertEqualsEffect(this, effect, mFun.effect);\r
+    }\r
+\r
+    @Override\r
+    public void destroy() {\r
+        getFunction().remove();\r
+        for(ValRef parameter : getParameters())\r
+            parameter.remove();\r
+    }\r
+    \r
+    @Override\r
+    public void simplify(SSASimplificationContext context) {\r
+        if(target.hasNoOccurences() && !hasEffect()) {\r
+            remove();\r
+            context.markModified("LetApply.dead-let-statement");\r
+            return;\r
+        }\r
+        Val functionVal = getFunction().getBinding();\r
+        if(functionVal instanceof BoundVar) {\r
+            BoundVarBinder parent_ = ((BoundVar)functionVal).parent;\r
+            if(parent_ instanceof SSAFunction) {\r
+                SSAFunction function = (SSAFunction)parent_;\r
+                if(functionVal.hasMoreThanOneOccurences())\r
+                    return;\r
+                if(getParameters().length < function.getArity())\r
+                    return;\r
+                if(getParameters().length > function.getArity())\r
+                    split(function.getArity());\r
+                inline(function);\r
+                function.detach();\r
+                context.markModified("LetApply.beta-lambda");\r
+            }\r
+            else if(parent_ instanceof LetApply) {\r
+                LetApply apply = (LetApply)parent_;\r
+                if(apply.hasEffect())\r
+                    return;\r
+                boolean hasJustOneOccurence = !functionVal.hasMoreThanOneOccurences();\r
+                if((hasJustOneOccurence && apply.getParent() == getParent()) ||\r
+                        apply.isPartial()) {\r
+                    if(hasJustOneOccurence) {\r
+                        apply.detach();\r
+                        setFunction(apply.getFunction());\r
+                        setParameters(ValRef.concat(apply.getParameters(), getParameters()));\r
+                    }\r
+                    else {\r
+                        setFunction(apply.getFunction().copy());\r
+                        setParameters(ValRef.concat(ValRef.copy(apply.getParameters()), getParameters()));\r
+                    }\r
+                    context.markModified("LetApply.merge-applications");\r
+                }\r
+            }\r
+            else if(parent_ instanceof SSABlock) {\r
+                SSABlock parent = getParent();\r
+                if(parent_ != parent)\r
+                    return;\r
+                if(parent.getFirstStatement() != this)\r
+                    return;\r
+                if(!parent.hasMoreThanOneOccurences())\r
+                    return; // We stop here, because situation can be handled by better transformations\r
+                if(functionVal.hasMoreThanOneOccurences())\r
+                    return;\r
+                // We have now the following situation:\r
+                //    [c] ... f ... =\r
+                //        x = f ... \r
+                // * this application is the only reference to f\r
+                // * there are multiple references to [c]\r
+                for(ContRef ref = parent.getOccurrence();ref != null; ref = ref.getNext())\r
+                    if(!(ref.getParent() instanceof Jump))\r
+                        return;\r
+                \r
+                // Finds the position of the functionVal in the parameter list of \r
+                // the parent block.\r
+                int position;\r
+                for(position=0;position<parent.getParameters().length;++position)\r
+                    if(parent.getParameters()[position] == functionVal)\r
+                        break;\r
+                if(position == parent.getParameters().length)\r
+                    throw new InternalCompilerError();\r
+                \r
+                // Do tranformation\r
+                for(ContRef ref = parent.getOccurrence();ref != null; ref = ref.getNext()) {\r
+                    Jump jump = (Jump)ref.getParent();\r
+                    SSABlock block = jump.getParent();\r
+                    \r
+                    BoundVar newTarget = new BoundVar(target.getType());\r
+                    block.addStatement(new LetApply(newTarget, effect, jump.getParameter(position), ValRef.copy(parameters)));\r
+                    jump.setParameter(position, newTarget.createOccurrence());\r
+                }\r
+                \r
+                parent.setParameter(position, target);\r
+                remove();\r
+                context.markModified("LetApply.hoist-apply");\r
+            }\r
+        }\r
+        else if(functionVal instanceof Constant) {\r
+            ((Constant)functionVal).inline(context, this);\r
+        }\r
+    }\r
+    \r
+    public boolean isPartial() {\r
+        return parameters.length < function.getBinding().getEffectiveArity();\r
+    }\r
+\r
+    /**\r
+     * Removes apply if it does not have parameters.\r
+     */\r
+    public void removeDegenerated() {\r
+        if(getParameters().length == 0) {\r
+            target.replaceBy(getFunction());\r
+            getFunction().remove();\r
+            detach();\r
+        }\r
+    }\r
+\r
+    public boolean determineGenerateOnFly() {\r
+        if(hasEffect())\r
+            return false;\r
+        ValRef ref = target.getOccurrence();\r
+        if(ref == null || ref.getNext() != null)\r
+            return false;\r
+        Object parent = ref.getParent();\r
+        if(parent instanceof SSAStatement) {\r
+            if(((SSAStatement)parent).getParent() != getParent())\r
+                return false;\r
+        }\r
+        else if(parent instanceof SSAExit) {\r
+            if(((SSAExit)parent).getParent() != getParent())\r
+                return false;\r
+            if(parent instanceof Switch)\r
+                return false;\r
+        }\r
+        else\r
+            return false;\r
+        return true;\r
+    }\r
+    \r
+    @Override\r
+    public void markGenerateOnFly() {        \r
+        target.generateOnFly = determineGenerateOnFly();\r
+    }\r
+\r
+    public ValRef getFunction() {\r
+        return function;\r
+    }\r
+\r
+    public void setFunction(ValRef function) {\r
+        this.function = function;\r
+        function.setParent(this);\r
+    }\r
+\r
+    public ValRef[] getParameters() {\r
+        return parameters;\r
+    }\r
+\r
+    public void setParameters(ValRef[] parameters) {\r
+        /*if(SCLCompilerConfiguration.DEBUG)\r
+            if(parameters.length == 0)\r
+                throw new InternalCompilerError();*/\r
+        this.parameters = parameters;\r
+        for(ValRef parameter : parameters)\r
+            parameter.setParent(this);\r
+    }\r
+    \r
+    @Override\r
+    public SSAStatement copy(CopyContext context) {\r
+        LetApply result = new LetApply(context.copy(target), \r
+                context.copyType(effect),\r
+                context.copy(function), \r
+                context.copy(parameters));\r
+        return result;\r
+    }\r
+    \r
+    @Override\r
+    public void replace(TVar[] vars, Type[] replacements) {\r
+        target.replace(vars, replacements);\r
+        function.replace(vars, replacements);\r
+        effect = effect.replace(vars, replacements);\r
+        for(ValRef parameter : parameters)\r
+            parameter.replace(vars, replacements);\r
+    }\r
+    \r
+    /**\r
+     * Inlines the application by the given function.\r
+     * It is assumed that the function has the same number\r
+     * of parameters as this one and there are no other \r
+     * references to the function (copy function before\r
+     * inlining if necessary).\r
+     */\r
+    public void inline(SSAFunction function) {\r
+        if(function.getArity() != parameters.length)\r
+            throw new InternalCompilerError();        \r
+               \r
+        SSABlock headBlock = getParent();\r
+        SSAFunction thisFunction = headBlock.getParent();\r
+               \r
+        /*System.out.println("--- INLINING -------------------------------");\r
+        System.out.println(thisFunction);\r
+        System.out.println("Function name: " + getFunction().getBinding());\r
+        System.out.println("++++++++++++++++++++++++++++++++++++++++++++");\r
+        System.out.println(function);   \r
+        */\r
+        \r
+        if(this.function.getTypeParameters().length > 0) {\r
+            /*if(function.getParent() != null) {\r
+                PrintingContext pc = new PrintingContext();\r
+                pc.append("----------------------------\n");\r
+                function.getParent().getParentFunction().toString(pc);\r
+                pc.append("\n----\n");\r
+                function.toString(pc);\r
+                pc.append("\n");\r
+                pc.append(function.getTypeParameters());\r
+                pc.append(" -> ");\r
+                pc.append(this.function.getTypeParameters());\r
+                System.out.println(pc.toString());\r
+            }*/\r
+            function.replace(function.getTypeParameters(), this.function.getTypeParameters());\r
+        }\r
+\r
+        if(getPrev() != null)\r
+            getPrev().setAsLastStatement();\r
+        else\r
+            headBlock.removeStatements();\r
+        \r
+        // Create tail block\r
+        SSABlock tailBlock = new SSABlock(new BoundVar[] {target});\r
+        thisFunction.addBlock(tailBlock);\r
+        {\r
+            SSAStatement stat = getNext();\r
+            while(stat != null) {\r
+                SSAStatement temp = stat.getNext();\r
+                tailBlock.addStatement(stat);\r
+                stat = temp;\r
+            }\r
+        }\r
+        tailBlock.setExit(headBlock.getExit());\r
+        \r
+        // Merge blocks        \r
+        thisFunction.mergeBlocks(function);           \r
+        \r
+        headBlock.setExit(new Jump(function.getFirstBlock().createOccurrence(), \r
+                parameters));\r
+        function.getReturnCont().replaceWith(tailBlock);\r
+\r
+        this.function.remove();\r
+        // Note: No need to remove or detach this statement anymore\r
+        \r
+        // TODO remove function\r
+        /*\r
+        System.out.println("============================================");\r
+        System.out.println(thisFunction);\r
+        */\r
+    }\r
+\r
+    @Override\r
+    public void collectFreeVariables(SSAFunction parentFunction,\r
+            ArrayList<ValRef> vars) {\r
+        function.collectFreeVariables(parentFunction, vars);\r
+        for(ValRef parameter : parameters)\r
+            parameter.collectFreeVariables(parentFunction, vars);\r
+    }\r
+    \r
+    @Override\r
+    public void replaceByApply(ValRef valRef, Val newFunction, Type[] typeParameters, Val[] parameters) {\r
+        if(function == valRef) {\r
+            valRef.remove();\r
+            setFunction(newFunction.createOccurrence(typeParameters));\r
+            setParameters(ValRef.concat(ValRef.createOccurrences(parameters), this.parameters));\r
+        }\r
+        else\r
+            super.replaceByApply(valRef, newFunction, typeParameters, parameters);\r
+    }\r
+\r
+    /**\r
+     * Splits this application into two applications where the first has\r
+     * the arity given as a parameter and the new application inserted \r
+     * after this statement has the rest of the parameters.\r
+     */\r
+    public void split(int arity) {\r
+        if(arity == parameters.length)\r
+            return;\r
+        if(arity > parameters.length)\r
+            throw new InternalCompilerError();\r
+        ValRef[] firstHalf = arity == 0 ? ValRef.EMPTY_ARRAY : Arrays.copyOf(parameters, arity);\r
+        ValRef[] secondHalf = arity == parameters.length ? ValRef.EMPTY_ARRAY : Arrays.copyOfRange(parameters, arity, parameters.length);\r
+        BoundVar newVar;\r
+        try {\r
+            MultiFunction mfun = Types.matchFunction(function.getType(), arity);\r
+            newVar = new BoundVar(mfun.returnType);\r
+        } catch (MatchException e) {\r
+            throw new InternalCompilerError();\r
+        }\r
+        LetApply newApply = new LetApply(target, effect, \r
+                newVar.createOccurrence(), secondHalf);\r
+        newApply.insertAfter(this);\r
+        effect = Types.NO_EFFECTS;\r
+        setTarget(newVar);\r
+        setParameters(firstHalf);\r
+    }\r
+    \r
+    /**\r
+     * True, if the application may have side effects.\r
+     */\r
+    public boolean hasEffect() {\r
+        return effect != Types.NO_EFFECTS;\r
+    }\r
+\r
+    public void updateEffect() {\r
+        try {\r
+            MultiFunction mFun = Types.matchFunction(function.getType(), parameters.length);\r
+            this.effect = mFun.effect;\r
+        } catch (MatchException e) {\r
+            throw new InternalCompilerError(e);\r
+        }\r
+    }\r
+    \r
+    @Override\r
+    public void prepare(MethodBuilder mb) {\r
+        function.getBinding().prepare(mb);\r
+    }\r
+}\r