]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/exits/Switch.java
SCL compiler generates line numbers to bytecode
[simantics/platform.git] / bundles / org.simantics.scl.compiler / src / org / simantics / scl / compiler / internal / codegen / ssa / exits / Switch.java
1 package org.simantics.scl.compiler.internal.codegen.ssa.exits;
2
3 import java.util.ArrayList;
4 import java.util.Arrays;
5
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;
29
30 import gnu.trove.map.hash.TIntObjectHashMap;
31
32 public class Switch extends SSAExit implements ValRefBinder {
33
34     ValRef scrutinee;
35     BranchRef[] branches;
36     
37     public Switch(int lineNumber, ValRef scrutinee, BranchRef[] branches) {
38         super(lineNumber);
39         this.scrutinee = scrutinee;
40         this.branches = branches;
41         scrutinee.setParent(this);
42         for(BranchRef branch : branches)
43             branch.cont.setParent(this);
44     }
45     
46     public ValRef getScrutinee() {
47         return scrutinee;
48     }
49     
50     public BranchRef[] getBranches() {
51         return branches;
52     }
53     
54     private boolean isIntegerSwitch() {
55         if(scrutinee.getType() != Types.INTEGER)
56             return false;
57         for(BranchRef branch : branches)
58             if(branch.constructor != null && !(branch.constructor instanceof IntegerConstant))
59                 return false;
60         return true;
61     }
62     
63     private void generateIntegerSwitch(MethodBuilder mb) {
64         int defaultId;
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();
71             values[i] = value;
72             Cont cont = branches[i].cont.getBinding();
73             labelMap.put(value,  mb.getLabel(cont));
74             continuations[i] = cont;
75         }
76         Arrays.sort(values);
77         Label[] labels = new Label[defaultId];
78         for(int i=0;i<defaultId;++i)
79             labels[i] = labelMap.get(values[i]);
80         Label defaultLabel;
81         {
82             Cont cont = branches[defaultId].cont.getBinding();
83             defaultLabel = mb.getLabel(cont);
84             continuations[defaultId] = cont;
85         }
86         mb.push(scrutinee, Types.INTEGER);
87         mb.switch_(values, labels, defaultLabel);
88         for(Cont cont : continuations)
89             mb.ensureExists(cont);
90     }
91
92     @Override
93     public void generateCode(MethodBuilder mb) {
94         mb.lineNumber(lineNumber);
95         if(isIntegerSwitch()) {
96             generateIntegerSwitch(mb);
97             return;
98         }
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);
108             }
109             else
110                 branch.constructor.deconstruct(mb, scrutinee, 
111                         branch.cont.getBinding(), null);
112         }
113     }
114
115     @Override
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");
124             else
125                 context.append(branch.constructor.toString());
126             context.append(" -> ");
127             {
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);
135                     /*}
136                     else {
137                         block.parametersToString(context);
138                         context.append('\n');
139                         block.bodyToString(context);
140                     }*/
141                 }
142                 else {
143                     context.append(cont);
144                     context.append('\n');
145                 }
146             }
147         }
148         for(SSABlock block : getSuccessors())
149             context.addBlock(block);
150     }
151
152     @Override
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();
161         }
162     }
163
164     @Override
165     public void destroy() {
166         scrutinee.remove();
167         for(BranchRef branch : branches)
168             branch.cont.remove();
169     }
170     
171     @Override
172     public SSAExit copy(CopyContext context) {
173         return new Switch(lineNumber, context.copy(scrutinee), 
174                 BranchRef.copy(context, branches));
175     }
176
177     @Override
178     public void replace(TVar[] vars, Type[] replacements) {
179         scrutinee.replace(vars, replacements);        
180     }
181     
182     @Override
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;
192                         used = true;
193                     }
194                     if(elseTarget == null) {
195                         elseTarget = branch.cont;
196                         used = true;
197                     }
198                 }
199                 else if(((BooleanConstant)branch.constructor).getValue()) {
200                     if(thenTarget == null) {
201                         thenTarget = branch.cont;
202                         used = true;
203                     }
204                 }
205                 else {
206                     if(elseTarget == null) {
207                         elseTarget = branch.cont;
208                         used = true;
209                     }
210                 }
211                 if(!used)
212                     branch.cont.remove();
213             }
214             
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;
221             
222             // Replace switch by jump or if
223             SSAExit newExit;
224             if(thenTarget == elseTarget) {
225                 scrutinee.remove();
226                 newExit = new Jump(lineNumber, thenTarget);
227             }
228             else {
229                 newExit = new If(lineNumber,
230                         scrutinee, 
231                         thenTarget, 
232                         elseTarget);
233             }
234             getParent().setExit(newExit);
235             context.markModified("switch-to-if");
236             newExit.simplify(context);
237         }
238         else if(branches.length == 1 && isConstructorParameterless(branches[0])) {
239             scrutinee.remove();
240             getParent().setExit(new Jump(lineNumber, branches[0].cont));
241         }
242     }
243     
244     private static boolean isConstructorParameterless(BranchRef branch) {
245         return branch.constructor == null || branch.constructor instanceof NoRepConstant;
246     }
247
248     @Override
249     public void collectFreeVariables(SSAFunction function,
250             ArrayList<ValRef> vars) {
251         scrutinee.collectFreeVariables(function, vars);
252     }
253     
254     @Override
255     public Cont addParametersInFrontOf(ContRef contRef, Val[] newParameters, Val[] oldParameters,
256             Cont proxy) {
257         if(proxy == null)
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;
264                 break;
265             }
266         }
267         return proxy;
268     }
269
270     @Override
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);
277         }
278         return result.toArray(new SSABlock[result.size()]);
279     }
280
281     @Override
282     public void forValRefs(ValRefVisitor visitor) {
283         visitor.visit(scrutinee);
284     }
285
286     @Override
287     public void cleanup() {
288         scrutinee.remove();
289     }
290 }