package org.simantics.scl.compiler.internal.codegen.ssa.statements; 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; import gnu.trove.map.hash.THashMap; import gnu.trove.set.hash.THashSet; 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; } }