X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=bundles%2Forg.simantics.scl.compiler%2Fsrc%2Forg%2Fsimantics%2Fscl%2Fcompiler%2Finternal%2Fcodegen%2Fssa%2Fstatements%2FLetFunctions.java;fp=bundles%2Forg.simantics.scl.compiler%2Fsrc%2Forg%2Fsimantics%2Fscl%2Fcompiler%2Finternal%2Fcodegen%2Fssa%2Fstatements%2FLetFunctions.java;h=7b1db339faed0fe8b39d43726130c0c2dc31d298;hb=969bd23cab98a79ca9101af33334000879fb60c5;hp=0000000000000000000000000000000000000000;hpb=866dba5cd5a3929bbeae85991796acb212338a08;p=simantics%2Fplatform.git diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/statements/LetFunctions.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/statements/LetFunctions.java new file mode 100644 index 000000000..7b1db339f --- /dev/null +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/statements/LetFunctions.java @@ -0,0 +1,245 @@ +package org.simantics.scl.compiler.internal.codegen.ssa.statements; + +import gnu.trove.map.hash.THashMap; +import gnu.trove.set.hash.THashSet; + +import java.util.ArrayList; + +import org.simantics.scl.compiler.common.exceptions.InternalCompilerError; +import org.simantics.scl.compiler.constants.SCLConstant; +import org.simantics.scl.compiler.internal.codegen.references.BoundVar; +import org.simantics.scl.compiler.internal.codegen.references.ValRef; +import org.simantics.scl.compiler.internal.codegen.ssa.SSAFunction; +import org.simantics.scl.compiler.internal.codegen.ssa.SSAStatement; +import org.simantics.scl.compiler.internal.codegen.ssa.binders.FunctionBinder; +import org.simantics.scl.compiler.internal.codegen.utils.CopyContext; +import org.simantics.scl.compiler.internal.codegen.utils.MethodBuilder; +import org.simantics.scl.compiler.internal.codegen.utils.PrintingContext; +import org.simantics.scl.compiler.internal.codegen.utils.SSALambdaLiftingContext; +import org.simantics.scl.compiler.internal.codegen.utils.SSASimplificationContext; +import org.simantics.scl.compiler.internal.codegen.utils.SSAValidationContext; +import org.simantics.scl.compiler.types.TVar; +import org.simantics.scl.compiler.types.Type; + +public class LetFunctions extends SSAStatement implements FunctionBinder { + long recursiveGroupLocation; + SSAFunction firstFunction; + + public LetFunctions() { + } + + public LetFunctions(SSAFunction function) { + firstFunction = function; + function.setParent(this); + } + + @Override + public void toString(PrintingContext context) { + for(SSAFunction function = firstFunction; function != null; function = function.getNext()) { + context.indentation(); + context.append(function.getTarget()); + context.append("(" + function.getTarget().occurrenceCount() + ")"); + context.append(" :: "); + context.append(function.getTarget().getType()); + context.append(" = \n"); + context.indent(); + function.toString(context); + context.dedent(); + } + } + + public void addFunction(SSAFunction function) { + function.setParent(this); + function.setNext(firstFunction); + if(firstFunction != null) + firstFunction.setPrev(function); + firstFunction = function; + } + + @Override + public void generateCode(MethodBuilder mb) { + throw new InternalCompilerError("Functions should be lambda lifted before code generation"); + } + + @Override + public void validate(SSAValidationContext context) { + for(SSAFunction function = firstFunction; function != null; function = function.getNext()) { + if(!(function.getTarget() instanceof BoundVar)) + throw new InternalCompilerError(); + function.validate(context); + } + } + + @Override + public void destroy() { + for(SSAFunction function = firstFunction; function != null; function = function.getNext()) + function.destroy(); + } + + @Override + public SSAStatement copy(CopyContext context) { + LetFunctions result = new LetFunctions(); + for(SSAFunction function = firstFunction; function != null; function = function.getNext()) { + SSAFunction newFunction = function.copy(context); + newFunction.setTarget(context.copy(function.getTarget())); + result.addFunction(newFunction); + } + return result; + } + + @Override + public void replace(TVar[] vars, Type[] replacements) { + for(SSAFunction function = firstFunction; function != null; function = function.getNext()) { + ((BoundVar)function.getTarget()).replace(vars, replacements); + function.replace(vars, replacements); + } + } + + @Override + public void addBoundVariablesTo(SSAValidationContext context) { + for(SSAFunction function = firstFunction; function != null; function = function.getNext()) + context.validBoundVariables.add((BoundVar)function.getTarget()); + } + + @Override + public SSAFunction getFirstFunction() { + return firstFunction; + } + + @Override + public void setFirstFunction(SSAFunction function) { + this.firstFunction = function; + if(function == null) + detach(); + } + + @Override + public void collectFreeVariables(SSAFunction parentFunction, + ArrayList vars) { + throw new InternalCompilerError("Should not be called for non-lambda-lifted functions."); + // FIXME inefficient, some kind of caching needed here + /*THashSet tempVars = new THashSet(); + for(SSAFunction function = firstFunction; function != null; function = function.getNext()) + function.collectFreeVariables(tempVars); + + for(BoundVar var : tempVars) + if(var.getFunctionParent() != parentFunction) + vars.add(var);*/ + } + + @Override + public void lambdaLift(SSALambdaLiftingContext context) { + boolean hasValues = false; + boolean isRecursive = false; + + // Lambda lift functions and collect free variables + THashSet targets = new THashSet(); + ArrayList freeVars = new ArrayList(); + for(SSAFunction function = firstFunction; + function != null; + function = function.getNext()) { + hasValues |= function.getArity() == 0; + function.lambdaLift(context); + targets.add((BoundVar)function.getTarget()); + function.collectFreeVariables(freeVars); + } + + // Classify by BoundVars + THashSet boundVars = new THashSet(); + ArrayList boundVarsList = new ArrayList(4); + ArrayList newFreeVars = new ArrayList(freeVars.size()); + for(ValRef ref : freeVars) { + BoundVar var = (BoundVar)ref.getBinding(); + if(targets.contains(var)) { + isRecursive = true; + continue; + } + if(boundVars.add(var)) + boundVarsList.add(var); + newFreeVars.add(ref); + /*BoundVar inVar = map.get(outVar); + if(inVar == null) { + inVar = new BoundVar(outVar.getType()); + map.put(outVar, inVar); + outParameters.add(outVar); + inParameters.add(inVar); + } + ref.replaceBy(inVar);*/ + } + BoundVar[] outVars = boundVarsList.toArray(new BoundVar[boundVarsList.size()]); + freeVars = newFreeVars; + + // Modify functions + THashMap> varMap = new THashMap>(); + THashMap inVarsMap = new THashMap(); + THashMap oldTargets = new THashMap(); + for(SSAFunction function = firstFunction; + function != null; + function = function.getNext()) { + THashMap map = new THashMap(2*outVars.length); + BoundVar[] inVars = new BoundVar[outVars.length]; + for(int i=0;i 0) + ref.replaceByApply(function.getTarget(), vars); + else + ref.replaceBy(function.getTarget()); + } + } + + // Fix references + for(ValRef ref : freeVars) { + BoundVar inVar = (BoundVar)ref.getBinding(); + if(targets.contains(inVar)) + continue; + BoundVar outVar = varMap.get(ref.getParentFunction()).get(inVar); + ref.replaceBy(outVar); + } + + detach(); + //context.validate(); + + if(hasValues && isRecursive) + context.getErrorLog().log(recursiveGroupLocation, "Variables defined recursively must all be functions."); + } + + @Override + public void simplify(SSASimplificationContext context) { + for(SSAFunction function = firstFunction; + function != null; + function = function.getNext()) + function.simplify(context); + } + + public void setRecursiveGroupLocation(long recursiveGroupLocation) { + this.recursiveGroupLocation = recursiveGroupLocation; + } +}