1 package org.simantics.scl.compiler.internal.codegen.ssa.exits;
3 import java.util.ArrayList;
5 import org.objectweb.asm.Label;
6 import org.simantics.scl.compiler.common.exceptions.InternalCompilerError;
7 import org.simantics.scl.compiler.constants.BooleanConstant;
8 import org.simantics.scl.compiler.constants.IntegerConstant;
9 import org.simantics.scl.compiler.constants.NoRepConstant;
10 import org.simantics.scl.compiler.internal.codegen.continuations.BranchRef;
11 import org.simantics.scl.compiler.internal.codegen.continuations.Cont;
12 import org.simantics.scl.compiler.internal.codegen.continuations.ContRef;
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.SSABlock;
16 import org.simantics.scl.compiler.internal.codegen.ssa.SSAExit;
17 import org.simantics.scl.compiler.internal.codegen.ssa.SSAFunction;
18 import org.simantics.scl.compiler.internal.codegen.ssa.binders.ValRefBinder;
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.PrintingContext;
22 import org.simantics.scl.compiler.internal.codegen.utils.SSASimplificationContext;
23 import org.simantics.scl.compiler.internal.codegen.utils.SSAValidationContext;
24 import org.simantics.scl.compiler.internal.codegen.utils.ValRefVisitor;
25 import org.simantics.scl.compiler.types.TVar;
26 import org.simantics.scl.compiler.types.Type;
27 import org.simantics.scl.compiler.types.Types;
29 public class Switch extends SSAExit implements ValRefBinder {
34 public Switch(ValRef scrutinee, BranchRef[] branches) {
35 this.scrutinee = scrutinee;
36 this.branches = branches;
37 scrutinee.setParent(this);
38 for(BranchRef branch : branches)
39 branch.cont.setParent(this);
42 public ValRef getScrutinee() {
46 public BranchRef[] getBranches() {
50 private boolean isIntegerSwitch() {
51 if(scrutinee.getType() != Types.INTEGER)
53 for(BranchRef branch : branches)
54 if(branch.constructor != null && !(branch.constructor instanceof IntegerConstant))
59 private void generateIntegerSwitch(MethodBuilder mb) {
61 for(defaultId=0;defaultId<branches.length-1&&branches[defaultId].constructor!=null;++defaultId);
62 int[] values = new int[defaultId];
63 Label[] labels = new Label[defaultId];
64 Cont[] continuations = new Cont[defaultId+1];
65 for(int i=0;i<defaultId;++i) {
66 values[i] = ((IntegerConstant)branches[i].constructor).getValue();
67 Cont cont = branches[i].cont.getBinding();
68 labels[i] = mb.getLabel(cont);
69 continuations[i] = cont;
73 Cont cont = branches[defaultId].cont.getBinding();
74 defaultLabel = mb.getLabel(cont);
75 continuations[defaultId] = cont;
77 mb.push(scrutinee, Types.INTEGER);
78 mb.switch_(values, labels, defaultLabel);
79 for(Cont cont : continuations)
80 mb.ensureExists(cont);
84 public void generateCode(MethodBuilder mb) {
85 if(isIntegerSwitch()) {
86 generateIntegerSwitch(mb);
89 for(int i=0;i<branches.length;++i) {
90 BranchRef branch = branches[i];
91 if(branch.constructor == null)
93 else if(i < branches.length-1) {
94 Label failure = mb.createLabel();
95 branch.constructor.deconstruct(mb, scrutinee,
96 branch.cont.getBinding(), failure);
97 mb.setLocation(failure);
100 branch.constructor.deconstruct(mb, scrutinee,
101 branch.cont.getBinding(), null);
106 public void toString(PrintingContext context) {
107 context.append("switch ");
108 context.append(scrutinee);
109 for(BranchRef branch : branches) {
110 context.append('\n');
111 context.indentation();
112 if(branch.constructor == null)
113 context.append("otherwise");
115 context.append(branch.constructor.toString());
116 context.append(" -> ");
118 Cont cont = branch.cont.getBinding();
119 if(cont instanceof SSABlock) {
120 SSABlock block = (SSABlock)cont;
121 //if(cont.hasMoreThanOneOccurences()) {
122 context.append(cont);
123 context.append('\n');
124 context.addBlock(block);
127 block.parametersToString(context);
128 context.append('\n');
129 block.bodyToString(context);
133 context.append(cont);
134 context.append('\n');
138 for(SSABlock block : getSuccessors())
139 context.addBlock(block);
143 public void validate(SSAValidationContext context) {
144 context.validate(scrutinee);
145 if(scrutinee.getParent() != this)
146 throw new InternalCompilerError();
147 for(BranchRef branch : branches) {
148 context.validate(branch.cont);
149 if(branch.cont.getParent() != this)
150 throw new InternalCompilerError();
155 public void destroy() {
157 for(BranchRef branch : branches)
158 branch.cont.remove();
162 public SSAExit copy(CopyContext context) {
163 return new Switch(context.copy(scrutinee),
164 BranchRef.copy(context, branches));
168 public void replace(TVar[] vars, Type[] replacements) {
169 scrutinee.replace(vars, replacements);
173 public void simplify(SSASimplificationContext context) {
174 if(Types.equals(scrutinee.getType(), Types.BOOLEAN)) {
175 ContRef thenTarget = null;
176 ContRef elseTarget = null;
177 for(BranchRef branch : branches) {
178 boolean used = false;
179 if(branch.constructor == null) {
180 if(thenTarget == null) {
181 thenTarget = branch.cont;
184 if(elseTarget == null) {
185 elseTarget = branch.cont;
189 else if(((BooleanConstant)branch.constructor).getValue()) {
190 if(thenTarget == null) {
191 thenTarget = branch.cont;
196 if(elseTarget == null) {
197 elseTarget = branch.cont;
202 branch.cont.remove();
205 // This may be possible if match compiler has
206 // determined that one of the branches is not possible.
207 if(elseTarget == null)
208 elseTarget = thenTarget;
209 else if(thenTarget == null)
210 thenTarget = elseTarget;
212 // Replace switch by jump or if
214 if(thenTarget == elseTarget) {
216 newExit = new Jump(thenTarget);
219 newExit = new If(scrutinee,
223 getParent().setExit(newExit);
224 context.markModified("switch-to-if");
225 newExit.simplify(context);
227 else if(branches.length == 1 && isConstructorParameterless(branches[0])) {
229 getParent().setExit(new Jump(branches[0].cont));
233 private static boolean isConstructorParameterless(BranchRef branch) {
234 return branch.constructor == null || branch.constructor instanceof NoRepConstant;
238 public void collectFreeVariables(SSAFunction function,
239 ArrayList<ValRef> vars) {
240 scrutinee.collectFreeVariables(function, vars);
244 public Cont addParametersInFrontOf(ContRef contRef, Val[] newParameters, Val[] oldParameters,
247 proxy = contRef.getBinding().createProxy(getParent().getParent(), newParameters, oldParameters);
248 ContRef proxyRef = proxy.createOccurrence();
249 proxyRef.setParent(this);
250 for(BranchRef branch : branches) {
251 if(branch.cont == contRef) {
252 branch.cont = proxyRef;
260 public SSABlock[] getSuccessors() {
261 ArrayList<SSABlock> result = new ArrayList<SSABlock>(branches.length);
262 for(BranchRef branch : branches) {
263 Cont cont = branch.cont.getBinding();
264 if(cont instanceof SSABlock)
265 result.add((SSABlock)cont);
267 return result.toArray(new SSABlock[result.size()]);
271 public void forValRefs(ValRefVisitor visitor) {
272 visitor.visit(scrutinee);