package org.simantics.scl.compiler.internal.codegen.ssa.statements; import java.util.ArrayList; import java.util.Arrays; import org.simantics.scl.compiler.common.exceptions.InternalCompilerError; import org.simantics.scl.compiler.constants.Constant; import org.simantics.scl.compiler.constants.SCLConstant; import org.simantics.scl.compiler.internal.codegen.continuations.ContRef; import org.simantics.scl.compiler.internal.codegen.references.BoundVar; import org.simantics.scl.compiler.internal.codegen.references.Val; import org.simantics.scl.compiler.internal.codegen.references.ValRef; import org.simantics.scl.compiler.internal.codegen.ssa.SSABlock; import org.simantics.scl.compiler.internal.codegen.ssa.SSAExit; 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.BoundVarBinder; import org.simantics.scl.compiler.internal.codegen.ssa.binders.ValRefBinder; import org.simantics.scl.compiler.internal.codegen.ssa.exits.Jump; import org.simantics.scl.compiler.internal.codegen.ssa.exits.Switch; 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.SSASimplificationContext; import org.simantics.scl.compiler.internal.codegen.utils.SSAValidationContext; import org.simantics.scl.compiler.internal.codegen.utils.ValRefVisitor; import org.simantics.scl.compiler.top.SCLCompilerConfiguration; import org.simantics.scl.compiler.types.TVar; import org.simantics.scl.compiler.types.Type; import org.simantics.scl.compiler.types.Types; import org.simantics.scl.compiler.types.exceptions.MatchException; import org.simantics.scl.compiler.types.util.MultiFunction; public class LetApply extends LetStatement implements ValRefBinder { private ValRef function; private ValRef[] parameters; Type effect; public LetApply(BoundVar target, Type effect, ValRef function, ValRef ... parameters) { super(target); if(SCLCompilerConfiguration.DEBUG) { if(effect == null) throw new InternalCompilerError(); if(function.getBinding() == null) throw new InternalCompilerError(); } this.setFunction(function); this.setParameters(parameters); this.effect = Types.canonical(effect); } public void push(MethodBuilder mb) { //mb.getCodeBuilder().mapLineNumber(lineNumber); Val f = getFunction().getBinding(); Val[] ps = ValRef.getBindings(getParameters()); if(f instanceof Constant) { Constant cf = (Constant)f; Type returnType = cf.apply(mb, getFunction().getTypeParameters(), ps); if(Types.isBoxed(returnType)) mb.unbox(target.getType()); } else { mb.push(f, f.getType()); mb.pushBoxed(ps); mb.genericApply(ps.length); mb.unbox(target.getType()); } } @Override public void generateCode(MethodBuilder mb) { if(!target.generateOnFly) { push(mb); mb.store(target); } } @Override public void toString(PrintingContext context) { if(determineGenerateOnFly()) context.addInlineExpression(target, this); else toStringAux(context); } private void toStringAux(PrintingContext context) { context.indentation(); context.append(target); context.append("(" + target.occurrenceCount() + ")"); context.append(" = "); bodyToString(context); context.append('\n'); } public void bodyToString(PrintingContext context) { if(context.getErrorMarker() == this) context.append("!> "); if(hasEffect()) { context.append("<"); context.append(effect); context.append("> "); } context.append(getFunction()); for(ValRef parameter : getParameters()) { context.append(' '); context.append(parameter); } } @Override public String toString() { PrintingContext context = new PrintingContext(); toStringAux(context); return context.toString(); } @Override public void validate(SSAValidationContext context) { context.validate(target); if(target.getParent() != this) throw new InternalCompilerError(); context.validate(function); if(function.getParent() != this) throw new InternalCompilerError(); for(ValRef parameter : parameters) { context.validate(parameter); if(parameter.getParent() != this) throw new InternalCompilerError(); } //if(parameters.length == 0) // throw new InternalCompilerError(); MultiFunction mFun; try { mFun = Types.matchFunction(getFunction().getType(), getParameters().length); } catch (MatchException e) { context.setErrorMarker(this); throw new InternalCompilerError(); } for(int i=0;i 0) continue; parameter.replaceBy(jump.getParameter(0).getBinding()); } Val functionVal = getFunction().getBinding(); if(functionVal instanceof BoundVar) { BoundVarBinder parent_ = ((BoundVar)functionVal).parent; if(parent_ instanceof SSAFunction) { SSAFunction function = (SSAFunction)parent_; if(functionVal.hasMoreThanOneOccurences()) return; if(getParameters().length < function.getArity()) return; if(getParameters().length > function.getArity()) split(function.getArity()); inline(function); function.detach(); context.markModified("LetApply.beta-lambda"); } else if(parent_ instanceof LetApply) { LetApply apply = (LetApply)parent_; if(apply.hasEffect()) return; boolean hasJustOneOccurence = !functionVal.hasMoreThanOneOccurences(); if((hasJustOneOccurence && apply.getParent() == getParent()) || apply.isPartial()) { if(hasJustOneOccurence) { apply.detach(); setFunction(apply.getFunction()); setParameters(ValRef.concat(apply.getParameters(), getParameters())); } else { setFunction(apply.getFunction().copy()); setParameters(ValRef.concat(ValRef.copy(apply.getParameters()), getParameters())); } context.markModified("LetApply.merge-applications"); } } else if(parent_ instanceof SSABlock) { SSABlock parent = getParent(); if(parent_ != parent) return; if(parent.getFirstStatement() != this) return; if(!parent.hasMoreThanOneOccurences()) return; // We stop here, because situation can be handled by better transformations if(functionVal.hasMoreThanOneOccurences()) return; // We have now the following situation: // [c] ... f ... = // x = f ... // * this application is the only reference to f // * there are multiple references to [c] for(ContRef ref = parent.getOccurrence();ref != null; ref = ref.getNext()) if(!(ref.getParent() instanceof Jump)) return; // Finds the position of the functionVal in the parameter list of // the parent block. int position; for(position=0;position 0) { /*if(function.getParent() != null) { PrintingContext pc = new PrintingContext(); pc.append("----------------------------\n"); function.getParent().getParentFunction().toString(pc); pc.append("\n----\n"); function.toString(pc); pc.append("\n"); pc.append(function.getTypeParameters()); pc.append(" -> "); pc.append(this.function.getTypeParameters()); System.out.println(pc.toString()); }*/ function.replace(function.getTypeParameters(), this.function.getTypeParameters()); } if(getPrev() != null) getPrev().setAsLastStatement(); else headBlock.removeStatements(); // Create tail block SSABlock tailBlock = new SSABlock(new BoundVar[] {target}); thisFunction.addBlock(tailBlock); { SSAStatement stat = getNext(); while(stat != null) { SSAStatement temp = stat.getNext(); tailBlock.addStatement(stat); stat = temp; } } tailBlock.setExit(headBlock.getExit()); // Merge blocks thisFunction.mergeBlocks(function); headBlock.setExit(new Jump(function.getFirstBlock().createOccurrence(), parameters)); function.getReturnCont().replaceWith(tailBlock); this.function.remove(); // Note: No need to remove or detach this statement anymore // TODO remove function /* System.out.println("============================================"); System.out.println(thisFunction); */ } @Override public void collectFreeVariables(SSAFunction parentFunction, ArrayList vars) { function.collectFreeVariables(parentFunction, vars); for(ValRef parameter : parameters) parameter.collectFreeVariables(parentFunction, vars); } @Override public void replaceByApply(ValRef valRef, Val newFunction, Type[] typeParameters, Val[] parameters) { if(function == valRef) { valRef.remove(); setFunction(newFunction.createOccurrence(typeParameters)); setParameters(ValRef.concat(ValRef.createOccurrences(parameters), this.parameters)); } else super.replaceByApply(valRef, newFunction, typeParameters, parameters); } /** * Splits this application into two applications where the first has * the arity given as a parameter and the new application inserted * after this statement has the rest of the parameters. */ public void split(int arity) { if(arity == parameters.length) return; if(arity > parameters.length) throw new InternalCompilerError(); ValRef[] firstHalf = arity == 0 ? ValRef.EMPTY_ARRAY : Arrays.copyOf(parameters, arity); ValRef[] secondHalf = arity == parameters.length ? ValRef.EMPTY_ARRAY : Arrays.copyOfRange(parameters, arity, parameters.length); BoundVar newVar; try { MultiFunction mfun = Types.matchFunction(function.getType(), arity); newVar = new BoundVar(mfun.returnType); } catch (MatchException e) { throw new InternalCompilerError(); } LetApply newApply = new LetApply(target, effect, newVar.createOccurrence(), secondHalf); newApply.insertAfter(this); effect = Types.NO_EFFECTS; setTarget(newVar); setParameters(firstHalf); } /** * True, if the application may have side effects. */ public boolean hasEffect() { return effect != Types.NO_EFFECTS; } public void updateEffect() { try { MultiFunction mFun = Types.matchFunction(function.getType(), parameters.length); this.effect = mFun.effect; } catch (MatchException e) { throw new InternalCompilerError(e); } } @Override public void prepare(MethodBuilder mb) { function.getBinding().prepare(mb); } @Override public void forValRefs(ValRefVisitor visitor) { visitor.visit(function); for(ValRef parameter : parameters) visitor.visit(parameter); } }