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 int oldLineNumber = mb.lineNumber(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());
73 mb.lineNumber(oldLineNumber);
77 public void generateCode(MethodBuilder mb) {
78 if(!target.generateOnFly) {
79 mb.lineNumber(lineNumber);
86 public void toString(PrintingContext context) {
87 if(/*target.getLabel() == null &&*/ determineGenerateOnFly())
88 context.addInlineExpression(target, this);
93 private void toStringAux(PrintingContext context) {
94 context.indentation();
95 context.append(target);
96 context.append("(" + target.occurrenceCount() + ")");
97 context.append(" = ");
98 bodyToString(context);
103 public void bodyToString(PrintingContext context) {
104 if(context.getErrorMarker() == this)
105 context.append("!> ");
106 context.append("L" + lineNumber + ": ");
109 context.append(effect);
110 context.append("> ");
112 context.append(getFunction());
113 for(ValRef parameter : getParameters()) {
115 context.append(parameter);
120 public String toString() {
121 PrintingContext context = new PrintingContext();
122 toStringAux(context);
123 return context.toString();
127 public void validate(SSAValidationContext context) {
128 context.validate(target);
129 if(target.getParent() != this)
130 throw new InternalCompilerError();
131 context.validate(function);
132 if(function.getParent() != this)
133 throw new InternalCompilerError();
134 for(ValRef parameter : parameters) {
135 context.validate(parameter);
136 if(parameter.getParent() != this)
137 throw new InternalCompilerError();
139 //if(parameters.length == 0)
140 // throw new InternalCompilerError();
145 mFun = Types.matchFunction(getFunction().getType(), getParameters().length);
146 } catch (MatchException e) {
147 context.setErrorMarker(this);
148 throw new InternalCompilerError();
150 for(int i=0;i<getParameters().length;++i)
151 context.assertSubsumes(this, getParameters()[i].getType(), mFun.parameterTypes[i]);
152 context.assertSubsumes(this, target.getType(), mFun.returnType);
153 context.assertEqualsEffect(this, effect, mFun.effect);
157 public void destroy() {
158 getFunction().remove();
159 for(ValRef parameter : getParameters())
164 public void simplify(SSASimplificationContext context) {
165 if(target.hasNoOccurences() && !hasEffect()) {
167 context.markModified("LetApply.dead-let-statement");
170 // TODO this is quite heavy way for inlining constants
171 for(int i=0;i<parameters.length;++i) {
172 ValRef parameter = parameters[i];
173 Val value = parameter.getBinding();
174 if(!(value instanceof SCLConstant))
176 SCLConstant constant = (SCLConstant)value;
177 if(constant.inlineArity != 0)
179 SSAFunction definition = constant.definition;
180 SSABlock block = definition.getFirstBlock();
181 if(block.getFirstStatement() != null || !(block.getExit() instanceof Jump))
183 Jump jump = (Jump)block.getExit();
184 if(jump.getTarget().getBinding() != definition.getReturnCont())
186 if(jump.getParameter(0).getTypeParameters().length > 0)
188 parameter.replaceBy(jump.getParameter(0).getBinding());
190 Val functionVal = getFunction().getBinding();
191 if(functionVal instanceof BoundVar) {
192 BoundVarBinder parent_ = ((BoundVar)functionVal).parent;
193 if(parent_ instanceof SSAFunction) {
194 SSAFunction function = (SSAFunction)parent_;
195 if(functionVal.hasMoreThanOneOccurences())
197 if(getParameters().length < function.getArity())
199 if(getParameters().length > function.getArity())
200 split(function.getArity());
203 context.markModified("LetApply.beta-lambda");
205 else if(parent_ instanceof LetApply) {
206 LetApply apply = (LetApply)parent_;
207 if(apply.hasEffect())
209 boolean hasJustOneOccurence = !functionVal.hasMoreThanOneOccurences();
210 if((hasJustOneOccurence && apply.getParent() == getParent()) ||
212 if(hasJustOneOccurence) {
214 setFunction(apply.getFunction());
215 setParameters(ValRef.concat(apply.getParameters(), getParameters()));
218 setFunction(apply.getFunction().copy());
219 setParameters(ValRef.concat(ValRef.copy(apply.getParameters()), getParameters()));
221 context.markModified("LetApply.merge-applications");
224 else if(parent_ instanceof SSABlock) {
225 SSABlock parent = getParent();
226 if(parent_ != parent)
228 if(parent.getFirstStatement() != this)
230 if(!parent.hasMoreThanOneOccurences())
231 return; // We stop here, because situation can be handled by better transformations
232 if(functionVal.hasMoreThanOneOccurences())
234 // We have now the following situation:
237 // * this application is the only reference to f
238 // * there are multiple references to [c]
239 for(ContRef ref = parent.getOccurrence();ref != null; ref = ref.getNext())
240 if(!(ref.getParent() instanceof Jump))
243 // Finds the position of the functionVal in the parameter list of
246 for(position=0;position<parent.getParameters().length;++position)
247 if(parent.getParameters()[position] == functionVal)
249 if(position == parent.getParameters().length)
250 throw new InternalCompilerError();
253 for(ContRef ref = parent.getOccurrence();ref != null; ref = ref.getNext()) {
254 Jump jump = (Jump)ref.getParent();
255 SSABlock block = jump.getParent();
257 BoundVar newTarget = new BoundVar(target.getType());
258 block.addStatement(new LetApply(newTarget, effect, jump.getParameter(position), ValRef.copy(parameters)));
259 jump.setParameter(position, newTarget.createOccurrence());
262 parent.setParameter(position, target);
264 context.markModified("LetApply.hoist-apply");
267 else if(functionVal instanceof Constant) {
268 ((Constant)functionVal).inline(context, this);
272 public boolean isPartial() {
273 return parameters.length < function.getBinding().getEffectiveArity();
277 * Removes apply if it does not have parameters.
279 public void removeDegenerated() {
280 if(getParameters().length == 0) {
281 target.replaceBy(getFunction());
282 getFunction().remove();
287 public boolean determineGenerateOnFly() {
290 ValRef ref = target.getOccurrence();
291 if(ref == null || ref.getNext() != null)
293 Object parent = ref.getParent();
294 if(parent instanceof SSAStatement) {
295 if(((SSAStatement)parent).getParent() != getParent())
298 else if(parent instanceof SSAExit) {
299 if(((SSAExit)parent).getParent() != getParent())
301 if(parent instanceof Switch)
310 public void markGenerateOnFly() {
311 target.generateOnFly = determineGenerateOnFly();
314 public ValRef getFunction() {
318 public void setFunction(ValRef function) {
319 this.function = function;
320 function.setParent(this);
323 public ValRef[] getParameters() {
327 public void setParameters(ValRef[] parameters) {
328 /*if(SCLCompilerConfiguration.DEBUG)
329 if(parameters.length == 0)
330 throw new InternalCompilerError();*/
331 this.parameters = parameters;
332 for(ValRef parameter : parameters)
333 parameter.setParent(this);
337 public SSAStatement copy(CopyContext context) {
338 LetApply result = new LetApply(context.copy(target),
339 context.copyType(effect),
340 context.copy(function),
341 context.copy(parameters));
346 public void replace(TVar[] vars, Type[] replacements) {
347 target.replace(vars, replacements);
348 function.replace(vars, replacements);
349 effect = effect.replace(vars, replacements);
350 for(ValRef parameter : parameters)
351 parameter.replace(vars, replacements);
355 * Inlines the application by the given function.
356 * It is assumed that the function has the same number
357 * of parameters as this one and there are no other
358 * references to the function (copy function before
359 * inlining if necessary).
361 public void inline(SSAFunction function) {
362 if(function.getArity() != parameters.length)
363 throw new InternalCompilerError();
365 SSABlock headBlock = getParent();
366 SSAFunction thisFunction = headBlock.getParent();
368 SSAFunction curParent=thisFunction;
370 if(curParent == function)
372 ClosureBinder binder = curParent.getParent();
375 curParent = binder.getParentFunction();
379 /*System.out.println("--- INLINING -------------------------------");
380 System.out.println(thisFunction);
381 System.out.println("Function name: " + getFunction().getBinding());
382 System.out.println("++++++++++++++++++++++++++++++++++++++++++++");
383 System.out.println(function);
386 if(this.function.getTypeParameters().length > 0) {
387 /*if(function.getParent() != null) {
388 PrintingContext pc = new PrintingContext();
389 pc.append("----------------------------\n");
390 function.getParent().getParentFunction().toString(pc);
391 pc.append("\n----\n");
392 function.toString(pc);
394 pc.append(function.getTypeParameters());
396 pc.append(this.function.getTypeParameters());
397 System.out.println(pc.toString());
399 function.replace(function.getTypeParameters(), this.function.getTypeParameters());
402 if(getPrev() != null)
403 getPrev().setAsLastStatement();
405 headBlock.removeStatements();
408 SSABlock tailBlock = new SSABlock(new BoundVar[] {target});
409 thisFunction.addBlock(tailBlock);
411 SSAStatement stat = getNext();
412 while(stat != null) {
413 SSAStatement temp = stat.getNext();
414 tailBlock.addStatement(stat);
418 tailBlock.setExit(headBlock.getExit());
421 thisFunction.mergeBlocks(function);
423 headBlock.setExit(new Jump(lineNumber, function.getFirstBlock().createOccurrence(), parameters));
424 function.getReturnCont().replaceWith(tailBlock);
426 this.function.remove();
427 // Note: No need to remove or detach this statement anymore
429 // TODO remove function
431 System.out.println("============================================");
432 System.out.println(thisFunction);
437 public void collectFreeVariables(SSAFunction parentFunction,
438 ArrayList<ValRef> vars) {
439 function.collectFreeVariables(parentFunction, vars);
440 for(ValRef parameter : parameters)
441 parameter.collectFreeVariables(parentFunction, vars);
445 public void replaceByApply(ValRef valRef, Val newFunction, Type[] typeParameters, Val[] parameters) {
446 if(function == valRef) {
448 setFunction(newFunction.createOccurrence(typeParameters));
449 setParameters(ValRef.concat(ValRef.createOccurrences(parameters), this.parameters));
452 super.replaceByApply(valRef, newFunction, typeParameters, parameters);
456 * Splits this application into two applications where the first has
457 * the arity given as a parameter and the new application inserted
458 * after this statement has the rest of the parameters.
460 public void split(int arity) {
461 if(arity == parameters.length)
463 if(arity > parameters.length)
464 throw new InternalCompilerError();
465 ValRef[] firstHalf = arity == 0 ? ValRef.EMPTY_ARRAY : Arrays.copyOf(parameters, arity);
466 ValRef[] secondHalf = arity == parameters.length ? ValRef.EMPTY_ARRAY : Arrays.copyOfRange(parameters, arity, parameters.length);
469 MultiFunction mfun = Types.matchFunction(function.getType(), arity);
470 newVar = new BoundVar(mfun.returnType);
471 } catch (MatchException e) {
472 throw new InternalCompilerError();
474 LetApply newApply = new LetApply(target, effect,
475 newVar.createOccurrence(), secondHalf);
476 newApply.insertAfter(this);
477 effect = Types.NO_EFFECTS;
479 setParameters(firstHalf);
483 * True, if the application may have side effects.
485 public boolean hasEffect() {
486 return effect != Types.NO_EFFECTS;
489 public void updateEffect() {
491 MultiFunction mFun = Types.matchFunction(function.getType(), parameters.length);
492 this.effect = mFun.effect;
493 } catch (MatchException e) {
494 throw new InternalCompilerError(e);
499 public void prepare(MethodBuilder mb) {
500 function.getBinding().prepare(mb);
504 public void forValRefs(ValRefVisitor visitor) {
505 visitor.visit(function);
506 for(ValRef parameter : parameters)
507 visitor.visit(parameter);
511 public void cleanup() {
513 for(ValRef parameter : parameters)