]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/exits/Switch.java
Added memory leak test and fixed the leak by removing references
[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(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);
43     }
44     
45     public ValRef getScrutinee() {
46         return scrutinee;
47     }
48     
49     public BranchRef[] getBranches() {
50         return branches;
51     }
52     
53     private boolean isIntegerSwitch() {
54         if(scrutinee.getType() != Types.INTEGER)
55             return false;
56         for(BranchRef branch : branches)
57             if(branch.constructor != null && !(branch.constructor instanceof IntegerConstant))
58                 return false;
59         return true;
60     }
61     
62     private void generateIntegerSwitch(MethodBuilder mb) {
63         int defaultId;
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();
70             values[i] = value;
71             Cont cont = branches[i].cont.getBinding();
72             labelMap.put(value,  mb.getLabel(cont));
73             continuations[i] = cont;
74         }
75         Arrays.sort(values);
76         Label[] labels = new Label[defaultId];
77         for(int i=0;i<defaultId;++i)
78             labels[i] = labelMap.get(values[i]);
79         Label defaultLabel;
80         {
81             Cont cont = branches[defaultId].cont.getBinding();
82             defaultLabel = mb.getLabel(cont);
83             continuations[defaultId] = cont;
84         }
85         mb.push(scrutinee, Types.INTEGER);
86         mb.switch_(values, labels, defaultLabel);
87         for(Cont cont : continuations)
88             mb.ensureExists(cont);
89     }
90
91     @Override
92     public void generateCode(MethodBuilder mb) {
93         if(isIntegerSwitch()) {
94             generateIntegerSwitch(mb);
95             return;
96         }
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);
106             }
107             else
108                 branch.constructor.deconstruct(mb, scrutinee, 
109                         branch.cont.getBinding(), null);
110         }
111     }
112
113     @Override
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");
122             else
123                 context.append(branch.constructor.toString());
124             context.append(" -> ");
125             {
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);
133                     /*}
134                     else {
135                         block.parametersToString(context);
136                         context.append('\n');
137                         block.bodyToString(context);
138                     }*/
139                 }
140                 else {
141                     context.append(cont);
142                     context.append('\n');
143                 }
144             }
145         }
146         for(SSABlock block : getSuccessors())
147             context.addBlock(block);
148     }
149
150     @Override
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();
159         }
160     }
161
162     @Override
163     public void destroy() {
164         scrutinee.remove();
165         for(BranchRef branch : branches)
166             branch.cont.remove();
167     }
168     
169     @Override
170     public SSAExit copy(CopyContext context) {
171         return new Switch(context.copy(scrutinee), 
172                 BranchRef.copy(context, branches));
173     }
174
175     @Override
176     public void replace(TVar[] vars, Type[] replacements) {
177         scrutinee.replace(vars, replacements);        
178     }
179     
180     @Override
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;
190                         used = true;
191                     }
192                     if(elseTarget == null) {
193                         elseTarget = branch.cont;
194                         used = true;
195                     }
196                 }
197                 else if(((BooleanConstant)branch.constructor).getValue()) {
198                     if(thenTarget == null) {
199                         thenTarget = branch.cont;
200                         used = true;
201                     }
202                 }
203                 else {
204                     if(elseTarget == null) {
205                         elseTarget = branch.cont;
206                         used = true;
207                     }
208                 }
209                 if(!used)
210                     branch.cont.remove();
211             }
212             
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;
219             
220             // Replace switch by jump or if
221             SSAExit newExit;
222             if(thenTarget == elseTarget) {
223                 scrutinee.remove();
224                 newExit = new Jump(thenTarget);
225             }
226             else {
227                 newExit = new If(scrutinee, 
228                         thenTarget, 
229                         elseTarget);
230             }
231             getParent().setExit(newExit);
232             context.markModified("switch-to-if");
233             newExit.simplify(context);
234         }
235         else if(branches.length == 1 && isConstructorParameterless(branches[0])) {
236             scrutinee.remove();
237             getParent().setExit(new Jump(branches[0].cont));
238         }
239     }
240     
241     private static boolean isConstructorParameterless(BranchRef branch) {
242         return branch.constructor == null || branch.constructor instanceof NoRepConstant;
243     }
244
245     @Override
246     public void collectFreeVariables(SSAFunction function,
247             ArrayList<ValRef> vars) {
248         scrutinee.collectFreeVariables(function, vars);
249     }
250     
251     @Override
252     public Cont addParametersInFrontOf(ContRef contRef, Val[] newParameters, Val[] oldParameters,
253             Cont proxy) {
254         if(proxy == null)
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;
261                 break;
262             }
263         }
264         return proxy;
265     }
266
267     @Override
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);
274         }
275         return result.toArray(new SSABlock[result.size()]);
276     }
277
278     @Override
279     public void forValRefs(ValRefVisitor visitor) {
280         visitor.visit(scrutinee);
281     }
282
283     @Override
284     public void cleanup() {
285         scrutinee.remove();
286     }
287 }