1 package org.simantics.scl.compiler.internal.codegen.ssa.exits;
3 import java.util.ArrayList;
4 import java.util.Arrays;
6 import org.objectweb.asm.Label;
7 import org.simantics.scl.compiler.common.exceptions.InternalCompilerError;
8 import org.simantics.scl.compiler.constants.BooleanConstant;
9 import org.simantics.scl.compiler.constants.IntegerConstant;
10 import org.simantics.scl.compiler.constants.NoRepConstant;
11 import org.simantics.scl.compiler.internal.codegen.continuations.BranchRef;
12 import org.simantics.scl.compiler.internal.codegen.continuations.Cont;
13 import org.simantics.scl.compiler.internal.codegen.continuations.ContRef;
14 import org.simantics.scl.compiler.internal.codegen.references.Val;
15 import org.simantics.scl.compiler.internal.codegen.references.ValRef;
16 import org.simantics.scl.compiler.internal.codegen.ssa.SSABlock;
17 import org.simantics.scl.compiler.internal.codegen.ssa.SSAExit;
18 import org.simantics.scl.compiler.internal.codegen.ssa.SSAFunction;
19 import org.simantics.scl.compiler.internal.codegen.ssa.binders.ValRefBinder;
20 import org.simantics.scl.compiler.internal.codegen.utils.CopyContext;
21 import org.simantics.scl.compiler.internal.codegen.utils.MethodBuilder;
22 import org.simantics.scl.compiler.internal.codegen.utils.PrintingContext;
23 import org.simantics.scl.compiler.internal.codegen.utils.SSASimplificationContext;
24 import org.simantics.scl.compiler.internal.codegen.utils.SSAValidationContext;
25 import org.simantics.scl.compiler.internal.codegen.utils.ValRefVisitor;
26 import org.simantics.scl.compiler.types.TVar;
27 import org.simantics.scl.compiler.types.Type;
28 import org.simantics.scl.compiler.types.Types;
30 import gnu.trove.map.hash.TIntObjectHashMap;
32 public class Switch extends SSAExit implements ValRefBinder {
37 public Switch(int lineNumber, ValRef scrutinee, BranchRef[] branches) {
39 this.scrutinee = scrutinee;
40 this.branches = branches;
41 scrutinee.setParent(this);
42 for(BranchRef branch : branches)
43 branch.cont.setParent(this);
46 public ValRef getScrutinee() {
50 public BranchRef[] getBranches() {
54 private boolean isIntegerSwitch() {
55 if(scrutinee.getType() != Types.INTEGER)
57 for(BranchRef branch : branches)
58 if(branch.constructor != null && !(branch.constructor instanceof IntegerConstant))
63 private void generateIntegerSwitch(MethodBuilder mb) {
65 for(defaultId=0;defaultId<branches.length-1&&branches[defaultId].constructor!=null;++defaultId);
66 int[] values = new int[defaultId];
67 Cont[] continuations = new Cont[defaultId+1];
68 TIntObjectHashMap<Label> labelMap = new TIntObjectHashMap<Label>(defaultId);
69 for(int i=0;i<defaultId;++i) {
70 int value = ((IntegerConstant)branches[i].constructor).getValue();
72 Cont cont = branches[i].cont.getBinding();
73 labelMap.put(value, mb.getLabel(cont));
74 continuations[i] = cont;
77 Label[] labels = new Label[defaultId];
78 for(int i=0;i<defaultId;++i)
79 labels[i] = labelMap.get(values[i]);
82 Cont cont = branches[defaultId].cont.getBinding();
83 defaultLabel = mb.getLabel(cont);
84 continuations[defaultId] = cont;
86 mb.push(scrutinee, Types.INTEGER);
87 mb.switch_(values, labels, defaultLabel);
88 for(Cont cont : continuations)
89 mb.ensureExists(cont);
93 public void generateCode(MethodBuilder mb) {
94 mb.lineNumber(lineNumber);
95 if(isIntegerSwitch()) {
96 generateIntegerSwitch(mb);
99 for(int i=0;i<branches.length;++i) {
100 BranchRef branch = branches[i];
101 if(branch.constructor == null)
102 mb.jump(branch.cont);
103 else if(i < branches.length-1) {
104 Label failure = mb.createLabel();
105 branch.constructor.deconstruct(mb, scrutinee,
106 branch.cont.getBinding(), failure);
107 mb.setLocation(failure);
110 branch.constructor.deconstruct(mb, scrutinee,
111 branch.cont.getBinding(), null);
116 public void toString(PrintingContext context) {
117 context.append("switch ");
118 context.append(scrutinee);
119 for(BranchRef branch : branches) {
120 context.append('\n');
121 context.indentation();
122 if(branch.constructor == null)
123 context.append("otherwise");
125 context.append(branch.constructor.toString());
126 context.append(" -> ");
128 Cont cont = branch.cont.getBinding();
129 if(cont instanceof SSABlock) {
130 SSABlock block = (SSABlock)cont;
131 //if(cont.hasMoreThanOneOccurences()) {
132 context.append(cont);
133 context.append('\n');
134 context.addBlock(block);
137 block.parametersToString(context);
138 context.append('\n');
139 block.bodyToString(context);
143 context.append(cont);
144 context.append('\n');
148 for(SSABlock block : getSuccessors())
149 context.addBlock(block);
153 public void validate(SSAValidationContext context) {
154 context.validate(scrutinee);
155 if(scrutinee.getParent() != this)
156 throw new InternalCompilerError();
157 for(BranchRef branch : branches) {
158 context.validate(branch.cont);
159 if(branch.cont.getParent() != this)
160 throw new InternalCompilerError();
165 public void destroy() {
167 for(BranchRef branch : branches)
168 branch.cont.remove();
172 public SSAExit copy(CopyContext context) {
173 return new Switch(lineNumber, context.copy(scrutinee),
174 BranchRef.copy(context, branches));
178 public void replace(TVar[] vars, Type[] replacements) {
179 scrutinee.replace(vars, replacements);
183 public void simplify(SSASimplificationContext context) {
184 if(Types.equals(scrutinee.getType(), Types.BOOLEAN)) {
185 ContRef thenTarget = null;
186 ContRef elseTarget = null;
187 for(BranchRef branch : branches) {
188 boolean used = false;
189 if(branch.constructor == null) {
190 if(thenTarget == null) {
191 thenTarget = branch.cont;
194 if(elseTarget == null) {
195 elseTarget = branch.cont;
199 else if(((BooleanConstant)branch.constructor).getValue()) {
200 if(thenTarget == null) {
201 thenTarget = branch.cont;
206 if(elseTarget == null) {
207 elseTarget = branch.cont;
212 branch.cont.remove();
215 // This may be possible if match compiler has
216 // determined that one of the branches is not possible.
217 if(elseTarget == null)
218 elseTarget = thenTarget;
219 else if(thenTarget == null)
220 thenTarget = elseTarget;
222 // Replace switch by jump or if
224 if(thenTarget == elseTarget) {
226 newExit = new Jump(lineNumber, thenTarget);
229 newExit = new If(lineNumber,
234 getParent().setExit(newExit);
235 context.markModified("switch-to-if");
236 newExit.simplify(context);
238 else if(branches.length == 1 && isConstructorParameterless(branches[0])) {
240 getParent().setExit(new Jump(lineNumber, branches[0].cont));
244 private static boolean isConstructorParameterless(BranchRef branch) {
245 return branch.constructor == null || branch.constructor instanceof NoRepConstant;
249 public void collectFreeVariables(SSAFunction function,
250 ArrayList<ValRef> vars) {
251 scrutinee.collectFreeVariables(function, vars);
255 public Cont addParametersInFrontOf(ContRef contRef, Val[] newParameters, Val[] oldParameters,
258 proxy = contRef.getBinding().createProxy(getParent().getParent(), newParameters, oldParameters);
259 ContRef proxyRef = proxy.createOccurrence();
260 proxyRef.setParent(this);
261 for(BranchRef branch : branches) {
262 if(branch.cont == contRef) {
263 branch.cont = proxyRef;
271 public SSABlock[] getSuccessors() {
272 ArrayList<SSABlock> result = new ArrayList<SSABlock>(branches.length);
273 for(BranchRef branch : branches) {
274 Cont cont = branch.cont.getBinding();
275 if(cont instanceof SSABlock)
276 result.add((SSABlock)cont);
278 return result.toArray(new SSABlock[result.size()]);
282 public void forValRefs(ValRefVisitor visitor) {
283 visitor.visit(scrutinee);
287 public void cleanup() {