1 package org.simantics.scl.compiler.internal.codegen.ssa.statements;
3 import java.util.ArrayList;
4 import java.util.Arrays;
6 import org.simantics.scl.compiler.common.exceptions.InternalCompilerError;
7 import org.simantics.scl.compiler.constants.Constant;
8 import org.simantics.scl.compiler.constants.SCLConstant;
9 import org.simantics.scl.compiler.internal.codegen.continuations.ContRef;
10 import org.simantics.scl.compiler.internal.codegen.references.BoundVar;
11 import org.simantics.scl.compiler.internal.codegen.references.Val;
12 import org.simantics.scl.compiler.internal.codegen.references.ValRef;
13 import org.simantics.scl.compiler.internal.codegen.ssa.SSABlock;
14 import org.simantics.scl.compiler.internal.codegen.ssa.SSAExit;
15 import org.simantics.scl.compiler.internal.codegen.ssa.SSAFunction;
16 import org.simantics.scl.compiler.internal.codegen.ssa.SSAStatement;
17 import org.simantics.scl.compiler.internal.codegen.ssa.binders.BoundVarBinder;
18 import org.simantics.scl.compiler.internal.codegen.ssa.binders.ClosureBinder;
19 import org.simantics.scl.compiler.internal.codegen.ssa.binders.ValRefBinder;
20 import org.simantics.scl.compiler.internal.codegen.ssa.exits.Jump;
21 import org.simantics.scl.compiler.internal.codegen.ssa.exits.Switch;
22 import org.simantics.scl.compiler.internal.codegen.utils.CopyContext;
23 import org.simantics.scl.compiler.internal.codegen.utils.MethodBuilder;
24 import org.simantics.scl.compiler.internal.codegen.utils.PrintingContext;
25 import org.simantics.scl.compiler.internal.codegen.utils.SSASimplificationContext;
26 import org.simantics.scl.compiler.internal.codegen.utils.SSAValidationContext;
27 import org.simantics.scl.compiler.internal.codegen.utils.ValRefVisitor;
28 import org.simantics.scl.compiler.top.SCLCompilerConfiguration;
29 import org.simantics.scl.compiler.types.TVar;
30 import org.simantics.scl.compiler.types.Type;
31 import org.simantics.scl.compiler.types.Types;
32 import org.simantics.scl.compiler.types.exceptions.MatchException;
33 import org.simantics.scl.compiler.types.util.MultiFunction;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
37 public class LetApply extends LetStatement implements ValRefBinder {
38 private static final Logger LOGGER = LoggerFactory.getLogger(LetApply.class);
40 private ValRef function;
41 private ValRef[] parameters;
44 public LetApply(BoundVar target, Type effect, ValRef function, ValRef ... parameters) {
46 if(SCLCompilerConfiguration.DEBUG) {
48 throw new InternalCompilerError();
49 if(function.getBinding() == null)
50 throw new InternalCompilerError();
52 this.setFunction(function);
53 this.setParameters(parameters);
54 this.effect = Types.canonical(effect);
57 public void push(MethodBuilder mb) {
58 //mb.getCodeBuilder().mapLineNumber(lineNumber);
59 Val f = getFunction().getBinding();
60 Val[] ps = ValRef.getBindings(getParameters());
61 if(f instanceof Constant) {
62 Constant cf = (Constant)f;
63 Type returnType = cf.apply(mb, getFunction().getTypeParameters(), ps);
64 if(Types.isBoxed(returnType))
65 mb.unbox(target.getType());
68 mb.push(f, f.getType());
70 mb.genericApply(ps.length);
71 mb.unbox(target.getType());
76 public void generateCode(MethodBuilder mb) {
77 if(!target.generateOnFly) {
84 public void toString(PrintingContext context) {
85 if(/*target.getLabel() == null &&*/ determineGenerateOnFly())
86 context.addInlineExpression(target, this);
91 private void toStringAux(PrintingContext context) {
92 context.indentation();
93 context.append(target);
94 context.append("(" + target.occurrenceCount() + ")");
95 context.append(" = ");
96 bodyToString(context);
101 public void bodyToString(PrintingContext context) {
102 if(context.getErrorMarker() == this)
103 context.append("!> ");
106 context.append(effect);
107 context.append("> ");
109 context.append(getFunction());
110 for(ValRef parameter : getParameters()) {
112 context.append(parameter);
117 public String toString() {
118 PrintingContext context = new PrintingContext();
119 toStringAux(context);
120 return context.toString();
124 public void validate(SSAValidationContext context) {
125 context.validate(target);
126 if(target.getParent() != this)
127 throw new InternalCompilerError();
128 context.validate(function);
129 if(function.getParent() != this)
130 throw new InternalCompilerError();
131 for(ValRef parameter : parameters) {
132 context.validate(parameter);
133 if(parameter.getParent() != this)
134 throw new InternalCompilerError();
136 //if(parameters.length == 0)
137 // throw new InternalCompilerError();
142 mFun = Types.matchFunction(getFunction().getType(), getParameters().length);
143 } catch (MatchException e) {
144 context.setErrorMarker(this);
145 throw new InternalCompilerError();
147 for(int i=0;i<getParameters().length;++i)
148 context.assertSubsumes(this, getParameters()[i].getType(), mFun.parameterTypes[i]);
149 context.assertSubsumes(this, target.getType(), mFun.returnType);
150 context.assertEqualsEffect(this, effect, mFun.effect);
154 public void destroy() {
155 getFunction().remove();
156 for(ValRef parameter : getParameters())
161 public void simplify(SSASimplificationContext context) {
162 if(target.hasNoOccurences() && !hasEffect()) {
164 context.markModified("LetApply.dead-let-statement");
167 // TODO this is quite heavy way for inlining constants
168 for(int i=0;i<parameters.length;++i) {
169 ValRef parameter = parameters[i];
170 Val value = parameter.getBinding();
171 if(!(value instanceof SCLConstant))
173 SCLConstant constant = (SCLConstant)value;
174 if(constant.inlineArity != 0)
176 SSAFunction definition = constant.definition;
177 SSABlock block = definition.getFirstBlock();
178 if(block.getFirstStatement() != null || !(block.getExit() instanceof Jump))
180 Jump jump = (Jump)block.getExit();
181 if(jump.getTarget().getBinding() != definition.getReturnCont())
183 if(jump.getParameter(0).getTypeParameters().length > 0)
185 parameter.replaceBy(jump.getParameter(0).getBinding());
187 Val functionVal = getFunction().getBinding();
188 if(functionVal instanceof BoundVar) {
189 BoundVarBinder parent_ = ((BoundVar)functionVal).parent;
190 if(parent_ instanceof SSAFunction) {
191 SSAFunction function = (SSAFunction)parent_;
192 if(functionVal.hasMoreThanOneOccurences())
194 if(getParameters().length < function.getArity())
196 if(getParameters().length > function.getArity())
197 split(function.getArity());
200 context.markModified("LetApply.beta-lambda");
202 else if(parent_ instanceof LetApply) {
203 LetApply apply = (LetApply)parent_;
204 if(apply.hasEffect())
206 boolean hasJustOneOccurence = !functionVal.hasMoreThanOneOccurences();
207 if((hasJustOneOccurence && apply.getParent() == getParent()) ||
209 if(hasJustOneOccurence) {
211 setFunction(apply.getFunction());
212 setParameters(ValRef.concat(apply.getParameters(), getParameters()));
215 setFunction(apply.getFunction().copy());
216 setParameters(ValRef.concat(ValRef.copy(apply.getParameters()), getParameters()));
218 context.markModified("LetApply.merge-applications");
221 else if(parent_ instanceof SSABlock) {
222 SSABlock parent = getParent();
223 if(parent_ != parent)
225 if(parent.getFirstStatement() != this)
227 if(!parent.hasMoreThanOneOccurences())
228 return; // We stop here, because situation can be handled by better transformations
229 if(functionVal.hasMoreThanOneOccurences())
231 // We have now the following situation:
234 // * this application is the only reference to f
235 // * there are multiple references to [c]
236 for(ContRef ref = parent.getOccurrence();ref != null; ref = ref.getNext())
237 if(!(ref.getParent() instanceof Jump))
240 // Finds the position of the functionVal in the parameter list of
243 for(position=0;position<parent.getParameters().length;++position)
244 if(parent.getParameters()[position] == functionVal)
246 if(position == parent.getParameters().length)
247 throw new InternalCompilerError();
250 for(ContRef ref = parent.getOccurrence();ref != null; ref = ref.getNext()) {
251 Jump jump = (Jump)ref.getParent();
252 SSABlock block = jump.getParent();
254 BoundVar newTarget = new BoundVar(target.getType());
255 block.addStatement(new LetApply(newTarget, effect, jump.getParameter(position), ValRef.copy(parameters)));
256 jump.setParameter(position, newTarget.createOccurrence());
259 parent.setParameter(position, target);
261 context.markModified("LetApply.hoist-apply");
264 else if(functionVal instanceof Constant) {
265 ((Constant)functionVal).inline(context, this);
269 public boolean isPartial() {
270 return parameters.length < function.getBinding().getEffectiveArity();
274 * Removes apply if it does not have parameters.
276 public void removeDegenerated() {
277 if(getParameters().length == 0) {
278 target.replaceBy(getFunction());
279 getFunction().remove();
284 public boolean determineGenerateOnFly() {
287 ValRef ref = target.getOccurrence();
288 if(ref == null || ref.getNext() != null)
290 Object parent = ref.getParent();
291 if(parent instanceof SSAStatement) {
292 if(((SSAStatement)parent).getParent() != getParent())
295 else if(parent instanceof SSAExit) {
296 if(((SSAExit)parent).getParent() != getParent())
298 if(parent instanceof Switch)
307 public void markGenerateOnFly() {
308 target.generateOnFly = determineGenerateOnFly();
311 public ValRef getFunction() {
315 public void setFunction(ValRef function) {
316 this.function = function;
317 function.setParent(this);
320 public ValRef[] getParameters() {
324 public void setParameters(ValRef[] parameters) {
325 /*if(SCLCompilerConfiguration.DEBUG)
326 if(parameters.length == 0)
327 throw new InternalCompilerError();*/
328 this.parameters = parameters;
329 for(ValRef parameter : parameters)
330 parameter.setParent(this);
334 public SSAStatement copy(CopyContext context) {
335 LetApply result = new LetApply(context.copy(target),
336 context.copyType(effect),
337 context.copy(function),
338 context.copy(parameters));
343 public void replace(TVar[] vars, Type[] replacements) {
344 target.replace(vars, replacements);
345 function.replace(vars, replacements);
346 effect = effect.replace(vars, replacements);
347 for(ValRef parameter : parameters)
348 parameter.replace(vars, replacements);
352 * Inlines the application by the given function.
353 * It is assumed that the function has the same number
354 * of parameters as this one and there are no other
355 * references to the function (copy function before
356 * inlining if necessary).
358 public void inline(SSAFunction function) {
359 if(function.getArity() != parameters.length)
360 throw new InternalCompilerError();
362 SSABlock headBlock = getParent();
363 SSAFunction thisFunction = headBlock.getParent();
365 SSAFunction curParent=thisFunction;
367 if(curParent == function)
369 ClosureBinder binder = curParent.getParent();
372 curParent = binder.getParentFunction();
376 /*System.out.println("--- INLINING -------------------------------");
377 System.out.println(thisFunction);
378 System.out.println("Function name: " + getFunction().getBinding());
379 System.out.println("++++++++++++++++++++++++++++++++++++++++++++");
380 System.out.println(function);
383 if(this.function.getTypeParameters().length > 0) {
384 /*if(function.getParent() != null) {
385 PrintingContext pc = new PrintingContext();
386 pc.append("----------------------------\n");
387 function.getParent().getParentFunction().toString(pc);
388 pc.append("\n----\n");
389 function.toString(pc);
391 pc.append(function.getTypeParameters());
393 pc.append(this.function.getTypeParameters());
394 System.out.println(pc.toString());
396 function.replace(function.getTypeParameters(), this.function.getTypeParameters());
399 if(getPrev() != null)
400 getPrev().setAsLastStatement();
402 headBlock.removeStatements();
405 SSABlock tailBlock = new SSABlock(new BoundVar[] {target});
406 thisFunction.addBlock(tailBlock);
408 SSAStatement stat = getNext();
409 while(stat != null) {
410 SSAStatement temp = stat.getNext();
411 tailBlock.addStatement(stat);
415 tailBlock.setExit(headBlock.getExit());
418 thisFunction.mergeBlocks(function);
420 headBlock.setExit(new Jump(function.getFirstBlock().createOccurrence(),
422 function.getReturnCont().replaceWith(tailBlock);
424 this.function.remove();
425 // Note: No need to remove or detach this statement anymore
427 // TODO remove function
429 System.out.println("============================================");
430 System.out.println(thisFunction);
435 public void collectFreeVariables(SSAFunction parentFunction,
436 ArrayList<ValRef> vars) {
437 function.collectFreeVariables(parentFunction, vars);
438 for(ValRef parameter : parameters)
439 parameter.collectFreeVariables(parentFunction, vars);
443 public void replaceByApply(ValRef valRef, Val newFunction, Type[] typeParameters, Val[] parameters) {
444 if(function == valRef) {
446 setFunction(newFunction.createOccurrence(typeParameters));
447 setParameters(ValRef.concat(ValRef.createOccurrences(parameters), this.parameters));
450 super.replaceByApply(valRef, newFunction, typeParameters, parameters);
454 * Splits this application into two applications where the first has
455 * the arity given as a parameter and the new application inserted
456 * after this statement has the rest of the parameters.
458 public void split(int arity) {
459 if(arity == parameters.length)
461 if(arity > parameters.length)
462 throw new InternalCompilerError();
463 ValRef[] firstHalf = arity == 0 ? ValRef.EMPTY_ARRAY : Arrays.copyOf(parameters, arity);
464 ValRef[] secondHalf = arity == parameters.length ? ValRef.EMPTY_ARRAY : Arrays.copyOfRange(parameters, arity, parameters.length);
467 MultiFunction mfun = Types.matchFunction(function.getType(), arity);
468 newVar = new BoundVar(mfun.returnType);
469 } catch (MatchException e) {
470 throw new InternalCompilerError();
472 LetApply newApply = new LetApply(target, effect,
473 newVar.createOccurrence(), secondHalf);
474 newApply.insertAfter(this);
475 effect = Types.NO_EFFECTS;
477 setParameters(firstHalf);
481 * True, if the application may have side effects.
483 public boolean hasEffect() {
484 return effect != Types.NO_EFFECTS;
487 public void updateEffect() {
489 MultiFunction mFun = Types.matchFunction(function.getType(), parameters.length);
490 this.effect = mFun.effect;
491 } catch (MatchException e) {
492 throw new InternalCompilerError(e);
497 public void prepare(MethodBuilder mb) {
498 function.getBinding().prepare(mb);
502 public void forValRefs(ValRefVisitor visitor) {
503 visitor.visit(function);
504 for(ValRef parameter : parameters)
505 visitor.visit(parameter);
509 public void cleanup() {
511 for(ValRef parameter : parameters)