]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/exits/Switch.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.scl.compiler / src / org / simantics / scl / compiler / internal / codegen / ssa / exits / Switch.java
diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/exits/Switch.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/exits/Switch.java
new file mode 100644 (file)
index 0000000..a6674eb
--- /dev/null
@@ -0,0 +1,227 @@
+package org.simantics.scl.compiler.internal.codegen.ssa.exits;
+
+import java.util.ArrayList;
+
+import org.objectweb.asm.Label;
+import org.simantics.scl.compiler.common.exceptions.InternalCompilerError;
+import org.simantics.scl.compiler.constants.BooleanConstant;
+import org.simantics.scl.compiler.internal.codegen.continuations.BranchRef;
+import org.simantics.scl.compiler.internal.codegen.continuations.Cont;
+import org.simantics.scl.compiler.internal.codegen.continuations.ContRef;
+import org.simantics.scl.compiler.internal.codegen.references.Val;
+import org.simantics.scl.compiler.internal.codegen.references.ValRef;
+import org.simantics.scl.compiler.internal.codegen.ssa.SSABlock;
+import org.simantics.scl.compiler.internal.codegen.ssa.SSAExit;
+import org.simantics.scl.compiler.internal.codegen.ssa.SSAFunction;
+import org.simantics.scl.compiler.internal.codegen.ssa.binders.ValRefBinder;
+import org.simantics.scl.compiler.internal.codegen.utils.CopyContext;
+import org.simantics.scl.compiler.internal.codegen.utils.MethodBuilder;
+import org.simantics.scl.compiler.internal.codegen.utils.PrintingContext;
+import org.simantics.scl.compiler.internal.codegen.utils.SSASimplificationContext;
+import org.simantics.scl.compiler.internal.codegen.utils.SSAValidationContext;
+import org.simantics.scl.compiler.types.TVar;
+import org.simantics.scl.compiler.types.Type;
+import org.simantics.scl.compiler.types.Types;
+
+public class Switch extends SSAExit implements ValRefBinder {
+
+    ValRef scrutinee;
+    BranchRef[] branches;
+    
+    public Switch(ValRef scrutinee, BranchRef[] branches) {
+        this.scrutinee = scrutinee;
+        this.branches = branches;
+        scrutinee.setParent(this);
+        for(BranchRef branch : branches)
+            branch.cont.setParent(this);
+    }
+    
+    public ValRef getScrutinee() {
+        return scrutinee;
+    }
+    
+    public BranchRef[] getBranches() {
+        return branches;
+    }
+
+    @Override
+    public void generateCode(MethodBuilder mb) {
+        for(int i=0;i<branches.length;++i) {
+            BranchRef branch = branches[i];
+            if(branch.constructor == null) {
+                mb.jump(branch.cont);
+            }
+            else if(i < branches.length-1) {                
+                Label failure = mb.createLabel();
+                branch.constructor.deconstruct(mb, scrutinee, 
+                        branch.cont.getBinding(), failure);
+                mb.setLocation(failure);
+            }
+            else {
+                branch.constructor.deconstruct(mb, scrutinee, 
+                        branch.cont.getBinding(), null);
+            }
+        }
+    }
+
+    @Override
+    public void toString(PrintingContext context) {
+        context.append("switch ");
+        context.append(scrutinee);        
+        for(BranchRef branch : branches) {
+            context.append('\n');
+            context.indentation();
+            if(branch.constructor == null)
+                context.append("otherwise");
+            else
+                context.append(branch.constructor.toString());
+            context.append(" -> ");
+            {
+                Cont cont = branch.cont.getBinding();
+                if(cont instanceof SSABlock) {
+                    SSABlock block = (SSABlock)cont;
+                    if(cont.hasMoreThanOneOccurences()) {
+                        context.append(cont);
+                        context.append('\n');
+                        context.addBlock(block);
+                    }
+                    else {
+                        block.parametersToString(context);
+                        context.append('\n');
+                        block.bodyToString(context);
+                    }
+                }
+                else {
+                    context.append(cont);
+                    context.append('\n');
+                }
+            }
+        }
+        for(SSABlock block : getSuccessors())
+            context.addBlock(block);
+    }
+
+    @Override
+    public void validate(SSAValidationContext context) {
+        context.validate(scrutinee);
+        if(scrutinee.getParent() != this)
+            throw new InternalCompilerError();
+        for(BranchRef branch : branches) {
+            context.validate(branch.cont);
+            if(branch.cont.getParent() != this)
+                throw new InternalCompilerError();
+        }
+    }
+
+    @Override
+    public void destroy() {
+        scrutinee.remove();
+        for(BranchRef branch : branches)
+            branch.cont.remove();
+    }
+    
+    @Override
+    public SSAExit copy(CopyContext context) {
+        return new Switch(context.copy(scrutinee), 
+                BranchRef.copy(context, branches));
+    }
+
+    @Override
+    public void replace(TVar[] vars, Type[] replacements) {
+        scrutinee.replace(vars, replacements);        
+    }
+    
+    @Override
+    public void simplify(SSASimplificationContext context) {
+        if(Types.equals(scrutinee.getType(), Types.BOOLEAN)) {
+            ContRef thenTarget = null;
+            ContRef elseTarget = null;
+            for(BranchRef branch : branches) {
+                boolean used = false;
+                if(branch.constructor == null) {
+                    if(thenTarget == null) {
+                        thenTarget = branch.cont;
+                        used = true;
+                    }
+                    if(elseTarget == null) {
+                        elseTarget = branch.cont;
+                        used = true;
+                    }
+                }
+                else if(((BooleanConstant)branch.constructor).getValue()) {
+                    if(thenTarget == null) {
+                        thenTarget = branch.cont;
+                        used = true;
+                    }
+                }
+                else {
+                    if(elseTarget == null) {
+                        elseTarget = branch.cont;
+                        used = true;
+                    }
+                }
+                if(!used)
+                    branch.cont.remove();
+            }
+            
+            // This may be possible if match compiler has
+            // determined that one of the branches is not possible.
+            if(elseTarget == null) 
+                elseTarget = thenTarget;
+            else if(thenTarget == null) 
+                thenTarget = elseTarget;
+            
+            // Replace switch by jump or if
+            SSAExit newExit;
+            if(thenTarget == elseTarget) {
+                scrutinee.remove();
+                newExit = new Jump(thenTarget);
+            }
+            else {
+                newExit = new If(scrutinee, 
+                        thenTarget, 
+                        elseTarget);
+            }
+            getParent().setExit(newExit);
+            context.markModified("switch-to-if");
+            newExit.simplify(context);
+        }
+        else if(branches.length == 1 && branches[0].constructor == null) {
+            scrutinee.remove();
+            getParent().setExit(new Jump(branches[0].cont));
+        }
+    }
+
+    @Override
+    public void collectFreeVariables(SSAFunction function,
+            ArrayList<ValRef> vars) {
+        scrutinee.collectFreeVariables(function, vars);
+    }
+    
+    @Override
+    public Cont addParametersInFrontOf(ContRef contRef, Val[] newParameters, Val[] oldParameters,
+            Cont proxy) {
+        if(proxy == null)
+            proxy = contRef.getBinding().createProxy(getParent().getParent(), newParameters, oldParameters);
+        ContRef proxyRef = proxy.createOccurrence();
+        proxyRef.setParent(this);
+        for(BranchRef branch : branches) {
+            if(branch.cont == contRef) {
+                branch.cont = proxyRef;
+                break;
+            }
+        }
+        return proxy;
+    }
+
+    @Override
+    public SSABlock[] getSuccessors() {
+        ArrayList<SSABlock> result = new ArrayList<SSABlock>(branches.length);
+        for(BranchRef branch : branches) {
+            Cont cont = branch.cont.getBinding();
+            if(cont instanceof SSABlock)
+                result.add((SSABlock)cont);
+        }
+        return result.toArray(new SSABlock[result.size()]);
+    }
+}