]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/exits/Switch.java
27f853e0283b3d714757f4d3369cab4379cf470d
[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
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;
28
29 public class Switch extends SSAExit implements ValRefBinder {
30
31     ValRef scrutinee;
32     BranchRef[] branches;
33     
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);
40     }
41     
42     public ValRef getScrutinee() {
43         return scrutinee;
44     }
45     
46     public BranchRef[] getBranches() {
47         return branches;
48     }
49     
50     private boolean isIntegerSwitch() {
51         if(scrutinee.getType() != Types.INTEGER)
52             return false;
53         for(BranchRef branch : branches)
54             if(branch.constructor != null && !(branch.constructor instanceof IntegerConstant))
55                 return false;
56         return true;
57     }
58     
59     private void generateIntegerSwitch(MethodBuilder mb) {
60         int defaultId;
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;
70         }
71         Label defaultLabel;
72         {
73             Cont cont = branches[defaultId].cont.getBinding();
74             defaultLabel = mb.getLabel(cont);
75             continuations[defaultId] = cont;
76         }
77         mb.push(scrutinee, Types.INTEGER);
78         mb.switch_(values, labels, defaultLabel);
79         for(Cont cont : continuations)
80             mb.ensureExists(cont);
81     }
82
83     @Override
84     public void generateCode(MethodBuilder mb) {
85         if(isIntegerSwitch()) {
86             generateIntegerSwitch(mb);
87             return;
88         }
89         for(int i=0;i<branches.length;++i) {
90             BranchRef branch = branches[i];
91             if(branch.constructor == null)
92                 mb.jump(branch.cont);
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);
98             }
99             else
100                 branch.constructor.deconstruct(mb, scrutinee, 
101                         branch.cont.getBinding(), null);
102         }
103     }
104
105     @Override
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");
114             else
115                 context.append(branch.constructor.toString());
116             context.append(" -> ");
117             {
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);
125                     /*}
126                     else {
127                         block.parametersToString(context);
128                         context.append('\n');
129                         block.bodyToString(context);
130                     }*/
131                 }
132                 else {
133                     context.append(cont);
134                     context.append('\n');
135                 }
136             }
137         }
138         for(SSABlock block : getSuccessors())
139             context.addBlock(block);
140     }
141
142     @Override
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();
151         }
152     }
153
154     @Override
155     public void destroy() {
156         scrutinee.remove();
157         for(BranchRef branch : branches)
158             branch.cont.remove();
159     }
160     
161     @Override
162     public SSAExit copy(CopyContext context) {
163         return new Switch(context.copy(scrutinee), 
164                 BranchRef.copy(context, branches));
165     }
166
167     @Override
168     public void replace(TVar[] vars, Type[] replacements) {
169         scrutinee.replace(vars, replacements);        
170     }
171     
172     @Override
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;
182                         used = true;
183                     }
184                     if(elseTarget == null) {
185                         elseTarget = branch.cont;
186                         used = true;
187                     }
188                 }
189                 else if(((BooleanConstant)branch.constructor).getValue()) {
190                     if(thenTarget == null) {
191                         thenTarget = branch.cont;
192                         used = true;
193                     }
194                 }
195                 else {
196                     if(elseTarget == null) {
197                         elseTarget = branch.cont;
198                         used = true;
199                     }
200                 }
201                 if(!used)
202                     branch.cont.remove();
203             }
204             
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;
211             
212             // Replace switch by jump or if
213             SSAExit newExit;
214             if(thenTarget == elseTarget) {
215                 scrutinee.remove();
216                 newExit = new Jump(thenTarget);
217             }
218             else {
219                 newExit = new If(scrutinee, 
220                         thenTarget, 
221                         elseTarget);
222             }
223             getParent().setExit(newExit);
224             context.markModified("switch-to-if");
225             newExit.simplify(context);
226         }
227         else if(branches.length == 1 && isConstructorParameterless(branches[0])) {
228             scrutinee.remove();
229             getParent().setExit(new Jump(branches[0].cont));
230         }
231     }
232     
233     private static boolean isConstructorParameterless(BranchRef branch) {
234         return branch.constructor == null || branch.constructor instanceof NoRepConstant;
235     }
236
237     @Override
238     public void collectFreeVariables(SSAFunction function,
239             ArrayList<ValRef> vars) {
240         scrutinee.collectFreeVariables(function, vars);
241     }
242     
243     @Override
244     public Cont addParametersInFrontOf(ContRef contRef, Val[] newParameters, Val[] oldParameters,
245             Cont proxy) {
246         if(proxy == null)
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;
253                 break;
254             }
255         }
256         return proxy;
257     }
258
259     @Override
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);
266         }
267         return result.toArray(new SSABlock[result.size()]);
268     }
269
270     @Override
271     public void forValRefs(ValRefVisitor visitor) {
272         visitor.visit(scrutinee);
273     }
274 }