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.internal.codegen.continuations.ContRef;
\r
9 import org.simantics.scl.compiler.internal.codegen.references.BoundVar;
\r
10 import org.simantics.scl.compiler.internal.codegen.references.Val;
\r
11 import org.simantics.scl.compiler.internal.codegen.references.ValRef;
\r
12 import org.simantics.scl.compiler.internal.codegen.ssa.SSABlock;
\r
13 import org.simantics.scl.compiler.internal.codegen.ssa.SSAExit;
\r
14 import org.simantics.scl.compiler.internal.codegen.ssa.SSAFunction;
\r
15 import org.simantics.scl.compiler.internal.codegen.ssa.SSAStatement;
\r
16 import org.simantics.scl.compiler.internal.codegen.ssa.binders.BoundVarBinder;
\r
17 import org.simantics.scl.compiler.internal.codegen.ssa.binders.ValRefBinder;
\r
18 import org.simantics.scl.compiler.internal.codegen.ssa.exits.Jump;
\r
19 import org.simantics.scl.compiler.internal.codegen.ssa.exits.Switch;
\r
20 import org.simantics.scl.compiler.internal.codegen.utils.CopyContext;
\r
21 import org.simantics.scl.compiler.internal.codegen.utils.MethodBuilder;
\r
22 import org.simantics.scl.compiler.internal.codegen.utils.PrintingContext;
\r
23 import org.simantics.scl.compiler.internal.codegen.utils.SSASimplificationContext;
\r
24 import org.simantics.scl.compiler.internal.codegen.utils.SSAValidationContext;
\r
25 import org.simantics.scl.compiler.top.SCLCompilerConfiguration;
\r
26 import org.simantics.scl.compiler.types.TVar;
\r
27 import org.simantics.scl.compiler.types.Type;
\r
28 import org.simantics.scl.compiler.types.Types;
\r
29 import org.simantics.scl.compiler.types.exceptions.MatchException;
\r
30 import org.simantics.scl.compiler.types.util.MultiFunction;
\r
31 import org.simantics.scl.compiler.types.util.TypeUnparsingContext;
\r
33 public class LetApply extends LetStatement implements ValRefBinder {
\r
34 private ValRef function;
\r
35 private ValRef[] parameters;
\r
38 public LetApply(BoundVar target, Type effect, ValRef function, ValRef ... parameters) {
\r
40 if(SCLCompilerConfiguration.DEBUG) {
\r
42 throw new InternalCompilerError();
\r
43 if(function.getBinding() == null)
\r
44 throw new InternalCompilerError();
\r
46 this.setFunction(function);
\r
47 this.setParameters(parameters);
\r
48 this.effect = Types.canonical(effect);
\r
51 public void push(MethodBuilder mb) {
\r
52 //mb.getCodeBuilder().mapLineNumber(lineNumber);
\r
53 Val f = getFunction().getBinding();
\r
54 Val[] ps = ValRef.getBindings(getParameters());
\r
55 if(f instanceof Constant) {
\r
56 Constant cf = (Constant)f;
\r
57 Type returnType = cf.apply(mb, getFunction().getTypeParameters(), ps);
\r
58 if(Types.isBoxed(returnType))
\r
59 mb.unbox(target.getType());
\r
62 mb.push(f, f.getType());
\r
64 mb.genericApply(ps.length);
\r
65 mb.unbox(target.getType());
\r
70 public void generateCode(MethodBuilder mb) {
\r
71 if(!target.generateOnFly) {
\r
78 public void toString(PrintingContext context) {
\r
79 if(determineGenerateOnFly())
\r
80 context.addInlineExpression(target, this);
\r
82 toStringAux(context);
\r
85 private void toStringAux(PrintingContext context) {
\r
86 context.indentation();
\r
87 context.append(target);
\r
88 context.append("(" + target.occurrenceCount() + ")");
\r
89 context.append(" = ");
\r
90 bodyToString(context);
\r
91 context.append('\n');
\r
95 public void bodyToString(PrintingContext context) {
\r
96 if(context.getErrorMarker() == this)
\r
97 context.append("!> ");
\r
99 context.append("<");
\r
100 context.append(effect);
\r
101 context.append("> ");
\r
103 context.append(getFunction());
\r
104 for(ValRef parameter : getParameters()) {
\r
105 context.append(' ');
\r
106 context.append(parameter);
\r
111 public String toString() {
\r
112 PrintingContext context = new PrintingContext();
\r
113 toStringAux(context);
\r
114 return context.toString();
\r
118 public void validate(SSAValidationContext context) {
\r
119 context.validate(target);
\r
120 if(target.getParent() != this)
\r
121 throw new InternalCompilerError();
\r
122 context.validate(function);
\r
123 if(function.getParent() != this)
\r
124 throw new InternalCompilerError();
\r
125 for(ValRef parameter : parameters) {
\r
126 context.validate(parameter);
\r
127 if(parameter.getParent() != this)
\r
128 throw new InternalCompilerError();
\r
130 //if(parameters.length == 0)
\r
131 // throw new InternalCompilerError();
\r
134 MultiFunction mFun;
\r
136 mFun = Types.matchFunction(getFunction().getType(), getParameters().length);
\r
137 } catch (MatchException e) {
\r
138 context.setErrorMarker(this);
\r
139 throw new InternalCompilerError();
\r
141 for(int i=0;i<getParameters().length;++i)
\r
142 context.assertSubsumes(this, getParameters()[i].getType(), mFun.parameterTypes[i]);
\r
143 context.assertSubsumes(this, target.getType(), mFun.returnType);
\r
144 context.assertEqualsEffect(this, effect, mFun.effect);
\r
148 public void destroy() {
\r
149 getFunction().remove();
\r
150 for(ValRef parameter : getParameters())
\r
151 parameter.remove();
\r
155 public void simplify(SSASimplificationContext context) {
\r
156 if(target.hasNoOccurences() && !hasEffect()) {
\r
158 context.markModified("LetApply.dead-let-statement");
\r
161 Val functionVal = getFunction().getBinding();
\r
162 if(functionVal instanceof BoundVar) {
\r
163 BoundVarBinder parent_ = ((BoundVar)functionVal).parent;
\r
164 if(parent_ instanceof SSAFunction) {
\r
165 SSAFunction function = (SSAFunction)parent_;
\r
166 if(functionVal.hasMoreThanOneOccurences())
\r
168 if(getParameters().length < function.getArity())
\r
170 if(getParameters().length > function.getArity())
\r
171 split(function.getArity());
\r
174 context.markModified("LetApply.beta-lambda");
\r
176 else if(parent_ instanceof LetApply) {
\r
177 LetApply apply = (LetApply)parent_;
\r
178 if(apply.hasEffect())
\r
180 boolean hasJustOneOccurence = !functionVal.hasMoreThanOneOccurences();
\r
181 if((hasJustOneOccurence && apply.getParent() == getParent()) ||
\r
182 apply.isPartial()) {
\r
183 if(hasJustOneOccurence) {
\r
185 setFunction(apply.getFunction());
\r
186 setParameters(ValRef.concat(apply.getParameters(), getParameters()));
\r
189 setFunction(apply.getFunction().copy());
\r
190 setParameters(ValRef.concat(ValRef.copy(apply.getParameters()), getParameters()));
\r
192 context.markModified("LetApply.merge-applications");
\r
195 else if(parent_ instanceof SSABlock) {
\r
196 SSABlock parent = getParent();
\r
197 if(parent_ != parent)
\r
199 if(parent.getFirstStatement() != this)
\r
201 if(!parent.hasMoreThanOneOccurences())
\r
202 return; // We stop here, because situation can be handled by better transformations
\r
203 if(functionVal.hasMoreThanOneOccurences())
\r
205 // We have now the following situation:
\r
208 // * this application is the only reference to f
\r
209 // * there are multiple references to [c]
\r
210 for(ContRef ref = parent.getOccurrence();ref != null; ref = ref.getNext())
\r
211 if(!(ref.getParent() instanceof Jump))
\r
214 // Finds the position of the functionVal in the parameter list of
\r
215 // the parent block.
\r
217 for(position=0;position<parent.getParameters().length;++position)
\r
218 if(parent.getParameters()[position] == functionVal)
\r
220 if(position == parent.getParameters().length)
\r
221 throw new InternalCompilerError();
\r
223 // Do tranformation
\r
224 for(ContRef ref = parent.getOccurrence();ref != null; ref = ref.getNext()) {
\r
225 Jump jump = (Jump)ref.getParent();
\r
226 SSABlock block = jump.getParent();
\r
228 BoundVar newTarget = new BoundVar(target.getType());
\r
229 block.addStatement(new LetApply(newTarget, effect, jump.getParameter(position), ValRef.copy(parameters)));
\r
230 jump.setParameter(position, newTarget.createOccurrence());
\r
233 parent.setParameter(position, target);
\r
235 context.markModified("LetApply.hoist-apply");
\r
238 else if(functionVal instanceof Constant) {
\r
239 ((Constant)functionVal).inline(context, this);
\r
243 public boolean isPartial() {
\r
244 return parameters.length < function.getBinding().getEffectiveArity();
\r
248 * Removes apply if it does not have parameters.
\r
250 public void removeDegenerated() {
\r
251 if(getParameters().length == 0) {
\r
252 target.replaceBy(getFunction());
\r
253 getFunction().remove();
\r
258 public boolean determineGenerateOnFly() {
\r
261 ValRef ref = target.getOccurrence();
\r
262 if(ref == null || ref.getNext() != null)
\r
264 Object parent = ref.getParent();
\r
265 if(parent instanceof SSAStatement) {
\r
266 if(((SSAStatement)parent).getParent() != getParent())
\r
269 else if(parent instanceof SSAExit) {
\r
270 if(((SSAExit)parent).getParent() != getParent())
\r
272 if(parent instanceof Switch)
\r
281 public void markGenerateOnFly() {
\r
282 target.generateOnFly = determineGenerateOnFly();
\r
285 public ValRef getFunction() {
\r
289 public void setFunction(ValRef function) {
\r
290 this.function = function;
\r
291 function.setParent(this);
\r
294 public ValRef[] getParameters() {
\r
298 public void setParameters(ValRef[] parameters) {
\r
299 /*if(SCLCompilerConfiguration.DEBUG)
\r
300 if(parameters.length == 0)
\r
301 throw new InternalCompilerError();*/
\r
302 this.parameters = parameters;
\r
303 for(ValRef parameter : parameters)
\r
304 parameter.setParent(this);
\r
308 public SSAStatement copy(CopyContext context) {
\r
309 LetApply result = new LetApply(context.copy(target),
\r
310 context.copyType(effect),
\r
311 context.copy(function),
\r
312 context.copy(parameters));
\r
317 public void replace(TVar[] vars, Type[] replacements) {
\r
318 target.replace(vars, replacements);
\r
319 function.replace(vars, replacements);
\r
320 effect = effect.replace(vars, replacements);
\r
321 for(ValRef parameter : parameters)
\r
322 parameter.replace(vars, replacements);
\r
326 * Inlines the application by the given function.
\r
327 * It is assumed that the function has the same number
\r
328 * of parameters as this one and there are no other
\r
329 * references to the function (copy function before
\r
330 * inlining if necessary).
\r
332 public void inline(SSAFunction function) {
\r
333 if(function.getArity() != parameters.length)
\r
334 throw new InternalCompilerError();
\r
336 SSABlock headBlock = getParent();
\r
337 SSAFunction thisFunction = headBlock.getParent();
\r
339 /*System.out.println("--- INLINING -------------------------------");
\r
340 System.out.println(thisFunction);
\r
341 System.out.println("Function name: " + getFunction().getBinding());
\r
342 System.out.println("++++++++++++++++++++++++++++++++++++++++++++");
\r
343 System.out.println(function);
\r
346 if(this.function.getTypeParameters().length > 0) {
\r
347 /*if(function.getParent() != null) {
\r
348 PrintingContext pc = new PrintingContext();
\r
349 pc.append("----------------------------\n");
\r
350 function.getParent().getParentFunction().toString(pc);
\r
351 pc.append("\n----\n");
\r
352 function.toString(pc);
\r
354 pc.append(function.getTypeParameters());
\r
356 pc.append(this.function.getTypeParameters());
\r
357 System.out.println(pc.toString());
\r
359 function.replace(function.getTypeParameters(), this.function.getTypeParameters());
\r
362 if(getPrev() != null)
\r
363 getPrev().setAsLastStatement();
\r
365 headBlock.removeStatements();
\r
367 // Create tail block
\r
368 SSABlock tailBlock = new SSABlock(new BoundVar[] {target});
\r
369 thisFunction.addBlock(tailBlock);
\r
371 SSAStatement stat = getNext();
\r
372 while(stat != null) {
\r
373 SSAStatement temp = stat.getNext();
\r
374 tailBlock.addStatement(stat);
\r
378 tailBlock.setExit(headBlock.getExit());
\r
381 thisFunction.mergeBlocks(function);
\r
383 headBlock.setExit(new Jump(function.getFirstBlock().createOccurrence(),
\r
385 function.getReturnCont().replaceWith(tailBlock);
\r
387 this.function.remove();
\r
388 // Note: No need to remove or detach this statement anymore
\r
390 // TODO remove function
\r
392 System.out.println("============================================");
\r
393 System.out.println(thisFunction);
\r
398 public void collectFreeVariables(SSAFunction parentFunction,
\r
399 ArrayList<ValRef> vars) {
\r
400 function.collectFreeVariables(parentFunction, vars);
\r
401 for(ValRef parameter : parameters)
\r
402 parameter.collectFreeVariables(parentFunction, vars);
\r
406 public void replaceByApply(ValRef valRef, Val newFunction, Type[] typeParameters, Val[] parameters) {
\r
407 if(function == valRef) {
\r
409 setFunction(newFunction.createOccurrence(typeParameters));
\r
410 setParameters(ValRef.concat(ValRef.createOccurrences(parameters), this.parameters));
\r
413 super.replaceByApply(valRef, newFunction, typeParameters, parameters);
\r
417 * Splits this application into two applications where the first has
\r
418 * the arity given as a parameter and the new application inserted
\r
419 * after this statement has the rest of the parameters.
\r
421 public void split(int arity) {
\r
422 if(arity == parameters.length)
\r
424 if(arity > parameters.length)
\r
425 throw new InternalCompilerError();
\r
426 ValRef[] firstHalf = arity == 0 ? ValRef.EMPTY_ARRAY : Arrays.copyOf(parameters, arity);
\r
427 ValRef[] secondHalf = arity == parameters.length ? ValRef.EMPTY_ARRAY : Arrays.copyOfRange(parameters, arity, parameters.length);
\r
430 MultiFunction mfun = Types.matchFunction(function.getType(), arity);
\r
431 newVar = new BoundVar(mfun.returnType);
\r
432 } catch (MatchException e) {
\r
433 throw new InternalCompilerError();
\r
435 LetApply newApply = new LetApply(target, effect,
\r
436 newVar.createOccurrence(), secondHalf);
\r
437 newApply.insertAfter(this);
\r
438 effect = Types.NO_EFFECTS;
\r
440 setParameters(firstHalf);
\r
444 * True, if the application may have side effects.
\r
446 public boolean hasEffect() {
\r
447 return effect != Types.NO_EFFECTS;
\r
450 public void updateEffect() {
\r
452 MultiFunction mFun = Types.matchFunction(function.getType(), parameters.length);
\r
453 this.effect = mFun.effect;
\r
454 } catch (MatchException e) {
\r
455 throw new InternalCompilerError(e);
\r
460 public void prepare(MethodBuilder mb) {
\r
461 function.getBinding().prepare(mb);
\r