1 package org.simantics.scl.compiler.internal.codegen.ssa.statements;
\r
3 import java.util.ArrayList;
\r
4 import java.util.Arrays;
\r
6 import org.simantics.scl.compiler.common.exceptions.InternalCompilerError;
\r
7 import org.simantics.scl.compiler.constants.Constant;
\r
8 import org.simantics.scl.compiler.constants.SCLConstant;
\r
9 import org.simantics.scl.compiler.internal.codegen.continuations.ContRef;
\r
10 import org.simantics.scl.compiler.internal.codegen.references.BoundVar;
\r
11 import org.simantics.scl.compiler.internal.codegen.references.Val;
\r
12 import org.simantics.scl.compiler.internal.codegen.references.ValRef;
\r
13 import org.simantics.scl.compiler.internal.codegen.ssa.SSABlock;
\r
14 import org.simantics.scl.compiler.internal.codegen.ssa.SSAExit;
\r
15 import org.simantics.scl.compiler.internal.codegen.ssa.SSAFunction;
\r
16 import org.simantics.scl.compiler.internal.codegen.ssa.SSAStatement;
\r
17 import org.simantics.scl.compiler.internal.codegen.ssa.binders.BoundVarBinder;
\r
18 import org.simantics.scl.compiler.internal.codegen.ssa.binders.ValRefBinder;
\r
19 import org.simantics.scl.compiler.internal.codegen.ssa.exits.Jump;
\r
20 import org.simantics.scl.compiler.internal.codegen.ssa.exits.Switch;
\r
21 import org.simantics.scl.compiler.internal.codegen.utils.CopyContext;
\r
22 import org.simantics.scl.compiler.internal.codegen.utils.MethodBuilder;
\r
23 import org.simantics.scl.compiler.internal.codegen.utils.PrintingContext;
\r
24 import org.simantics.scl.compiler.internal.codegen.utils.SSASimplificationContext;
\r
25 import org.simantics.scl.compiler.internal.codegen.utils.SSAValidationContext;
\r
26 import org.simantics.scl.compiler.internal.codegen.utils.ValRefVisitor;
\r
27 import org.simantics.scl.compiler.top.SCLCompilerConfiguration;
\r
28 import org.simantics.scl.compiler.types.TVar;
\r
29 import org.simantics.scl.compiler.types.Type;
\r
30 import org.simantics.scl.compiler.types.Types;
\r
31 import org.simantics.scl.compiler.types.exceptions.MatchException;
\r
32 import org.simantics.scl.compiler.types.util.MultiFunction;
\r
34 public class LetApply extends LetStatement implements ValRefBinder {
\r
35 private ValRef function;
\r
36 private ValRef[] parameters;
\r
39 public LetApply(BoundVar target, Type effect, ValRef function, ValRef ... parameters) {
\r
41 if(SCLCompilerConfiguration.DEBUG) {
\r
43 throw new InternalCompilerError();
\r
44 if(function.getBinding() == null)
\r
45 throw new InternalCompilerError();
\r
47 this.setFunction(function);
\r
48 this.setParameters(parameters);
\r
49 this.effect = Types.canonical(effect);
\r
52 public void push(MethodBuilder mb) {
\r
53 //mb.getCodeBuilder().mapLineNumber(lineNumber);
\r
54 Val f = getFunction().getBinding();
\r
55 Val[] ps = ValRef.getBindings(getParameters());
\r
56 if(f instanceof Constant) {
\r
57 Constant cf = (Constant)f;
\r
58 Type returnType = cf.apply(mb, getFunction().getTypeParameters(), ps);
\r
59 if(Types.isBoxed(returnType))
\r
60 mb.unbox(target.getType());
\r
63 mb.push(f, f.getType());
\r
65 mb.genericApply(ps.length);
\r
66 mb.unbox(target.getType());
\r
71 public void generateCode(MethodBuilder mb) {
\r
72 if(!target.generateOnFly) {
\r
79 public void toString(PrintingContext context) {
\r
80 if(determineGenerateOnFly())
\r
81 context.addInlineExpression(target, this);
\r
83 toStringAux(context);
\r
86 private void toStringAux(PrintingContext context) {
\r
87 context.indentation();
\r
88 context.append(target);
\r
89 context.append("(" + target.occurrenceCount() + ")");
\r
90 context.append(" = ");
\r
91 bodyToString(context);
\r
92 context.append('\n');
\r
96 public void bodyToString(PrintingContext context) {
\r
97 if(context.getErrorMarker() == this)
\r
98 context.append("!> ");
\r
100 context.append("<");
\r
101 context.append(effect);
\r
102 context.append("> ");
\r
104 context.append(getFunction());
\r
105 for(ValRef parameter : getParameters()) {
\r
106 context.append(' ');
\r
107 context.append(parameter);
\r
112 public String toString() {
\r
113 PrintingContext context = new PrintingContext();
\r
114 toStringAux(context);
\r
115 return context.toString();
\r
119 public void validate(SSAValidationContext context) {
\r
120 context.validate(target);
\r
121 if(target.getParent() != this)
\r
122 throw new InternalCompilerError();
\r
123 context.validate(function);
\r
124 if(function.getParent() != this)
\r
125 throw new InternalCompilerError();
\r
126 for(ValRef parameter : parameters) {
\r
127 context.validate(parameter);
\r
128 if(parameter.getParent() != this)
\r
129 throw new InternalCompilerError();
\r
131 //if(parameters.length == 0)
\r
132 // throw new InternalCompilerError();
\r
135 MultiFunction mFun;
\r
137 mFun = Types.matchFunction(getFunction().getType(), getParameters().length);
\r
138 } catch (MatchException e) {
\r
139 context.setErrorMarker(this);
\r
140 throw new InternalCompilerError();
\r
142 for(int i=0;i<getParameters().length;++i)
\r
143 context.assertSubsumes(this, getParameters()[i].getType(), mFun.parameterTypes[i]);
\r
144 context.assertSubsumes(this, target.getType(), mFun.returnType);
\r
145 context.assertEqualsEffect(this, effect, mFun.effect);
\r
149 public void destroy() {
\r
150 getFunction().remove();
\r
151 for(ValRef parameter : getParameters())
\r
152 parameter.remove();
\r
156 public void simplify(SSASimplificationContext context) {
\r
157 if(target.hasNoOccurences() && !hasEffect()) {
\r
159 context.markModified("LetApply.dead-let-statement");
\r
162 // TODO this is quite heavy way for inlining constants
\r
163 for(int i=0;i<parameters.length;++i) {
\r
164 ValRef parameter = parameters[i];
\r
165 Val value = parameter.getBinding();
\r
166 if(!(value instanceof SCLConstant))
\r
168 SCLConstant constant = (SCLConstant)value;
\r
169 if(constant.inlineArity != 0)
\r
171 SSAFunction definition = constant.definition;
\r
172 SSABlock block = definition.getFirstBlock();
\r
173 if(block.getFirstStatement() != null || !(block.getExit() instanceof Jump))
\r
175 Jump jump = (Jump)block.getExit();
\r
176 if(jump.getTarget().getBinding() != definition.getReturnCont())
\r
178 if(jump.getParameter(0).getTypeParameters().length > 0)
\r
180 parameter.replaceBy(jump.getParameter(0).getBinding());
\r
182 Val functionVal = getFunction().getBinding();
\r
183 if(functionVal instanceof BoundVar) {
\r
184 BoundVarBinder parent_ = ((BoundVar)functionVal).parent;
\r
185 if(parent_ instanceof SSAFunction) {
\r
186 SSAFunction function = (SSAFunction)parent_;
\r
187 if(functionVal.hasMoreThanOneOccurences())
\r
189 if(getParameters().length < function.getArity())
\r
191 if(getParameters().length > function.getArity())
\r
192 split(function.getArity());
\r
195 context.markModified("LetApply.beta-lambda");
\r
197 else if(parent_ instanceof LetApply) {
\r
198 LetApply apply = (LetApply)parent_;
\r
199 if(apply.hasEffect())
\r
201 boolean hasJustOneOccurence = !functionVal.hasMoreThanOneOccurences();
\r
202 if((hasJustOneOccurence && apply.getParent() == getParent()) ||
\r
203 apply.isPartial()) {
\r
204 if(hasJustOneOccurence) {
\r
206 setFunction(apply.getFunction());
\r
207 setParameters(ValRef.concat(apply.getParameters(), getParameters()));
\r
210 setFunction(apply.getFunction().copy());
\r
211 setParameters(ValRef.concat(ValRef.copy(apply.getParameters()), getParameters()));
\r
213 context.markModified("LetApply.merge-applications");
\r
216 else if(parent_ instanceof SSABlock) {
\r
217 SSABlock parent = getParent();
\r
218 if(parent_ != parent)
\r
220 if(parent.getFirstStatement() != this)
\r
222 if(!parent.hasMoreThanOneOccurences())
\r
223 return; // We stop here, because situation can be handled by better transformations
\r
224 if(functionVal.hasMoreThanOneOccurences())
\r
226 // We have now the following situation:
\r
229 // * this application is the only reference to f
\r
230 // * there are multiple references to [c]
\r
231 for(ContRef ref = parent.getOccurrence();ref != null; ref = ref.getNext())
\r
232 if(!(ref.getParent() instanceof Jump))
\r
235 // Finds the position of the functionVal in the parameter list of
\r
236 // the parent block.
\r
238 for(position=0;position<parent.getParameters().length;++position)
\r
239 if(parent.getParameters()[position] == functionVal)
\r
241 if(position == parent.getParameters().length)
\r
242 throw new InternalCompilerError();
\r
244 // Do tranformation
\r
245 for(ContRef ref = parent.getOccurrence();ref != null; ref = ref.getNext()) {
\r
246 Jump jump = (Jump)ref.getParent();
\r
247 SSABlock block = jump.getParent();
\r
249 BoundVar newTarget = new BoundVar(target.getType());
\r
250 block.addStatement(new LetApply(newTarget, effect, jump.getParameter(position), ValRef.copy(parameters)));
\r
251 jump.setParameter(position, newTarget.createOccurrence());
\r
254 parent.setParameter(position, target);
\r
256 context.markModified("LetApply.hoist-apply");
\r
259 else if(functionVal instanceof Constant) {
\r
260 ((Constant)functionVal).inline(context, this);
\r
264 public boolean isPartial() {
\r
265 return parameters.length < function.getBinding().getEffectiveArity();
\r
269 * Removes apply if it does not have parameters.
\r
271 public void removeDegenerated() {
\r
272 if(getParameters().length == 0) {
\r
273 target.replaceBy(getFunction());
\r
274 getFunction().remove();
\r
279 public boolean determineGenerateOnFly() {
\r
282 ValRef ref = target.getOccurrence();
\r
283 if(ref == null || ref.getNext() != null)
\r
285 Object parent = ref.getParent();
\r
286 if(parent instanceof SSAStatement) {
\r
287 if(((SSAStatement)parent).getParent() != getParent())
\r
290 else if(parent instanceof SSAExit) {
\r
291 if(((SSAExit)parent).getParent() != getParent())
\r
293 if(parent instanceof Switch)
\r
302 public void markGenerateOnFly() {
\r
303 target.generateOnFly = determineGenerateOnFly();
\r
306 public ValRef getFunction() {
\r
310 public void setFunction(ValRef function) {
\r
311 this.function = function;
\r
312 function.setParent(this);
\r
315 public ValRef[] getParameters() {
\r
319 public void setParameters(ValRef[] parameters) {
\r
320 /*if(SCLCompilerConfiguration.DEBUG)
\r
321 if(parameters.length == 0)
\r
322 throw new InternalCompilerError();*/
\r
323 this.parameters = parameters;
\r
324 for(ValRef parameter : parameters)
\r
325 parameter.setParent(this);
\r
329 public SSAStatement copy(CopyContext context) {
\r
330 LetApply result = new LetApply(context.copy(target),
\r
331 context.copyType(effect),
\r
332 context.copy(function),
\r
333 context.copy(parameters));
\r
338 public void replace(TVar[] vars, Type[] replacements) {
\r
339 target.replace(vars, replacements);
\r
340 function.replace(vars, replacements);
\r
341 effect = effect.replace(vars, replacements);
\r
342 for(ValRef parameter : parameters)
\r
343 parameter.replace(vars, replacements);
\r
347 * Inlines the application by the given function.
\r
348 * It is assumed that the function has the same number
\r
349 * of parameters as this one and there are no other
\r
350 * references to the function (copy function before
\r
351 * inlining if necessary).
\r
353 public void inline(SSAFunction function) {
\r
354 if(function.getArity() != parameters.length)
\r
355 throw new InternalCompilerError();
\r
357 SSABlock headBlock = getParent();
\r
358 SSAFunction thisFunction = headBlock.getParent();
\r
360 /*System.out.println("--- INLINING -------------------------------");
\r
361 System.out.println(thisFunction);
\r
362 System.out.println("Function name: " + getFunction().getBinding());
\r
363 System.out.println("++++++++++++++++++++++++++++++++++++++++++++");
\r
364 System.out.println(function);
\r
367 if(this.function.getTypeParameters().length > 0) {
\r
368 /*if(function.getParent() != null) {
\r
369 PrintingContext pc = new PrintingContext();
\r
370 pc.append("----------------------------\n");
\r
371 function.getParent().getParentFunction().toString(pc);
\r
372 pc.append("\n----\n");
\r
373 function.toString(pc);
\r
375 pc.append(function.getTypeParameters());
\r
377 pc.append(this.function.getTypeParameters());
\r
378 System.out.println(pc.toString());
\r
380 function.replace(function.getTypeParameters(), this.function.getTypeParameters());
\r
383 if(getPrev() != null)
\r
384 getPrev().setAsLastStatement();
\r
386 headBlock.removeStatements();
\r
388 // Create tail block
\r
389 SSABlock tailBlock = new SSABlock(new BoundVar[] {target});
\r
390 thisFunction.addBlock(tailBlock);
\r
392 SSAStatement stat = getNext();
\r
393 while(stat != null) {
\r
394 SSAStatement temp = stat.getNext();
\r
395 tailBlock.addStatement(stat);
\r
399 tailBlock.setExit(headBlock.getExit());
\r
402 thisFunction.mergeBlocks(function);
\r
404 headBlock.setExit(new Jump(function.getFirstBlock().createOccurrence(),
\r
406 function.getReturnCont().replaceWith(tailBlock);
\r
408 this.function.remove();
\r
409 // Note: No need to remove or detach this statement anymore
\r
411 // TODO remove function
\r
413 System.out.println("============================================");
\r
414 System.out.println(thisFunction);
\r
419 public void collectFreeVariables(SSAFunction parentFunction,
\r
420 ArrayList<ValRef> vars) {
\r
421 function.collectFreeVariables(parentFunction, vars);
\r
422 for(ValRef parameter : parameters)
\r
423 parameter.collectFreeVariables(parentFunction, vars);
\r
427 public void replaceByApply(ValRef valRef, Val newFunction, Type[] typeParameters, Val[] parameters) {
\r
428 if(function == valRef) {
\r
430 setFunction(newFunction.createOccurrence(typeParameters));
\r
431 setParameters(ValRef.concat(ValRef.createOccurrences(parameters), this.parameters));
\r
434 super.replaceByApply(valRef, newFunction, typeParameters, parameters);
\r
438 * Splits this application into two applications where the first has
\r
439 * the arity given as a parameter and the new application inserted
\r
440 * after this statement has the rest of the parameters.
\r
442 public void split(int arity) {
\r
443 if(arity == parameters.length)
\r
445 if(arity > parameters.length)
\r
446 throw new InternalCompilerError();
\r
447 ValRef[] firstHalf = arity == 0 ? ValRef.EMPTY_ARRAY : Arrays.copyOf(parameters, arity);
\r
448 ValRef[] secondHalf = arity == parameters.length ? ValRef.EMPTY_ARRAY : Arrays.copyOfRange(parameters, arity, parameters.length);
\r
451 MultiFunction mfun = Types.matchFunction(function.getType(), arity);
\r
452 newVar = new BoundVar(mfun.returnType);
\r
453 } catch (MatchException e) {
\r
454 throw new InternalCompilerError();
\r
456 LetApply newApply = new LetApply(target, effect,
\r
457 newVar.createOccurrence(), secondHalf);
\r
458 newApply.insertAfter(this);
\r
459 effect = Types.NO_EFFECTS;
\r
461 setParameters(firstHalf);
\r
465 * True, if the application may have side effects.
\r
467 public boolean hasEffect() {
\r
468 return effect != Types.NO_EFFECTS;
\r
471 public void updateEffect() {
\r
473 MultiFunction mFun = Types.matchFunction(function.getType(), parameters.length);
\r
474 this.effect = mFun.effect;
\r
475 } catch (MatchException e) {
\r
476 throw new InternalCompilerError(e);
\r
481 public void prepare(MethodBuilder mb) {
\r
482 function.getBinding().prepare(mb);
\r
486 public void forValRefs(ValRefVisitor visitor) {
\r
487 visitor.visit(function);
\r
488 for(ValRef parameter : parameters)
\r
489 visitor.visit(parameter);
\r