package org.simantics.scl.compiler.constants; import java.util.ArrayList; import java.util.Arrays; import org.simantics.scl.compiler.common.names.Name; import org.simantics.scl.compiler.common.names.Named; import org.simantics.scl.compiler.internal.codegen.optimization.Optimization; import org.simantics.scl.compiler.internal.codegen.optimization.OptimizationMap; 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.SSAFunction; import org.simantics.scl.compiler.internal.codegen.ssa.exits.Jump; import org.simantics.scl.compiler.internal.codegen.ssa.statements.LetApply; import org.simantics.scl.compiler.internal.codegen.ssa.statements.LetFunctions; import org.simantics.scl.compiler.internal.codegen.utils.SSASimplificationContext; import org.simantics.scl.compiler.types.TVar; import org.simantics.scl.compiler.types.Type; import org.simantics.scl.compiler.types.Types; public class SCLConstant extends DelegateConstant implements Named { public Name name; // Needed only for debugging public SSAFunction definition; public SSAFunction inlinableDefinition; public int inlineArity = Integer.MAX_VALUE; public int inlinePhaseMask = 0xffffffff; public boolean isPrivate = false; public SCLConstant(Name name, Type type) { super(type); this.name = name; } public void setInlineArity(int inlineArity, int inlinePhaseMask) { this.inlineArity = inlineArity; this.inlinePhaseMask = inlinePhaseMask; //System.out.println(name + " " + inlineArity + " " + inlinePhaseMask); } public void setPrivate(boolean isPrivate) { this.isPrivate = isPrivate; } public void setDefinition(SSAFunction definition) { this.definition = definition; } public SSAFunction getDefinition() { return definition; } @Override public void inline(SSASimplificationContext context, LetApply apply) { if(inlineTailCallToSelf(context, apply)) { return; } /*if(tryBetaReduce(context, apply)) { return; }*/ if(basicInline(context, apply)) { return; } trySpecialize(context, apply); Optimization opt = OptimizationMap.OPTIMIZATIONS.get(name); if(opt != null) opt.inline(context, apply); } static int inlineCount = 0; private boolean canInlineInPhase(int phase) { return ((inlinePhaseMask >> phase)&1) == 1; } private boolean basicInline(SSASimplificationContext context, LetApply apply) { if(!canInlineInPhase(context.getPhase())) { //System.out.println("Cannot optimize " + name + " in phase " + context.getPhase()); return false; } ValRef functionRef = apply.getFunction(); ValRef[] parameters = apply.getParameters(); SSAFunction def = inlinableDefinition == null ? definition : inlinableDefinition; if(parameters.length < inlineArity && (!isPrivate || parameters.length != def.getArity() || hasMoreThanOneOccurences())) return false; //if(def.getArity() == 0) // return false; // FIXME //System.out.println("basicInline: " + apply); //System.out.println("def: " + def); if(isPrivate && !hasMoreThanOneOccurences()) context.removeConstant(name); else def = (SSAFunction)def.copy(); if(parameters.length >= def.getArity()) { if(parameters.length != def.getArity()) apply.split(def.getArity()); apply.inline(def); context.markModified("SCLConstant.beta-constant " + getName()); } else /*if(parameters.length < def.getArity())*/ { def.applyTypes(functionRef.getTypeParameters()); def.apply(apply.lineNumber, parameters); def.setTarget(apply.getTarget()); new LetFunctions(def).insertBefore(apply); apply.remove(); context.markModified("SCLConstant.partial-beta-constant " + getName()); } /*Name newName = Name.create(name.module, name.name + "_S" + (++inlineCount)); SCLConstant newConstant = new SCLConstant(newName, type); newConstant.setPrivate(true); newConstant.setDefinition(definition.copy()); */ /*System.out.println("*** 1 *************************************"); System.out.println(definition); System.out.println("*** 2 *************************************"); System.out.println(newConstant.definition); function.remove(); apply.setFunction(newConstant.createOccurrence(function.getTypeParameters())); context.addConstant(newConstant); context.markModified(); newConstant.trySpecialize(context, apply); */ return true; } private boolean inlineTailCallToSelf(SSASimplificationContext context, LetApply apply) { SSAFunction thisFunction = apply.getParent().getParent(); if(thisFunction != definition) return false; ValRef ref = apply.getTarget().getOccurrence(); if(ref == null || ref.getNext() != null) return false; if(!(ref.getParent() instanceof Jump)) return false; Jump jump = (Jump)ref.getParent(); if(jump.getParameters().length != 1) return false; if(jump.getTarget().getBinding() != thisFunction.getReturnCont()) return false; if(apply.getParameters().length != thisFunction.getArity()) return false; jump.getTarget().remove(); jump.setTarget(thisFunction.getFirstBlock().createOccurrence()); jump.setParameters(apply.getParameters()); apply.getFunction().remove(); apply.detach(); context.markModified("SCLConstant.simplify-tail-call"); return true; } private void trySpecialize(SSASimplificationContext context, LetApply apply) { if(!isPrivate) return; if(hasMoreThanOneOccurences()) return; if(apply.getParent().getParent() == definition) return; // Specialization of type parameters { ValRef functionRef = apply.getFunction(); Type[] pValues = functionRef.getTypeParameters(); boolean hasComplexTypes = false; for(Type type : pValues) if(!(Types.canonical(type) instanceof TVar)) { hasComplexTypes = true; break; } if(hasComplexTypes) { /*PrintingContext pc = new PrintingContext(); pc.append(">> BEFORE >>\n"); definition.toString(pc);*/ TVar[] pVars = definition.getTypeParameters(); TVar[] pVarsTail; if(pVars.length == pValues.length) pVarsTail = TVar.EMPTY_ARRAY; else { pVarsTail = Arrays.copyOfRange(pVars, pValues.length, pVars.length); pVars = Arrays.copyOf(pVars, pValues.length); } type = Types.instantiate(type, pValues); /*pc.append("REPLACE: "); pc.append(pVars); pc.append(" -> "); pc.append(pValues); pc.append('\n');*/ definition.replace(pVars, pValues); TVar[] newParameters = Types.freeVarsArray(pValues); type = Types.forAll(newParameters, type); functionRef.setTypeParameters(newParameters); definition.setTypeParameters(Types.concat(newParameters, pVarsTail)); /*pc.append(">> AFTER >>\n"); definition.toString(pc); System.out.println(pc);*/ context.markModified("SCLConstant.specialize-types"); } } if(!definition.getFirstBlock().hasNoOccurences()) // TODO We can flex this requirement if all jumps to the first block // give same values to the first block return; // Specialization of parameters ValRef[] parameters = apply.getParameters(); ValRef[] specialization = null; int arity = Math.min(parameters.length, definition.getArity()); for(int i=0;i newParameters = new ArrayList(parameters.length); for(int i=0;i " + constant.getBinding()); System.out.println("replace: " + this); System.out.println("of type: " + this.getType()); System.out.println("by: " + binding); System.out.println("of type: " + binding.getType()); System.out.println("parameters: " + Types.toString(definition.getTypeParameters())); System.out.println("parameters2: " + Types.toString(constant.getTypeParameters())); System.out.println("private: " + isPrivate);*/ replaceBy(binding, definition.getTypeParameters(), constant.getTypeParameters()); if(isPrivate) { definition.destroy(); context.removeConstant(name); } context.markModified("SCLConstant.simplify-constant"); return true; } return false; } public void simplify(SSASimplificationContext context) { if(!hasNoOccurences() /* TODO why this condition is needed? */) { if(simplifyConstantFunction(context)) return; } /*if(isPrivate) definition.tryToMakeMonadic(context);*/ definition.simplify(context); if(inlineArity == Integer.MAX_VALUE && definition.isSimpleEnoughForInline()) { inlineArity = definition.getArity(); inlinableDefinition = (SSAFunction)definition.copy(); context.markModified("mark inlineable " + name); // FIXME this will make self calling function inlinable that may crash the compiler } } public void saveInlinableDefinition() { if(inlineArity < Integer.MAX_VALUE) inlinableDefinition = (SSAFunction)definition.copy(); } public void cleanup() { if(definition != null) definition.cleanup(); if(inlinableDefinition != null) inlinableDefinition.cleanup(); } }