1 package org.simantics.scl.compiler.internal.codegen.ssa;
3 import java.util.ArrayList;
5 import org.simantics.scl.compiler.common.exceptions.InternalCompilerError;
6 import org.simantics.scl.compiler.constants.Constant;
7 import org.simantics.scl.compiler.constants.NoRepConstant;
8 import org.simantics.scl.compiler.constants.SCLConstant;
9 import org.simantics.scl.compiler.internal.codegen.continuations.BranchRef;
10 import org.simantics.scl.compiler.internal.codegen.continuations.Cont;
11 import org.simantics.scl.compiler.internal.codegen.continuations.ContRef;
12 import org.simantics.scl.compiler.internal.codegen.references.BoundVar;
13 import org.simantics.scl.compiler.internal.codegen.references.Val;
14 import org.simantics.scl.compiler.internal.codegen.references.ValRef;
15 import org.simantics.scl.compiler.internal.codegen.ssa.binders.BoundVarBinder;
16 import org.simantics.scl.compiler.internal.codegen.ssa.exits.Jump;
17 import org.simantics.scl.compiler.internal.codegen.ssa.exits.Switch;
18 import org.simantics.scl.compiler.internal.codegen.ssa.statements.LetApply;
19 import org.simantics.scl.compiler.internal.codegen.utils.CopyContext;
20 import org.simantics.scl.compiler.internal.codegen.utils.MethodBuilder;
21 import org.simantics.scl.compiler.internal.codegen.utils.Printable;
22 import org.simantics.scl.compiler.internal.codegen.utils.PrintingContext;
23 import org.simantics.scl.compiler.internal.codegen.utils.SSALambdaLiftingContext;
24 import org.simantics.scl.compiler.internal.codegen.utils.SSASimplificationContext;
25 import org.simantics.scl.compiler.internal.codegen.utils.SSAUtils;
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;
33 public final class SSABlock extends Cont implements Printable, BoundVarBinder {
34 public static final SSABlock[] EMPTY_ARRAY = new SSABlock[0];
36 BoundVar[] parameters;
41 SSAStatement firstStatement;
42 SSAStatement lastStatement;
45 public SSABlock(Type ... parameterTypes) {
46 parameters = new BoundVar[parameterTypes.length];
47 for(int i=0;i<parameterTypes.length;++i) {
48 BoundVar parameter = new BoundVar(parameterTypes[i]);
49 parameters[i] = parameter;
50 parameter.parent = this;
54 public SSABlock(BoundVar[] parameters) {
55 this.parameters = parameters;
56 for(BoundVar parameter : parameters)
57 parameter.parent = this;
60 public SSAFunction getParent() {
64 public SSABlock getNext() {
68 public SSABlock getPrev() {
72 public SSAExit getExit() {
76 public void removeStatements() {
77 this.firstStatement = null;
78 this.lastStatement = null;
82 public int getArity() {
83 return parameters.length;
86 public int getStatementCount() {
88 for(SSAStatement stat = firstStatement;stat != null;stat = stat.next)
94 public Type getParameterType(int parameterId) {
95 return parameters[parameterId].getType();
98 public Type[] getParameterTypes() {
99 return Types.getTypes(parameters);
102 public BoundVar[] getParameters() {
106 public void setExit(SSAExit exit) {
111 public void detach() {
113 parent.firstBlock = next;
117 parent.lastBlock = prev;
120 if(parent.firstBlock == null)
121 throw new InternalCompilerError();
124 public void remove() {
129 public void destroy() {
130 for(SSAStatement statement = firstStatement; statement != null; statement = statement.next)
135 public void addStatement(SSAStatement stat) {
136 if(SCLCompilerConfiguration.DEBUG) {
137 if((firstStatement == null) != (lastStatement == null))
138 throw new InternalCompilerError();
141 if(lastStatement == null) {
142 firstStatement = lastStatement = stat;
143 stat.next = stat.prev = null;
146 lastStatement.next = stat;
147 stat.prev = lastStatement;
149 lastStatement = stat;
153 public void generateCode(MethodBuilder mb) {
154 mb.setLocation(this);
155 for(SSAStatement stat = firstStatement; stat != null; stat = stat.next)
156 stat.generateCode(mb);
157 exit.generateCode(mb);
160 public void toString(PrintingContext context) {
161 context.indentation();
162 context.append(this);
163 context.append("(" + occurrenceCount() + ")");
164 parametersToString(context);
165 context.append(" =\n");
166 bodyToString(context);
169 public void parametersToString(PrintingContext context) {
170 for(BoundVar parameter : parameters) {
172 if(parameter.hasNoOccurences()) {
175 context.append(" :: ");
176 context.append(parameter.getType());
181 context.append(parameter);
182 context.append(" :: ");
183 context.append(parameter.getType());
189 public void bodyToString(PrintingContext context) {
191 for(SSAStatement statement = firstStatement; statement != null; statement = statement.next)
192 statement.toString(context);
193 context.indentation();
194 exit.toString(context);
198 public void validate(SSAValidationContext context) {
199 if(exit.getParent() != this)
200 throw new InternalCompilerError();
201 for(BoundVar parameter : parameters) {
202 context.validate(parameter);
203 if(parameter.parent != this)
204 throw new InternalCompilerError();
206 for(SSAStatement statement = firstStatement; statement != null; statement = statement.next) {
207 if(statement.getParent() != this)
208 throw new InternalCompilerError();
209 statement.validate(context);
211 exit.validate(context);
214 SSAStatement last = firstStatement;
216 while(last.next != null)
219 if(last != lastStatement)
220 throw new InternalCompilerError();
224 public void simplify(SSASimplificationContext context) {
225 if(hasNoOccurences() && parent.firstBlock != this) {
227 context.markModified("dead-block");
231 tryToImproveParameters(context);
233 // Simplify statements and exit
234 for(SSAStatement statement = firstStatement; statement != null; statement = statement.next)
235 statement.simplify(context);
236 exit.simplify(context);
238 // Simplifications to this block
239 if(exit instanceof Switch) {
240 if(simplifySwitch()) {
241 context.markModified("beta-switch");
244 if(exit instanceof Jump) {
245 if(firstStatement == null && parent.firstBlock != this) {
246 if(etaBlock(context)) {
247 context.markModified("eta-block");
250 else if(inlineJump()) {
251 context.markModified("beta-block");
256 if(optimizeTailSelfCall()) {
257 context.markModified("simplify-tail-call");
260 else if(inlineJump()) {
261 context.markModified("beta-block");
268 private void tryToImproveParameters(SSASimplificationContext context) {
269 if(parent.firstBlock == this)
271 if(parameters.length == 0)
273 for(ContRef ref = getOccurrence(); ref != null; ref = ref.getNext())
274 if(!(ref.getParent() instanceof Jump))
276 boolean modified = false;
277 for(int i=0;i<parameters.length;++i)
278 if(tryToImproveParameter(i)) {
283 context.markModified("improve-parameters");
286 private static Constant getOnlyPossibleValue(Type type) {
287 type = Types.canonical(type);
288 if(type == Types.UNIT)
289 return NoRepConstant.UNIT;
290 else if(type == Types.PUNIT)
291 return NoRepConstant.PUNIT;
295 private boolean tryToImproveParameter(int position) {
296 BoundVar parameter = parameters[position];
297 Constant onlyPossibleValue = getOnlyPossibleValue(parameter.getType());
298 if(onlyPossibleValue == null) {
300 ValRef constantRef = null;
301 for(ContRef ref = getOccurrence(); ref != null; ref = ref.getNext()) {
302 Jump jump = (Jump)ref.getParent();
303 ValRef valRef = jump.getParameters()[position];
304 Val val = valRef.getBinding();
307 if(constant == null) {
309 constantRef = valRef;
316 return false; // This is a strange case, because we cannot get the parameter anywhere
317 parameter.replaceBy(constantRef);
320 parameter.replaceBy(onlyPossibleValue);
323 for(ContRef ref = getOccurrence(); ref != null; ref = ref.getNext()) {
324 Jump jump = (Jump)ref.getParent();
325 jump.setParameters(removeAt(jump.getParameters(), position));
328 parameters = removeAt(parameters, position);
332 private static BoundVar[] removeAt(BoundVar[] vars, int pos) {
333 BoundVar[] result = new BoundVar[vars.length-1];
334 for(int i=0;i<pos;++i)
336 for(int i=pos+1;i<vars.length;++i)
337 result[i-1] = vars[i];
341 private static ValRef[] removeAt(ValRef[] vars, int pos) {
342 ValRef[] result = new ValRef[vars.length-1];
343 for(int i=0;i<pos;++i)
346 for(int i=pos+1;i<vars.length;++i)
347 result[i-1] = vars[i];
352 * This method assumes that the exit of the block is Jump.
354 private boolean optimizeTailSelfCall() {
355 // The last statement of the block is LetApply that calls the parent function with right number of parameters
356 if(lastStatement == null || !(lastStatement instanceof LetApply))
358 LetApply apply = (LetApply)lastStatement;
359 Val function = apply.getFunction().getBinding();
360 if(function != parent.target)
362 SSABlock initialBlock = parent.firstBlock;
363 if(initialBlock.parameters.length != apply.getParameters().length)
366 // The jump is a return (with one parameter)
367 // The parameter of the jump is the target of LetApply
368 Jump jump = (Jump)exit;
369 Cont targetCont = jump.getTarget().getBinding();
370 if(targetCont != parent.returnCont) {
371 SSABlock targetBlock = (SSABlock)targetCont;
372 if(targetBlock.firstStatement != null)
374 if(!(targetBlock.exit instanceof Jump))
376 Jump targetJump = (Jump)targetBlock.exit;
377 if(targetJump.getTarget().getBinding() != parent.returnCont)
379 if(targetJump.getParameters().length != 1)
382 BoundVar applyTarget = apply.getTarget();
383 ValRef targetJumpParameter = targetJump.getParameter(0);
384 isSameParam: if(!SSAUtils.representSameValue(applyTarget, targetJumpParameter)) {
385 BoundVar[] targetBlockParameters = targetBlock.getParameters();
386 for(int i=0;i<targetBlockParameters.length;++i) {
387 if(targetJumpParameter.getBinding() == targetBlockParameters[i]
388 && jump.getParameter(i).getBinding() == applyTarget)
395 if(jump.getParameters().length != 1)
397 if(!SSAUtils.representSameValue(apply.getTarget(), jump.getParameter(0)))
403 apply.getFunction().remove();
404 jump.getTarget().remove();
405 jump.setTarget(initialBlock.createOccurrence());
406 for(ValRef parameter : jump.getParameters())
408 jump.setParameters(apply.getParameters());
414 * Assumes that this block has no statements, the block is not the first block
415 * and the exit of the block is Jump.
417 private boolean etaBlock(SSASimplificationContext context) {
418 Jump jump = (Jump)exit;
419 if(parameters.length != jump.getParameters().length)
421 for(int i=0;i<parameters.length;++i)
422 if(parameters[i] != jump.getParameters()[i].getBinding() ||
423 parameters[i].hasMoreThanOneOccurences())
426 replaceWith(jump.getTarget().getBinding());
431 private boolean simplifySwitch() {
432 Switch sw = (Switch)exit;
433 ValRef scrutineeRef = sw.getScrutinee();
434 Val scrutinee = scrutineeRef.getBinding();
435 if(scrutinee instanceof BoundVar) {
436 BoundVarBinder parent = ((BoundVar)scrutinee).parent;
437 if(!(parent instanceof LetApply))
439 LetApply apply = (LetApply)parent;
440 Val function = apply.getFunction().getBinding();
441 if(!(function instanceof Constant) || function instanceof SCLConstant)
443 for(BranchRef branch : sw.getBranches()) {
444 if(branch.constructor == function) {
446 setExit(new Jump(branch.cont.getBinding().createOccurrence(),
447 ValRef.copy(apply.getParameters())));
452 else if(scrutinee instanceof Constant) {
453 if(sw.getBranches().length == 1) {
454 BranchRef branch = sw.getBranches()[0];
455 if(branch.constructor == scrutinee) {
457 * Optimizes for example
464 setExit(new Jump(branch.cont.getBinding().createOccurrence()));
471 private boolean inlineJump() {
472 Jump jump = (Jump)exit;
473 Cont target = jump.getTarget().getBinding();
474 if(!(target instanceof SSABlock))
476 if(target.hasMoreThanOneOccurences())
478 SSABlock block = (SSABlock)target;
479 if(block == parent.firstBlock || block == this)
482 /*System.out.println(">> BEFORE INLINE >>");
483 System.out.println(getParent());
484 System.out.println(">> THIS BLOCK >>");
485 System.out.println(this);
486 System.out.println(">> TARGET BLOCK >>");
487 System.out.println(block);*/
489 mergeStatements(block);
491 for(int i=0;i<jump.getParameters().length;++i)
492 block.parameters[i].replaceBy(jump.getParameters()[i]);
498 /*System.out.println(">> AFTER INLINE >>");
499 System.out.println(getParent());
500 System.out.println(">> THIS BLOCK >>");
501 System.out.println(this);
502 System.out.println(">>>>>>>>>>>>>>>>>>");*/
506 private void mergeStatements(SSABlock block) {
507 if(SCLCompilerConfiguration.DEBUG) {
508 SSAStatement last = firstStatement;
510 while(last.next != null)
513 if(last != lastStatement)
514 throw new InternalCompilerError();
516 SSAStatement stat = block.firstStatement;
517 while(stat != null) {
518 SSAStatement next = stat.next;
525 public String toString() {
526 PrintingContext context = new PrintingContext();
528 return context.toString();
531 public void markGenerateOnFly() {
532 for(SSAStatement stat = firstStatement; stat != null; stat = stat.next)
533 stat.markGenerateOnFly();
536 public SSABlock copy(CopyContext context) {
537 SSABlock newBlock = new SSABlock(context.copy(parameters));
538 context.put(this, newBlock);
539 for(SSAStatement statement = firstStatement;
540 statement != null; statement = statement.next)
541 newBlock.addStatement(statement.copy(context));
542 newBlock.setExit(exit.copy(context));
546 public void setParameters(BoundVar[] parameters) {
547 for(BoundVar parameter : parameters)
548 parameter.parent = this;
549 this.parameters = parameters;
553 public void replace(TVar[] vars, Type[] replacements) {
554 for(BoundVar parameter : parameters)
555 parameter.replace(vars, replacements);
556 for(SSAStatement statement = firstStatement;
557 statement != null; statement = statement.next)
558 statement.replace(vars, replacements);
559 exit.replace(vars, replacements);
562 public void collectFreeVariables(SSAFunction function, ArrayList<ValRef> vars) {
563 for(SSAStatement statement = firstStatement;
564 statement != null; statement = statement.next)
565 statement.collectFreeVariables(function, vars);
566 exit.collectFreeVariables(function, vars);
570 public SSAFunction getParentFunction() {
574 public void lambdaLift(SSALambdaLiftingContext context) {
575 for(SSAStatement statement = firstStatement;
576 statement != null; statement = statement.next)
577 statement.lambdaLift(context);
580 public SSAStatement getFirstStatement() {
581 return firstStatement;
584 public void setParameter(int position, BoundVar target) {
585 parameters[position] = target;
586 target.parent = this;
589 public void prepare(MethodBuilder mb) {
590 for(SSAStatement stat = firstStatement; stat != null; stat = stat.next)
595 public void forValRefs(ValRefVisitor visitor) {
596 for(SSAStatement statement = firstStatement;
597 statement != null; statement = statement.next)
598 statement.forValRefs(visitor);
599 exit.forValRefs(visitor);