package org.simantics.scl.compiler.internal.codegen.ssa.exits; import java.util.ArrayList; import org.objectweb.asm.Label; import org.simantics.scl.compiler.common.exceptions.InternalCompilerError; import org.simantics.scl.compiler.constants.BooleanConstant; import org.simantics.scl.compiler.constants.ComparisonFunction; import org.simantics.scl.compiler.internal.codegen.continuations.Cont; import org.simantics.scl.compiler.internal.codegen.continuations.ContRef; import org.simantics.scl.compiler.internal.codegen.references.BoundVar; import org.simantics.scl.compiler.internal.codegen.references.Val; import org.simantics.scl.compiler.internal.codegen.references.ValRef; import org.simantics.scl.compiler.internal.codegen.ssa.SSABlock; import org.simantics.scl.compiler.internal.codegen.ssa.SSAExit; import org.simantics.scl.compiler.internal.codegen.ssa.SSAFunction; import org.simantics.scl.compiler.internal.codegen.ssa.binders.ValRefBinder; import org.simantics.scl.compiler.internal.codegen.ssa.statements.LetApply; import org.simantics.scl.compiler.internal.codegen.utils.CopyContext; import org.simantics.scl.compiler.internal.codegen.utils.MethodBuilder; import org.simantics.scl.compiler.internal.codegen.utils.PrintingContext; import org.simantics.scl.compiler.internal.codegen.utils.SSASimplificationContext; import org.simantics.scl.compiler.internal.codegen.utils.SSAValidationContext; import org.simantics.scl.compiler.internal.codegen.utils.ValRefVisitor; import org.simantics.scl.compiler.types.TVar; import org.simantics.scl.compiler.types.Type; import org.simantics.scl.compiler.types.Types; public class If extends SSAExit implements ValRefBinder { private ValRef condition; private ContRef thenTarget; private ContRef elseTarget; public If(ValRef condition, ContRef thenTarget, ContRef elseTarget) { setCondition(condition); setThenTarget(thenTarget); setElseTarget(elseTarget); } public void setCondition(ValRef condition) { this.condition = condition; condition.setParent(this); } public void setThenTarget(ContRef thenTarget) { this.thenTarget = thenTarget; thenTarget.setParent(this); } public void setElseTarget(ContRef elseTarget) { this.elseTarget = elseTarget; elseTarget.setParent(this); } @Override public void generateCode(MethodBuilder mb) { Val binding = condition.getBinding(); simplifyTestCode: if(binding instanceof BoundVar) { BoundVar boundVar = (BoundVar)binding; if(!boundVar.generateOnFly) break simplifyTestCode; LetApply apply = (LetApply)boundVar.getParent(); Val function = apply.getFunction().getBinding(); if(!(function instanceof ComparisonFunction)) break simplifyTestCode; Val[] ps = ValRef.getBindings(apply.getParameters()); ((ComparisonFunction)function).generateCondition(mb, ps, thenTarget.getBinding(), elseTarget.getBinding()); return; } mb.push(condition.getBinding(), Types.BOOLEAN); Label elseLabel = mb.getLabel(elseTarget.getBinding()); mb.ifZeroComparisonBranch(elseLabel, "=="); mb.jump(thenTarget.getBinding()); mb.ensureExists(elseTarget.getBinding()); } @Override public void toString(PrintingContext context) { context.append("if "); context.append(condition); context.append(" then "); { Cont thenCont = thenTarget.getBinding(); if(thenCont instanceof SSABlock) { SSABlock thenBlock = (SSABlock)thenCont; if(thenCont.hasMoreThanOneOccurences()) { context.append(thenCont); context.addBlock(thenBlock); context.append(' '); } else { context.append('\n'); thenBlock.bodyToString(context); context.indentation(); } } else { context.append(thenCont); context.append(' '); } } context.append("else "); { Cont elseCont = elseTarget.getBinding(); if(elseCont instanceof SSABlock) { SSABlock elseBlock = (SSABlock)elseCont; if(elseCont.hasMoreThanOneOccurences()) { context.append(elseCont); context.addBlock(elseBlock); context.append('\n'); } else { context.append('\n'); elseBlock.bodyToString(context); } } else { context.append(elseCont); context.append('\n'); } } } @Override public void validate(SSAValidationContext context) { context.validate(condition); context.validate(elseTarget); context.validate(thenTarget); if(condition.getParent() != this) throw new InternalCompilerError(); if(elseTarget.getParent() != this) throw new InternalCompilerError(); if(thenTarget.getParent() != this) throw new InternalCompilerError(); context.assertEquals(this, condition.getType(), Types.BOOLEAN); context.assertEquals(elseTarget.getBinding().getArity(), 0); context.assertEquals(thenTarget.getBinding().getArity(), 0); } @Override public void destroy() { condition.remove(); elseTarget.remove(); thenTarget.remove(); } @Override public SSAExit copy(CopyContext context) { return new If(context.copy(condition), context.copy(thenTarget), context.copy(elseTarget)); } @Override public void replace(TVar[] vars, Type[] replacements) { condition.replace(vars, replacements); } @Override public void simplify(SSASimplificationContext context) { Val cond = condition.getBinding(); if(cond instanceof BooleanConstant) { SSAExit newExit; if(((BooleanConstant) cond).getValue()) { newExit = new Jump(thenTarget); elseTarget.remove(); } else { newExit = new Jump(elseTarget); thenTarget.remove(); } condition.remove(); getParent().setExit(newExit); context.markModified("beta-if"); } else if(thenTarget.getBinding() == elseTarget.getBinding()) { elseTarget.remove(); condition.remove(); getParent().setExit(new Jump(thenTarget)); context.markModified("equal-branches-if"); } } @Override public void collectFreeVariables(SSAFunction function, ArrayList vars) { condition.collectFreeVariables(function, vars); } @Override public Cont addParametersInFrontOf(ContRef contRef, Val[] newParameters, Val[] oldParameters, Cont proxy) { if(proxy == null) proxy = contRef.getBinding().createProxy(getParent().getParent(), newParameters, oldParameters); ContRef proxyRef = proxy.createOccurrence(); if(thenTarget == contRef) setThenTarget(proxyRef); else setElseTarget(proxyRef); return proxy; } @Override public SSABlock[] getSuccessors() { Cont thenCont = thenTarget.getBinding(); Cont elseCont = elseTarget.getBinding(); if(thenCont instanceof SSABlock) { if(elseCont instanceof SSABlock) return new SSABlock[] {(SSABlock)thenCont, (SSABlock)elseCont}; else return new SSABlock[] {(SSABlock)thenCont}; } else { if(elseCont instanceof SSABlock) return new SSABlock[] {(SSABlock)elseCont}; else return SSABlock.EMPTY_ARRAY; } } @Override public void forValRefs(ValRefVisitor visitor) { visitor.visit(condition); } }