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(ValRef scrutinee, BranchRef[] branches) {
38 this.scrutinee = scrutinee;
39 this.branches = branches;
40 scrutinee.setParent(this);
41 for(BranchRef branch : branches)
42 branch.cont.setParent(this);
45 public ValRef getScrutinee() {
49 public BranchRef[] getBranches() {
53 private boolean isIntegerSwitch() {
54 if(scrutinee.getType() != Types.INTEGER)
56 for(BranchRef branch : branches)
57 if(branch.constructor != null && !(branch.constructor instanceof IntegerConstant))
62 private void generateIntegerSwitch(MethodBuilder mb) {
64 for(defaultId=0;defaultId<branches.length-1&&branches[defaultId].constructor!=null;++defaultId);
65 int[] values = new int[defaultId];
66 Cont[] continuations = new Cont[defaultId+1];
67 TIntObjectHashMap<Label> labelMap = new TIntObjectHashMap<Label>(defaultId);
68 for(int i=0;i<defaultId;++i) {
69 int value = ((IntegerConstant)branches[i].constructor).getValue();
71 Cont cont = branches[i].cont.getBinding();
72 labelMap.put(value, mb.getLabel(cont));
73 continuations[i] = cont;
76 Label[] labels = new Label[defaultId];
77 for(int i=0;i<defaultId;++i)
78 labels[i] = labelMap.get(values[i]);
81 Cont cont = branches[defaultId].cont.getBinding();
82 defaultLabel = mb.getLabel(cont);
83 continuations[defaultId] = cont;
85 mb.push(scrutinee, Types.INTEGER);
86 mb.switch_(values, labels, defaultLabel);
87 for(Cont cont : continuations)
88 mb.ensureExists(cont);
92 public void generateCode(MethodBuilder mb) {
93 if(isIntegerSwitch()) {
94 generateIntegerSwitch(mb);
97 for(int i=0;i<branches.length;++i) {
98 BranchRef branch = branches[i];
99 if(branch.constructor == null)
100 mb.jump(branch.cont);
101 else if(i < branches.length-1) {
102 Label failure = mb.createLabel();
103 branch.constructor.deconstruct(mb, scrutinee,
104 branch.cont.getBinding(), failure);
105 mb.setLocation(failure);
108 branch.constructor.deconstruct(mb, scrutinee,
109 branch.cont.getBinding(), null);
114 public void toString(PrintingContext context) {
115 context.append("switch ");
116 context.append(scrutinee);
117 for(BranchRef branch : branches) {
118 context.append('\n');
119 context.indentation();
120 if(branch.constructor == null)
121 context.append("otherwise");
123 context.append(branch.constructor.toString());
124 context.append(" -> ");
126 Cont cont = branch.cont.getBinding();
127 if(cont instanceof SSABlock) {
128 SSABlock block = (SSABlock)cont;
129 //if(cont.hasMoreThanOneOccurences()) {
130 context.append(cont);
131 context.append('\n');
132 context.addBlock(block);
135 block.parametersToString(context);
136 context.append('\n');
137 block.bodyToString(context);
141 context.append(cont);
142 context.append('\n');
146 for(SSABlock block : getSuccessors())
147 context.addBlock(block);
151 public void validate(SSAValidationContext context) {
152 context.validate(scrutinee);
153 if(scrutinee.getParent() != this)
154 throw new InternalCompilerError();
155 for(BranchRef branch : branches) {
156 context.validate(branch.cont);
157 if(branch.cont.getParent() != this)
158 throw new InternalCompilerError();
163 public void destroy() {
165 for(BranchRef branch : branches)
166 branch.cont.remove();
170 public SSAExit copy(CopyContext context) {
171 return new Switch(context.copy(scrutinee),
172 BranchRef.copy(context, branches));
176 public void replace(TVar[] vars, Type[] replacements) {
177 scrutinee.replace(vars, replacements);
181 public void simplify(SSASimplificationContext context) {
182 if(Types.equals(scrutinee.getType(), Types.BOOLEAN)) {
183 ContRef thenTarget = null;
184 ContRef elseTarget = null;
185 for(BranchRef branch : branches) {
186 boolean used = false;
187 if(branch.constructor == null) {
188 if(thenTarget == null) {
189 thenTarget = branch.cont;
192 if(elseTarget == null) {
193 elseTarget = branch.cont;
197 else if(((BooleanConstant)branch.constructor).getValue()) {
198 if(thenTarget == null) {
199 thenTarget = branch.cont;
204 if(elseTarget == null) {
205 elseTarget = branch.cont;
210 branch.cont.remove();
213 // This may be possible if match compiler has
214 // determined that one of the branches is not possible.
215 if(elseTarget == null)
216 elseTarget = thenTarget;
217 else if(thenTarget == null)
218 thenTarget = elseTarget;
220 // Replace switch by jump or if
222 if(thenTarget == elseTarget) {
224 newExit = new Jump(thenTarget);
227 newExit = new If(scrutinee,
231 getParent().setExit(newExit);
232 context.markModified("switch-to-if");
233 newExit.simplify(context);
235 else if(branches.length == 1 && isConstructorParameterless(branches[0])) {
237 getParent().setExit(new Jump(branches[0].cont));
241 private static boolean isConstructorParameterless(BranchRef branch) {
242 return branch.constructor == null || branch.constructor instanceof NoRepConstant;
246 public void collectFreeVariables(SSAFunction function,
247 ArrayList<ValRef> vars) {
248 scrutinee.collectFreeVariables(function, vars);
252 public Cont addParametersInFrontOf(ContRef contRef, Val[] newParameters, Val[] oldParameters,
255 proxy = contRef.getBinding().createProxy(getParent().getParent(), newParameters, oldParameters);
256 ContRef proxyRef = proxy.createOccurrence();
257 proxyRef.setParent(this);
258 for(BranchRef branch : branches) {
259 if(branch.cont == contRef) {
260 branch.cont = proxyRef;
268 public SSABlock[] getSuccessors() {
269 ArrayList<SSABlock> result = new ArrayList<SSABlock>(branches.length);
270 for(BranchRef branch : branches) {
271 Cont cont = branch.cont.getBinding();
272 if(cont instanceof SSABlock)
273 result.add((SSABlock)cont);
275 return result.toArray(new SSABlock[result.size()]);
279 public void forValRefs(ValRefVisitor visitor) {
280 visitor.visit(scrutinee);