--- /dev/null
+package org.simantics.scl.compiler.internal.codegen.ssa.statements;\r
+\r
+import gnu.trove.map.hash.THashMap;\r
+import gnu.trove.set.hash.THashSet;\r
+\r
+import java.util.ArrayList;\r
+\r
+import org.simantics.scl.compiler.common.exceptions.InternalCompilerError;\r
+import org.simantics.scl.compiler.constants.SCLConstant;\r
+import org.simantics.scl.compiler.internal.codegen.references.BoundVar;\r
+import org.simantics.scl.compiler.internal.codegen.references.ValRef;\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.FunctionBinder;\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.SSALambdaLiftingContext;\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.types.TVar;\r
+import org.simantics.scl.compiler.types.Type;\r
+\r
+public class LetFunctions extends SSAStatement implements FunctionBinder {\r
+ long recursiveGroupLocation;\r
+ SSAFunction firstFunction;\r
+\r
+ public LetFunctions() {\r
+ }\r
+ \r
+ public LetFunctions(SSAFunction function) {\r
+ firstFunction = function;\r
+ function.setParent(this);\r
+ }\r
+\r
+ @Override\r
+ public void toString(PrintingContext context) {\r
+ for(SSAFunction function = firstFunction; function != null; function = function.getNext()) {\r
+ context.indentation();\r
+ context.append(function.getTarget());\r
+ context.append("(" + function.getTarget().occurrenceCount() + ")");\r
+ context.append(" :: ");\r
+ context.append(function.getTarget().getType());\r
+ context.append(" = \n");\r
+ context.indent();\r
+ function.toString(context);\r
+ context.dedent();\r
+ }\r
+ }\r
+ \r
+ public void addFunction(SSAFunction function) {\r
+ function.setParent(this); \r
+ function.setNext(firstFunction);\r
+ if(firstFunction != null)\r
+ firstFunction.setPrev(function);\r
+ firstFunction = function;\r
+ }\r
+\r
+ @Override\r
+ public void generateCode(MethodBuilder mb) {\r
+ throw new InternalCompilerError("Functions should be lambda lifted before code generation"); \r
+ }\r
+\r
+ @Override\r
+ public void validate(SSAValidationContext context) {\r
+ for(SSAFunction function = firstFunction; function != null; function = function.getNext()) {\r
+ if(!(function.getTarget() instanceof BoundVar))\r
+ throw new InternalCompilerError();\r
+ function.validate(context);\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public void destroy() {\r
+ for(SSAFunction function = firstFunction; function != null; function = function.getNext())\r
+ function.destroy();\r
+ }\r
+\r
+ @Override\r
+ public SSAStatement copy(CopyContext context) {\r
+ LetFunctions result = new LetFunctions();\r
+ for(SSAFunction function = firstFunction; function != null; function = function.getNext()) {\r
+ SSAFunction newFunction = function.copy(context);\r
+ newFunction.setTarget(context.copy(function.getTarget()));\r
+ result.addFunction(newFunction);\r
+ }\r
+ return result; \r
+ }\r
+\r
+ @Override\r
+ public void replace(TVar[] vars, Type[] replacements) {\r
+ for(SSAFunction function = firstFunction; function != null; function = function.getNext()) {\r
+ ((BoundVar)function.getTarget()).replace(vars, replacements);\r
+ function.replace(vars, replacements);\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public void addBoundVariablesTo(SSAValidationContext context) {\r
+ for(SSAFunction function = firstFunction; function != null; function = function.getNext())\r
+ context.validBoundVariables.add((BoundVar)function.getTarget()); \r
+ }\r
+\r
+ @Override\r
+ public SSAFunction getFirstFunction() {\r
+ return firstFunction;\r
+ }\r
+\r
+ @Override\r
+ public void setFirstFunction(SSAFunction function) {\r
+ this.firstFunction = function; \r
+ if(function == null)\r
+ detach();\r
+ }\r
+\r
+ @Override\r
+ public void collectFreeVariables(SSAFunction parentFunction,\r
+ ArrayList<ValRef> vars) {\r
+ throw new InternalCompilerError("Should not be called for non-lambda-lifted functions.");\r
+ // FIXME inefficient, some kind of caching needed here\r
+ /*THashSet<BoundVar> tempVars = new THashSet<BoundVar>();\r
+ for(SSAFunction function = firstFunction; function != null; function = function.getNext())\r
+ function.collectFreeVariables(tempVars);\r
+ \r
+ for(BoundVar var : tempVars)\r
+ if(var.getFunctionParent() != parentFunction)\r
+ vars.add(var);*/\r
+ }\r
+\r
+ @Override\r
+ public void lambdaLift(SSALambdaLiftingContext context) {\r
+ boolean hasValues = false;\r
+ boolean isRecursive = false;\r
+ \r
+ // Lambda lift functions and collect free variables\r
+ THashSet<BoundVar> targets = new THashSet<BoundVar>();\r
+ ArrayList<ValRef> freeVars = new ArrayList<ValRef>(); \r
+ for(SSAFunction function = firstFunction; \r
+ function != null; \r
+ function = function.getNext()) {\r
+ hasValues |= function.getArity() == 0;\r
+ function.lambdaLift(context);\r
+ targets.add((BoundVar)function.getTarget());\r
+ function.collectFreeVariables(freeVars);\r
+ }\r
+ \r
+ // Classify by BoundVars\r
+ THashSet<BoundVar> boundVars = new THashSet<BoundVar>(); \r
+ ArrayList<BoundVar> boundVarsList = new ArrayList<BoundVar>(4);\r
+ ArrayList<ValRef> newFreeVars = new ArrayList<ValRef>(freeVars.size()); \r
+ for(ValRef ref : freeVars) {\r
+ BoundVar var = (BoundVar)ref.getBinding();\r
+ if(targets.contains(var)) {\r
+ isRecursive = true;\r
+ continue;\r
+ }\r
+ if(boundVars.add(var))\r
+ boundVarsList.add(var);\r
+ newFreeVars.add(ref);\r
+ /*BoundVar inVar = map.get(outVar);\r
+ if(inVar == null) {\r
+ inVar = new BoundVar(outVar.getType());\r
+ map.put(outVar, inVar);\r
+ outParameters.add(outVar);\r
+ inParameters.add(inVar);\r
+ }\r
+ ref.replaceBy(inVar);*/\r
+ }\r
+ BoundVar[] outVars = boundVarsList.toArray(new BoundVar[boundVarsList.size()]);\r
+ freeVars = newFreeVars;\r
+ \r
+ // Modify functions\r
+ THashMap<SSAFunction, THashMap<BoundVar, BoundVar>> varMap = new THashMap<SSAFunction, THashMap<BoundVar, BoundVar>>();\r
+ THashMap<SSAFunction, BoundVar[]> inVarsMap = new THashMap<SSAFunction, BoundVar[]>();\r
+ THashMap<SSAFunction, BoundVar> oldTargets = new THashMap<SSAFunction, BoundVar>();\r
+ for(SSAFunction function = firstFunction; \r
+ function != null; \r
+ function = function.getNext()) {\r
+ THashMap<BoundVar, BoundVar> map = new THashMap<BoundVar, BoundVar>(2*outVars.length);\r
+ BoundVar[] inVars = new BoundVar[outVars.length]; \r
+ for(int i=0;i<inVars.length;++i) {\r
+ inVars[i] = new BoundVar(outVars[i].getType());\r
+ map.put(outVars[i], inVars[i]);\r
+ }\r
+ inVarsMap.put(function, inVars);\r
+ varMap.put(function, map);\r
+ \r
+ function.addParametersInFront(inVars); \r
+ SCLConstant functionConstant = new SCLConstant(context.createName(), function.getType());\r
+ context.addConstant(functionConstant); \r
+ oldTargets.put(function, (BoundVar)function.getTarget());\r
+ function.setTarget(functionConstant);\r
+ functionConstant.setDefinition(function); \r
+ functionConstant.setPrivate(true);\r
+ // TODO handle type parameters\r
+ \r
+ // Define target by an application\r
+ /*new LetApply(oldTarget, functionConstant.createOccurrence(), \r
+ ValRef.createOccurrences(outVars)).insertBefore(this);*/\r
+ }\r
+ \r
+ for(SSAFunction function = firstFunction; \r
+ function != null; \r
+ function = function.getNext()) {\r
+ BoundVar oldTarget = oldTargets.get(function);\r
+ for(ValRef ref : oldTarget.getOccurences()) {\r
+ SSAFunction parent = ref.getParentFunction();\r
+ BoundVar[] vars = inVarsMap.get(parent);\r
+ if(vars == null)\r
+ vars = outVars;\r
+ if(vars.length > 0)\r
+ ref.replaceByApply(function.getTarget(), vars);\r
+ else\r
+ ref.replaceBy(function.getTarget());\r
+ }\r
+ }\r
+ \r
+ // Fix references\r
+ for(ValRef ref : freeVars) {\r
+ BoundVar inVar = (BoundVar)ref.getBinding();\r
+ if(targets.contains(inVar))\r
+ continue;\r
+ BoundVar outVar = varMap.get(ref.getParentFunction()).get(inVar);\r
+ ref.replaceBy(outVar);\r
+ }\r
+ \r
+ detach();\r
+ //context.validate();\r
+ \r
+ if(hasValues && isRecursive)\r
+ context.getErrorLog().log(recursiveGroupLocation, "Variables defined recursively must all be functions.");\r
+ }\r
+ \r
+ @Override\r
+ public void simplify(SSASimplificationContext context) {\r
+ for(SSAFunction function = firstFunction; \r
+ function != null; \r
+ function = function.getNext())\r
+ function.simplify(context);\r
+ }\r
+ \r
+ public void setRecursiveGroupLocation(long recursiveGroupLocation) {\r
+ this.recursiveGroupLocation = recursiveGroupLocation;\r
+ }\r
+}\r