--- /dev/null
+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