Пример #1
0
 private string TryReduceJump(BasicBlock root, JumpBasicBlock jumpbb)
 {
     // Try to find a linear chain of branches of length > 1
     var group = new Set<BasicBlock> { jumpbb };
     var chain = new Seq<BasicBlock> { jumpbb };
     var start = jumpbb;
     while (start.Sources.Count == 1 && start.Sources[0] is JumpBasicBlock &&
            !group.Contains(start.Sources[0]))
     {
         // Extend chain upwards
         start = (JumpBasicBlock)start.Sources[0];
         group.Add(start);
         chain.Insert(0, start);
     }
     var endjump = jumpbb;
     while (endjump.Target.Sources.Count == 1 && endjump.Target is JumpBasicBlock && !group.Contains(endjump.Target))
     {
         // Extend chain downwards
         endjump = (JumpBasicBlock)endjump.Target;
         group.Add(endjump);
         chain.Add(endjump);
     }
     var end = (BasicBlock)endjump;
     if (endjump.Target.Sources.Count == 1 && endjump.Target.AcceptsInstructions && !group.Contains(endjump.Target))
     {
         // Extend chain downwards to include a final non jump basic block, provided we can put all
         // the accumulated instructions there
         end = endjump.Target;
         group.Add(end);
         chain.Add(end);
     }
     if (chain.Count > 1)
     {
         // Coalesce and peephole
         var instructions = new Seq<Instruction>();
         foreach (var bb in chain)
             bb.Block.AccumInstructions(instructions);
         var newbb = end.CloneWithInstructions(nextBlockId++, Peephole(chain[0].Block.BeforeState, instructions));
         root.Coalesce(start, group, newbb);
         return String.Format("collapsed chain of {0} jump blocks from B{1}", chain.Count, start.Id); 
     }
     else if (start.Block.Count == 0 && !start.Target.Equals(start))
     {
         // Short-circuit a trivial jump
         root.Delete(start);
         return String.Format("short-circuited trivial jump at B{0}", start.Id);
     }
     else if (start.Target.Equals(start))
     {
         // loop
         var li = new LoopInstruction(NextInstructionId--, start.Block)
                   { BeforeState = jumpbb.Block.BeforeState, AfterState = jumpbb.Block.AfterState };
         var newbb = new NonReturningBasicBlock(nextBlockId++, new Instructions(li));
         root.Coalesce(start, new Set<BasicBlock> { start }, newbb);
         return String.Format("trival loop at B{0}", start.Id);
     }
     else
         return null;
 }
Пример #2
0
        private string TryReduceBranch(BasicBlock root, BranchBasicBlock branchbb)
        {
            var beforeState = branchbb.Block.BeforeState;
            var afterState = branchbb.Block.AfterState;
            {
                // While-do
                var thenbb = branchbb.Target as JumpBasicBlock;
                var group = new Set<BasicBlock> { branchbb };
                if (thenbb != null && thenbb.Target.Equals(branchbb) && thenbb.Sources.Count == 1 && group.Add(thenbb) &&
                    !group.Contains(branchbb.Fallthrough))
                {
                    var instructions = branchbb.Block.CopyContents();
                    branchbb.Test.Eval(instructions, afterState, thenbb.Block.BeforeState, BottomPT);
                    var wdi = new WhileDoInstruction
                        (NextInstructionId--, new Instructions(beforeState, instructions), thenbb.Block)
                              {
                                  BeforeState = beforeState,
                                  AfterState = branchbb.Fallthrough.Block.BeforeState
                              };
                    var newbb = new JumpBasicBlock(nextBlockId++, new Instructions(wdi), branchbb.Fallthrough);
                    root.Coalesce(branchbb, group, newbb);
                    return String.Format("while-do from B{0}", branchbb.Id);
                }
            }

            {
                // Flipped while-do
                var elsebb = branchbb.Fallthrough as JumpBasicBlock;
                var group = new Set<BasicBlock> { branchbb };
                if (elsebb != null && elsebb.Target.Equals(branchbb) && elsebb.Sources.Count == 1 && group.Add(elsebb) &&
                    !group.Contains(branchbb.Target))
                {
                    var instructions = branchbb.Block.CopyContents();
                    branchbb.Test.Negate().Eval
                        (instructions, afterState, elsebb.Block.BeforeState, BottomPT);
                    var wdi = new WhileDoInstruction
                        (NextInstructionId--, new Instructions(beforeState, instructions), elsebb.Block)
                              {
                                  BeforeState = beforeState,
                                  AfterState = branchbb.Target.Block.BeforeState
                              };
                    var newbb = new JumpBasicBlock(nextBlockId++, new Instructions(wdi), branchbb.Target);
                    root.Coalesce(branchbb, group, newbb);
                    return String.Format("while-do (flipped) from B{0}", branchbb.Id);
                }
            }

            {
                // Do-while
                var thenbb = branchbb.Target;
                if (thenbb.Equals(branchbb) && !branchbb.Fallthrough.Equals(branchbb))
                {
                    var instructions = branchbb.Block.CopyContents();
                    branchbb.Test.Eval(instructions, afterState, beforeState, BottomPT);
                    var cond = SeparateCondition(beforeState, ref instructions);
                    var dwi = new DoWhileInstruction(NextInstructionId--, new Instructions(beforeState, instructions), cond)
                              {
                                  BeforeState = beforeState,
                                  AfterState = branchbb.Fallthrough.Block.BeforeState
                              };
                    var newbb = new JumpBasicBlock(nextBlockId++, new Instructions(dwi), branchbb.Fallthrough);
                    root.Coalesce(branchbb, new Set<BasicBlock> { branchbb }, newbb);
                    return String.Format("do-while from B{0}", branchbb.Id);
                }
            }

            {
                // Flipped Do-while
                var elsebb = branchbb.Fallthrough;
                if (elsebb.Equals(branchbb) && !branchbb.Target.Equals(branchbb))
                {
                    var instructions = branchbb.Block.CopyContents();
                    branchbb.Test.Negate().Eval
                        (instructions, afterState, beforeState, BottomPT);
                    var cond = SeparateCondition(beforeState, ref instructions);
                    var dwi = new DoWhileInstruction(NextInstructionId--, new Instructions(beforeState, instructions), cond)
                              {
                                  BeforeState = beforeState,
                                  AfterState = branchbb.Target.Block.BeforeState
                              };
                    var newbb = new JumpBasicBlock(nextBlockId++, new Instructions(dwi), branchbb.Target);
                    root.Coalesce(branchbb, new Set<BasicBlock> { branchbb }, newbb);
                    return String.Format("do-while (flipped) from B{0}", branchbb.Id);
                }
            }

            {
                // If-then, converging control
                var thenbb = branchbb.Target as JumpBasicBlock;
                if (thenbb != null && thenbb.Target.Equals(branchbb.Fallthrough) && thenbb.Sources.Count == 1)
                {
                    var instructions = branchbb.Block.CopyContents();
                    branchbb.Test.Eval(instructions, afterState, thenbb.Block.BeforeState, BottomPT);
                    var cond = SeparateCondition(beforeState, ref instructions);
                    var itei = new IfThenElseInstruction(NextInstructionId--, cond, thenbb.Block, null)
                               { BeforeState = beforeState, AfterState = thenbb.Block.AfterState };
                    instructions.Add(itei);
                    var newbb = thenbb.CloneWithInstructions
                        (nextBlockId++, new Instructions(beforeState, instructions));
                    root.Coalesce(branchbb, new Set<BasicBlock> { branchbb, thenbb }, newbb);
                    return String.Format("if-then with converging control paths from B{0}", branchbb.Id);
                }
            }

            {
                // Flipped if-then, converging control
                var elsebb = branchbb.Fallthrough as JumpBasicBlock;
                if (elsebb != null && elsebb.Target.Equals(branchbb.Target) && elsebb.Sources.Count == 1)
                {
                    var instructions = branchbb.Block.CopyContents();
                    branchbb.Test.Negate().Eval
                        (instructions, afterState, elsebb.Block.BeforeState, BottomPT);
                    var cond = SeparateCondition(beforeState, ref instructions);
                    var itei = new IfThenElseInstruction(NextInstructionId--, cond, elsebb.Block, null)
                               { BeforeState = beforeState, AfterState = elsebb.Block.AfterState };
                    instructions.Add(itei);
                    var newbb = elsebb.CloneWithInstructions
                        (nextBlockId++, new Instructions(beforeState, instructions));
                    root.Coalesce(branchbb, new Set<BasicBlock> { branchbb, elsebb }, newbb);
                    return String.Format("if-then (flipped) with converging control paths from B{0}", branchbb.Id);
                }
            }

            {
                // Short-circuited and/or "expression"
                var thenbb = branchbb.Target as JumpBasicBlock;
                var elsebb = branchbb.Fallthrough as JumpBasicBlock;
                var group = new Set<BasicBlock> { branchbb };
                if (thenbb != null && elsebb != null && thenbb.Target.Equals(elsebb.Target) &&
                    elsebb.Sources.Count == 1 && group.Add(elsebb) && !group.Contains(thenbb) &&
                    !group.Contains(thenbb.Target) && !thenbb.Target.Equals(thenbb) &&
                    IsLoadBooleanBlock(thenbb.Block))
                {
                    var instructions = branchbb.Block.CopyContents();
                    var test = IsLoadTrueBooleanBlock(thenbb.Block) ? branchbb.Test : branchbb.Test.Negate();
                    test.Eval(instructions, afterState, thenbb.Block.BeforeState, BottomPT);
                    var left = SeparateCondition(beforeState, ref instructions);
                    var op = IsLoadTrueBooleanBlock(thenbb.Block) ? ShortCircuitingOp.Or : ShortCircuitingOp.And;
                    var sci = new ShortCircuitingInstruction(NextInstructionId--, left, op, elsebb.Block)
                              { BeforeState = beforeState, AfterState = elsebb.Block.AfterState };
                    instructions.Add(sci);
                    var newbb = new JumpBasicBlock
                        (nextBlockId++, new Instructions(beforeState, instructions), elsebb.Target);
                    if (thenbb.Sources.Count == 1)
                        group.Add(thenbb);
                    root.Coalesce(branchbb, group, newbb);
                    return String.Format("short-circuited and/or expression from B{0}", branchbb.Id);
                }
            }

            {
                // Flipped short-circuited and/or "expression"
                var thenbb = branchbb.Target as JumpBasicBlock;
                var elsebb = branchbb.Fallthrough as JumpBasicBlock;
                var group = new Set<BasicBlock> { branchbb };
                if (thenbb != null && elsebb != null && thenbb.Target.Equals(elsebb.Target) &&
                    thenbb.Sources.Count == 1 && group.Add(thenbb) && !group.Contains(elsebb) &&
                    !group.Contains(elsebb.Target) && !elsebb.Target.Equals(elsebb) &&
                    IsLoadBooleanBlock(elsebb.Block))
                {
                    var instructions = branchbb.Block.CopyContents();
                    var test = IsLoadTrueBooleanBlock(elsebb.Block) ? branchbb.Test.Negate() : branchbb.Test;
                    test.Eval(instructions, afterState, thenbb.Block.BeforeState, BottomPT);
                    var left = SeparateCondition(beforeState, ref instructions);
                    var op = IsLoadTrueBooleanBlock(elsebb.Block) ? ShortCircuitingOp.Or : ShortCircuitingOp.And;
                    var sci = new ShortCircuitingInstruction(NextInstructionId--, left, op, thenbb.Block)
                              { BeforeState = beforeState, AfterState = thenbb.Block.AfterState };
                    instructions.Add(sci);
                    var newbb = new JumpBasicBlock
                        (nextBlockId++, new Instructions(beforeState, instructions), thenbb.Target);
                    if (elsebb.Sources.Count == 1)
                        group.Add(elsebb);
                    root.Coalesce(branchbb, group, newbb);
                    return String.Format("short-circuited (flipped) and/or expression from B{0}", branchbb.Id);
                }
            }

            {
                // Short-circuited and/or control flow
                var group = new Set<BasicBlock> { branchbb };
                var thenbb = branchbb.Target as BranchBasicBlock;
                if (thenbb != null && branchbb.Fallthrough.Equals(thenbb.Fallthrough) && thenbb.Sources.Count == 1 &&
                    group.Add(thenbb) && !group.Contains(thenbb.Target) && !group.Contains(branchbb.Fallthrough))
                {
                    var instructions = branchbb.Block.CopyContents();
                    branchbb.Test.Eval(instructions, afterState, thenbb.Block.BeforeState, BottomPT);
                    var left = SeparateCondition(beforeState, ref instructions);
                    var instructions2 = thenbb.Block.CopyContents();
                    var afterState2 = thenbb.Test.Eval
                        (instructions2, thenbb.Block.AfterState, thenbb.Target.Block.BeforeState, BottomPT);
                    var right = new Instructions(thenbb.Block.BeforeState, instructions2);
                    var sci = new ShortCircuitingInstruction(NextInstructionId--, left, ShortCircuitingOp.And, right)
                              { BeforeState = beforeState, AfterState = afterState2 };
                    instructions.Add(sci);
                    var newbb = new BranchBasicBlock
                        (nextBlockId++,
                         new Instructions(beforeState, instructions),
                         new Test(TestOp.True, false, methEnv.Global.Int32Ref))
                                { Target = thenbb.Target, Fallthrough = thenbb.Fallthrough };
                    root.Coalesce(branchbb, group, newbb);
                    return String.Format
                        ("short-circuited (normal, normal) and/or control flow from B{0}", branchbb.Id);
                }
            }

            {
                // Short-circuited and/or control flow, flipped first
                var group = new Set<BasicBlock> { branchbb };
                var elsebb = branchbb.Fallthrough as BranchBasicBlock;
                if (elsebb != null && branchbb.Target.Equals(elsebb.Fallthrough) && elsebb.Sources.Count == 1 &&
                    group.Add(elsebb) && !group.Contains(elsebb.Target) && !group.Contains(branchbb.Target))
                {
                    var instructions = branchbb.Block.CopyContents();
                    branchbb.Test.Negate().Eval
                        (instructions, afterState, elsebb.Block.BeforeState, BottomPT);
                    var left = SeparateCondition(beforeState, ref instructions);
                    var instructions2 = elsebb.Block.CopyContents();
                    var afterState2 = elsebb.Test.Eval
                        (instructions2, elsebb.Block.AfterState, elsebb.Target.Block.BeforeState, BottomPT);
                    var right = new Instructions(elsebb.Block.BeforeState, instructions2);
                    var sci = new ShortCircuitingInstruction(NextInstructionId--, left, ShortCircuitingOp.And, right)
                              { BeforeState = beforeState, AfterState = afterState2 };
                    instructions.Add(sci);
                    var newbb = new BranchBasicBlock
                        (nextBlockId++,
                         new Instructions(beforeState, instructions),
                         new Test(TestOp.True, false, methEnv.Global.Int32Ref))
                                { Target = elsebb.Target, Fallthrough = branchbb.Target };
                    root.Coalesce(branchbb, group, newbb);
                    return String.Format
                        ("short-circuited  (flipped, normal) and/or control flow from B{0}", branchbb.Id);
                }
            }

            {
                // Short-circuited and/or control flow, flipped second
                var group = new Set<BasicBlock> { branchbb };
                var thenbb = branchbb.Target as BranchBasicBlock;
                if (thenbb != null && branchbb.Fallthrough.Equals(thenbb.Target) && thenbb.Sources.Count == 1 &&
                    group.Add(thenbb) && !group.Contains(thenbb.Fallthrough) && !group.Contains(branchbb.Fallthrough))
                {
                    var instructions = branchbb.Block.CopyContents();
                    branchbb.Test.Eval(instructions, afterState, thenbb.Block.BeforeState, BottomPT);
                    var left = SeparateCondition(beforeState, ref instructions);
                    var instructions2 = thenbb.Block.CopyContents();
                    var afterState2 = thenbb.Test.Negate().Eval
                        (instructions2, thenbb.Block.AfterState, thenbb.Fallthrough.Block.BeforeState, BottomPT);
                    var right = new Instructions(thenbb.Block.BeforeState, instructions2);
                    var sci = new ShortCircuitingInstruction(NextInstructionId--, left, ShortCircuitingOp.And, right)
                              { BeforeState = beforeState, AfterState = afterState2 };
                    instructions.Add(sci);
                    var newbb = new BranchBasicBlock
                        (nextBlockId++,
                         new Instructions(beforeState, instructions),
                         new Test(TestOp.True, false, methEnv.Global.Int32Ref))
                                { Target = thenbb.Fallthrough, Fallthrough = branchbb.Fallthrough };
                    root.Coalesce(branchbb, group, newbb);
                    return String.Format
                        ("short-circuited  (normal, flipped) and/or control flow from B{0}", branchbb.Id);
                }
            }

            {
                // Short-circuited and/or control flow, flipped first and second
                var group = new Set<BasicBlock> { branchbb };
                var elsebb = branchbb.Fallthrough as BranchBasicBlock;
                if (elsebb != null && branchbb.Target.Equals(elsebb.Target) && elsebb.Sources.Count == 1 &&
                    group.Add(elsebb) && !group.Contains(elsebb.Fallthrough) && !group.Contains(branchbb.Target))
                {
                    var instructions = branchbb.Block.CopyContents();
                    branchbb.Test.Negate().Eval
                        (instructions, afterState, elsebb.Block.BeforeState, BottomPT);
                    var left = SeparateCondition(beforeState, ref instructions);
                    var instructions2 = elsebb.Block.CopyContents();
                    var afterState2 = elsebb.Test.Negate().Eval
                        (instructions2, elsebb.Block.AfterState, elsebb.Fallthrough.Block.BeforeState, BottomPT);
                    var right = new Instructions(elsebb.Block.BeforeState, instructions2);
                    var sci = new ShortCircuitingInstruction(NextInstructionId--, left, ShortCircuitingOp.And, right)
                              { BeforeState = beforeState, AfterState = afterState2 };
                    instructions.Add(sci);
                    var newbb = new BranchBasicBlock
                        (nextBlockId++,
                         new Instructions(beforeState, instructions),
                         new Test(TestOp.True, false, methEnv.Global.Int32Ref))
                                { Target = elsebb.Fallthrough, Fallthrough = branchbb.Target };
                    root.Coalesce(branchbb, group, newbb);
                    return String.Format
                        ("short-circuited  (flipped, flipped) and/or control flow from B{0}", branchbb.Id);
                }
            }

            {
                // If-then-else, neither side returns
                var thenbb = branchbb.Target as NonReturningBasicBlock;
                var elsebb = branchbb.Fallthrough as NonReturningBasicBlock;
                if (thenbb != null && elsebb != null && !branchbb.Equals(thenbb) && !branchbb.Equals(elsebb) &&
                    !thenbb.Equals(elsebb) && branchbb.Target.Sources.Count == 1 &&
                    branchbb.Fallthrough.Sources.Count == 1)
                {
                    var instructions = branchbb.Block.CopyContents();
                    if (thenbb.Block.Size <= elsebb.Block.Size)
                    {
                        branchbb.Test.Eval
                            (instructions, afterState, thenbb.Block.BeforeState, BottomPT);
                        var cond = SeparateCondition(beforeState, ref instructions);
                        var itei = new IfThenElseInstruction(NextInstructionId--, cond, thenbb.Block, null)
                                   { BeforeState = beforeState, AfterState = thenbb.Block.AfterState };
                        instructions.Add(itei);
                        var newbb = new JumpBasicBlock
                            (nextBlockId++, new Instructions(beforeState, instructions), elsebb);
                        root.Coalesce(branchbb, new Set<BasicBlock> { branchbb, thenbb }, newbb);
                        return String.Format("if-then-else, no return, then smaller, from B{0}", branchbb.Id);
                    }
                    else
                    {
                        branchbb.Test.Negate().Eval
                            (instructions, afterState, elsebb.Block.BeforeState, BottomPT);
                        var cond = SeparateCondition(beforeState, ref instructions);
                        var itei = new IfThenElseInstruction(NextInstructionId--, cond, elsebb.Block, null)
                                   { BeforeState = beforeState, AfterState = elsebb.Block.AfterState };
                        instructions.Add(itei);
                        var newbb = new JumpBasicBlock
                            (nextBlockId++, new Instructions(beforeState, instructions), thenbb);
                        root.Coalesce(branchbb, new Set<BasicBlock> { branchbb, elsebb }, newbb);
                        return String.Format("if-then-else, no return, else smaller, from B{0}", branchbb.Id);
                    }
                }
            }

            {
                // If-then, no-return
                var thenbb = branchbb.Target as NonReturningBasicBlock;
                if (thenbb != null && thenbb.Sources.Count == 1)
                {
                    var instructions = branchbb.Block.CopyContents();
                    branchbb.Test.Eval(instructions, afterState, thenbb.Block.BeforeState, BottomPT);
                    var cond = SeparateCondition(beforeState, ref instructions);
                    var itei = new IfThenElseInstruction(NextInstructionId--, cond, thenbb.Block, null)
                               { BeforeState = beforeState, AfterState = thenbb.Block.AfterState };
                    instructions.Add(itei);
                    var newbb = new JumpBasicBlock
                        (nextBlockId++, new Instructions(beforeState, instructions), branchbb.Fallthrough);
                    root.Coalesce(branchbb, new Set<BasicBlock> { branchbb, thenbb }, newbb);
                    return String.Format("if-then, no return, from B{0}", branchbb.Id);
                }
            }

            {
                // Flipped if-then, no-return
                var elsebb = branchbb.Fallthrough as NonReturningBasicBlock;
                if (elsebb != null && elsebb.Sources.Count == 1)
                {
                    var instructions = branchbb.Block.CopyContents();
                    branchbb.Test.Negate().Eval
                        (instructions, afterState, elsebb.Block.BeforeState, BottomPT);
                    var cond = SeparateCondition(beforeState, ref instructions);
                    var itei = new IfThenElseInstruction(NextInstructionId--, cond, elsebb.Block, null)
                               { BeforeState = beforeState, AfterState = elsebb.Block.AfterState };
                    instructions.Add(itei);
                    var newbb = new JumpBasicBlock
                        (nextBlockId++, new Instructions(beforeState, instructions), branchbb.Target);
                    root.Coalesce(branchbb, new Set<BasicBlock> { branchbb, elsebb }, newbb);
                    return String.Format("if-then (flipped), no return, from B{0}", branchbb.Id);
                }
            }

            {
                // If-then-else
                var group = new Set<BasicBlock> { branchbb };
                if (group.Add(branchbb.Target) && group.Add(branchbb.Fallthrough) &&
                    branchbb.Target.Sources.Count == 1 && branchbb.Fallthrough.Sources.Count == 1 &&
                    branchbb.Target.HasSameExit(branchbb.Fallthrough))
                {
                    var instructions = branchbb.Block.CopyContents();
                    branchbb.Test.Eval
                        (instructions, afterState, branchbb.Target.Block.BeforeState, BottomPT);
                    var cond = SeparateCondition(beforeState, ref instructions);
                    var itei = new IfThenElseInstruction
                        (NextInstructionId--, cond, branchbb.Target.Block, branchbb.Fallthrough.Block)
                               {
                                   BeforeState = beforeState,
                                   AfterState = branchbb.Target.Block.AfterState
                               };
                    instructions.Add(itei);
                    var newbb = branchbb.Fallthrough.CloneWithInstructions
                        (nextBlockId++, new Instructions(beforeState, instructions));
                    root.Coalesce(branchbb, group, newbb);
                    return String.Format("if-then-else from B{0}", branchbb.Id);
                }
            }

            return null;
        }
Пример #3
0
        private BasicBlock BasicBlockFromIndex(int start, Context context)
        {
            if (start >= context.Block.Count)
                throw new InvalidOperationException("run off of end of instructions");

            var offset = context.Block[start].Offset;
            var i = start;

            Func<int, Instructions> extractInclusive = j => context.Block.Peephole(start, j - start + 1, tracer);
            Func<int, Instructions> extractExclusive = j => context.Block.Peephole(start, j - start, tracer);

            while (i < context.Block.Count)
            {
                var instruction = context.Block[i];

                if (i > start && targets.Contains(instruction.Offset))
                {
                    var jbb = new JumpBasicBlock(nextBlockId++, extractExclusive(i));
                    context.OffsetToBlock.Add(offset, jbb);
                    jbb.Target = BasicBlockFromLocalTarget(instruction.Offset, context);
                    jbb.Target.Sources.Add(jbb);
                    return jbb;
                }
                else
                {
                    switch (instruction.Flavor)
                    {
                    case InstructionFlavor.Misc:
                        {
                            var misci = (MiscInstruction)instruction;
                            switch (misci.Op)
                            {
                            case MiscOp.Throw:
                                {
                                    var nrbb = new NonReturningBasicBlock(nextBlockId++, extractInclusive(i));
                                    context.OffsetToBlock.Add(offset, nrbb);
                                    return nrbb;
                                }
                            case MiscOp.Rethrow:
                                {
                                    var handlerContext = context as HandlerContext;
                                    if (handlerContext == null ||
                                        !(handlerContext.ILHandler is CatchTryInstructionHandler))
                                        throw new InvalidOperationException("rethrow not within catch");
                                    var nrbb = new NonReturningBasicBlock(nextBlockId++, extractInclusive(i));
                                    context.OffsetToBlock.Add(offset, nrbb);
                                    return nrbb;
                                }
                            case MiscOp.Ret:
                            case MiscOp.RetVal:
                                {
                                    if (context is TryContext || context is HandlerContext)
                                        throw new InvalidOperationException("return within a try or handler block");
                                    var nrbb = new NonReturningBasicBlock(nextBlockId++, extractInclusive(i));
                                    context.OffsetToBlock.Add(offset, nrbb);
                                    return nrbb;
                                }
                            case MiscOp.Endfinally:
                                {
                                    var handlerContext = context as HandlerContext;
                                    if (handlerContext == null)
                                        throw new InvalidOperationException
                                            ("endfinally not within fault/finally block");
                                    switch (handlerContext.ILHandler.Flavor)
                                    {
                                    case HandlerFlavor.Catch:
                                    case HandlerFlavor.Filter:
                                        throw new InvalidOperationException
                                            ("endfinally not within fault/finally block");
                                    case HandlerFlavor.Fault:
                                        {
                                            var efbb = new EndFaultBasicBlock
                                                (nextBlockId++,
                                                 extractExclusive(i),
                                                 (TBBFaultHandler)handlerContext.TBBHandler);
                                            context.OffsetToBlock.Add(offset, efbb);
                                            return efbb;
                                        }
                                    case HandlerFlavor.Finally:
                                        {
                                            var efbb = new EndFinallyBasicBlock
                                                (nextBlockId++,
                                                 extractExclusive(i),
                                                 (TBBFinallyHandler)handlerContext.TBBHandler,
                                                 misci.BeforeState.Depth);
                                            context.OffsetToBlock.Add(offset, efbb);
                                            return efbb;
                                        }
                                    default:
                                        throw new ArgumentOutOfRangeException();
                                    }
                                }
                            default:
                                break;
                            }
                            break;
                        }
                    case InstructionFlavor.Branch:
                        {
                            var bri = (BranchInstruction)instruction;
                            switch (bri.Op)
                            {
                            case BranchOp.Br:
                                {
                                    var jbb = new JumpBasicBlock(nextBlockId++, extractExclusive(i));
                                    context.OffsetToBlock.Add(offset, jbb);
                                    jbb.Target = BasicBlockFromLocalTarget(bri.Target, context);
                                    jbb.Target.Sources.Add(jbb);
                                    return jbb;
                                }
                            case BranchOp.Brtrue:
                            case BranchOp.Brfalse:
                            case BranchOp.Breq:
                            case BranchOp.Brne:
                            case BranchOp.BrLt:
                            case BranchOp.BrLe:
                            case BranchOp.BrGt:
                            case BranchOp.BrGe:
                                {
                                    if (i + 1 >= context.Block.Count)
                                        throw new InvalidOperationException("run off end of instructions");
                                    var bbb = new BranchBasicBlock
                                        (nextBlockId++,
                                         extractExclusive(i),
                                         Test.FromBranchOp(bri.Op, bri.IsUnsigned, bri.Type));
                                    context.OffsetToBlock.Add(offset, bbb);
                                    bbb.Target = BasicBlockFromLocalTarget(bri.Target, context);
                                    bbb.Target.Sources.Add(bbb);
                                    bbb.Fallthrough = BasicBlockFromLocalTarget(context.Block[i + 1].Offset, context);
                                    if (!bbb.Fallthrough.Equals(bbb.Target))
                                        bbb.Fallthrough.Sources.Add(bbb);
                                    return bbb;
                                }
                            case BranchOp.Leave:
                                {
                                    var handlerPopCount = default(int);
                                    var stackPopCount = bri.BeforeState.Depth;
                                    var leftContext = default(bool);
                                    var bb = BasicBlockFromTarget
                                        (bri.Target, context, out leftContext, out handlerPopCount);
                                    if (!leftContext)
                                    {
                                        // Not leaving try or handler block, so just empty stack and branch
                                        var lbb = new LeaveBasicBlock
                                            (nextBlockId++, extractExclusive(i), stackPopCount);
                                        context.OffsetToBlock.Add(offset, lbb);
                                        lbb.Target = bb;
                                        lbb.Target.Sources.Add(lbb);
                                        return lbb;
                                    }
                                    else
                                    {
                                        var tryContext = context as TryContext;
                                        if (tryContext != null)
                                        {
                                            // Poping at least one exception handler and branching
                                            var ltbb = new LeaveTryBasicBlock
                                                (nextBlockId++,
                                                 extractExclusive(i),
                                                 tryContext.TryBasicBlock,
                                                 handlerPopCount,
                                                 stackPopCount);
                                            context.OffsetToBlock.Add(offset, ltbb);
                                            ltbb.Target = bb;
                                            ltbb.Target.Sources.Add(ltbb);
                                            return ltbb;
                                        }
                                        else
                                        {
                                            var handlerContext = context as HandlerContext;
                                            if (handlerContext != null)
                                            {
                                                switch (handlerContext.ILHandler.Flavor)
                                                {
                                                case HandlerFlavor.Catch:
                                                    {
                                                        // Poping zero or more exception handlers and branching
                                                        var lcbb = new LeaveCatchBasicBlock
                                                            (nextBlockId++,
                                                             extractExclusive(i),
                                                             (TBBCatchHandler)handlerContext.TBBHandler,
                                                             handlerPopCount,
                                                             stackPopCount);
                                                        lcbb.Target = bb;
                                                        lcbb.Target.Sources.Add(lcbb);
                                                        return lcbb;
                                                    }
                                                case HandlerFlavor.Filter:
                                                    throw new NotSupportedException("filter");
                                                case HandlerFlavor.Fault:
                                                    throw new InvalidOperationException("leaving fault block");
                                                case HandlerFlavor.Finally:
                                                    throw new InvalidOperationException("leaving finally block");
                                                default:
                                                    throw new ArgumentOutOfRangeException();
                                                }
                                            }
                                            else
                                                throw new InvalidOperationException
                                                    ("no try or handler context to leave");
                                        }
                                    }
                                }
                            default:
                                throw new ArgumentOutOfRangeException();
                            }
                        }
                    case InstructionFlavor.Switch:
                        {
                            if (i + 1 >= context.Block.Count)
                                throw new InvalidOperationException("run off end of instructions");
                            var switchi = (SwitchInstruction)instruction;
                            var sbb = new SwitchBasicBlock(nextBlockId++, extractExclusive(i));
                            context.OffsetToBlock.Add(offset, sbb);
                            var seen = new Set<BasicBlock>();
                            foreach (var t in switchi.CaseTargets)
                            {
                                var target = BasicBlockFromLocalTarget(t, context);
                                sbb.CaseTargets.Add(target);
                                if (!seen.Contains(target))
                                {
                                    target.Sources.Add(sbb);
                                    seen.Add(target);
                                }
                            }
                            sbb.Fallthrough = BasicBlockFromLocalTarget(context.Block[i + 1].Offset, context);
                            if (!seen.Contains(sbb.Fallthrough))
                                sbb.Fallthrough.Sources.Add(sbb);
                            return sbb;
                        }
                    case InstructionFlavor.Try:
                        {
                            // Try is known to be a target, thus i == start and extract(i) would yield empty
                            var tryi = (TryInstruction)instruction;
                            var parent = default(TryBasicBlock);
                            var tryContext = context as TryContext;
                            if (tryContext != null)
                                parent = tryContext.TryBasicBlock;
                            else
                            {
                                var handlerContext = context as HandlerContext;
                                if (handlerContext != null)
                                    parent = handlerContext.TryContext.TryBasicBlock;
                            }
                            var tbb = new TryBasicBlock(nextBlockId++, parent, instruction.BeforeState);
                            context.OffsetToBlock.Add(offset, tbb);
                            var subTryContext = new TryContext(tryi.Body, context, tbb);
                            tbb.Body = BasicBlockFromInstructions(subTryContext);
                            tbb.Body.Sources.Add(tbb);
                            foreach (var ilHandler in tryi.Handlers)
                            {
                                var tbbHandler = default(TBBHandler);
                                switch (ilHandler.Flavor)
                                {
                                case HandlerFlavor.Catch:
                                    {
                                        var catchh = (CatchTryInstructionHandler)ilHandler;
                                        tbbHandler = new TBBCatchHandler(tbb, catchh.Type);
                                        break;
                                    }
                                case HandlerFlavor.Filter:
                                    throw new NotSupportedException("filter blocks");
                                case HandlerFlavor.Fault:
                                    {
                                        tbbHandler = new TBBFaultHandler(tbb);
                                        break;
                                    }
                                case HandlerFlavor.Finally:
                                    {
                                        tbbHandler = new TBBFinallyHandler(tbb);
                                        break;
                                    }
                                default:
                                    throw new ArgumentOutOfRangeException();
                                }
                                var subHandlerContext = new HandlerContext
                                    (ilHandler.Body, ilHandler, subTryContext, tbbHandler);
                                tbbHandler.Body = BasicBlockFromInstructions(subHandlerContext);
                                tbbHandler.Body.Sources.Add(tbb);
                                tbb.Handlers.Add(tbbHandler);
                            }
                            return tbb;
                        }
                    default:
                        break;
                    }
                    i++;
                }
            }
            throw new InvalidOperationException("ran off of end of instructions");
        }
Пример #4
0
        private string TryDuplicate(BasicBlock root, JumpBasicBlock jumpbb)
        {
            if (jumpbb.Target.Sources.Count > 1 && !jumpbb.Target.Equals(jumpbb) &&
                IsDuplicatableBasicBlock(jumpbb.Target))
            {
                // Inline and peephole
                var instructions = new Seq<Instruction>();
                // var block = new InstructionBlock(jumpbb.Block.BeforeState);
                jumpbb.Block.AccumClonedInstructions(instructions, ref NextInstructionId);
                jumpbb.Target.Block.AccumClonedInstructions(instructions, ref NextInstructionId);
                var newbb = jumpbb.Target.CloneWithInstructions
                    (nextBlockId++, Peephole(jumpbb.Block.BeforeState, instructions));
                jumpbb.Target.Sources.Remove(jumpbb);
                root.Coalesce(jumpbb, new Set<BasicBlock> { jumpbb }, newbb);
                return String.Format("inlined block B{0} into B{1}", jumpbb.Target.Id, jumpbb.Id);
            }

            return null;
        }
Пример #5
0
 private void BreakReduceJump(BasicBlock root, BBLoop loop, BasicBlock breakTarget, JumpBasicBlock jumpbb, Seq<BasicBlock> removed, Seq<BasicBlock> added)
 {
     if (jumpbb.Target.Equals(breakTarget))
     {
         var instructions = jumpbb.Block.CopyContents();
         var lci = new BreakContinueInstruction(NextInstructionId--, BreakContinueOp.Break, loop.Label)
                       { BeforeState = jumpbb.Block.AfterState, AfterState = jumpbb.Block.AfterState };
         instructions.Add(lci);
         var newbb = new NonReturningBasicBlock(nextBlockId++, new Instructions(jumpbb.Block.BeforeState, instructions));
         root.Coalesce(jumpbb, new Set<BasicBlock> { jumpbb }, newbb);
         removed.Add(jumpbb);
         added.Add(newbb);
     }
 }
Пример #6
0
 private void BreakReduceBranch(BasicBlock root, BBLoop loop, BasicBlock breakTarget, BranchBasicBlock branchbb, Seq<BasicBlock> removed, Seq<BasicBlock> added)
 {
     var beforeState = branchbb.Block.BeforeState;
     var afterState = branchbb.Block.AfterState;
     if (branchbb.Target.Equals(breakTarget) && !branchbb.Fallthrough.Equals(breakTarget))
     {
         var instructions = branchbb.Block.CopyContents();
         branchbb.Test.Eval(instructions, afterState, branchbb.Target.Block.BeforeState, BottomPT);
         var cond = SeparateCondition(beforeState, ref instructions);
         var lci = new BreakContinueInstruction(NextInstructionId--, BreakContinueOp.Break, loop.Label)
                       { BeforeState = afterState, AfterState = afterState };
         var itei = new IfThenElseInstruction(NextInstructionId--, cond, new Instructions(lci), null)
                        { BeforeState = beforeState, AfterState = afterState };
         instructions.Add(itei);
         var newbb = new JumpBasicBlock(nextBlockId++, new Instructions(beforeState, instructions), branchbb.Fallthrough);
         root.Coalesce(branchbb, new Set<BasicBlock> { branchbb }, newbb);
         removed.Add(branchbb);
         added.Add(newbb);
     }
     else if (branchbb.Fallthrough.Equals(breakTarget) && !branchbb.Target.Equals(breakTarget))
     {
         var instructions = branchbb.Block.CopyContents();
         branchbb.Test.Negate().Eval
             (instructions, afterState, branchbb.Fallthrough.Block.BeforeState, BottomPT);
         var cond = SeparateCondition(beforeState, ref instructions);
         var lci = new BreakContinueInstruction(NextInstructionId--, BreakContinueOp.Break, loop.Label)
                       { BeforeState = afterState, AfterState = afterState };
         var itei = new IfThenElseInstruction(NextInstructionId--, cond, new Instructions(lci), null)
                        { BeforeState = beforeState, AfterState = afterState };
         instructions.Add(itei);
         var newbb = new JumpBasicBlock(nextBlockId++, new Instructions(beforeState, instructions), branchbb.Target);
         root.Coalesce(branchbb, new Set<BasicBlock> { branchbb }, newbb);
         removed.Add(branchbb);
         added.Add(newbb);
     }
 }
Пример #7
0
        private string ContinueReduceBranch(BasicBlock root, BBLoop loop, BranchBasicBlock branchbb)
        {
            var beforeState = branchbb.Block.BeforeState;
            var afterState = branchbb.Block.AfterState;
            if (branchbb.Target.Equals(loop.ContinueTarget) && !branchbb.Fallthrough.Equals(loop.ContinueTarget) &&
                loop.ContinueTarget.Sources.Count > 1)
            {
                var instructions = branchbb.Block.CopyContents();
                branchbb.Test.Eval
                    (instructions, afterState, branchbb.Target.Block.BeforeState, BottomPT);
                var cond = SeparateCondition(beforeState, ref instructions);
                var lci = new BreakContinueInstruction(NextInstructionId--, BreakContinueOp.Continue, loop.Label)
                          { BeforeState = afterState, AfterState = afterState };
                var ifei = new IfThenElseInstruction(-1, cond, new Instructions(lci), null)
                           { BeforeState = beforeState, AfterState = afterState };
                instructions.Add(ifei);
                var newbb = new JumpBasicBlock
                    (nextBlockId++, new Instructions(beforeState, instructions), branchbb.Fallthrough);
                root.Coalesce(branchbb, new Set<BasicBlock> { branchbb }, newbb);
                return String.Format("continue from branch at B{0} within loop from B{1}", branchbb.Id, loop.Head.Id);
            }

            if (branchbb.Fallthrough.Equals(loop.ContinueTarget) && !branchbb.Target.Equals(loop.ContinueTarget) &&
                loop.ContinueTarget.Sources.Count > 1)
            {
                var instructions = branchbb.Block.CopyContents();
                branchbb.Test.Negate().Eval
                    (instructions, afterState, branchbb.Fallthrough.Block.BeforeState, BottomPT);
                var cond = SeparateCondition(beforeState, ref instructions);
                var lci = new BreakContinueInstruction(NextInstructionId--, BreakContinueOp.Continue, loop.Label)
                              { BeforeState = afterState, AfterState = afterState };
                var ifei = new IfThenElseInstruction(NextInstructionId--, cond, new Instructions(lci), null)
                               { BeforeState = beforeState, AfterState = afterState };
                instructions.Add(ifei);
                var newbb = new JumpBasicBlock(nextBlockId++, new Instructions(beforeState, instructions), branchbb.Target);
                root.Coalesce(branchbb, new Set<BasicBlock> { branchbb }, newbb);
                return String.Format
                    ("continue from branch (flipped) at B{0} within loop from B{1}", branchbb.Id, loop.Head.Id);
            }

            return null;
        }
Пример #8
0
        private string ContinueReduceJump(BasicBlock root, BBLoop loop, JumpBasicBlock jumpbb)
        {
            if (jumpbb.Target.Equals(loop.ContinueTarget) && jumpbb.Block.AfterState.Depth == 0 &&
                loop.ContinueTarget.Sources.Count > 1)
            {
                var instructions = jumpbb.Block.CopyContents();
                var lci = new BreakContinueInstruction(NextInstructionId--, BreakContinueOp.Continue, loop.Label)
                          { BeforeState = jumpbb.Block.AfterState, AfterState = jumpbb.Block.AfterState };
                instructions.Add(lci);
                var newbb = new NonReturningBasicBlock
                    (nextBlockId++, new Instructions(jumpbb.Block.BeforeState, instructions));
                root.Coalesce(jumpbb, new Set<BasicBlock> { jumpbb }, newbb);
                return String.Format("continue from jump at B{0} within loop from B{1}", jumpbb.Id, loop.Head.Id);
            }

            return null;
        }
Пример #9
0
        private string TryReduceLoopCandidate(BasicBlock root, LoopCandidateBasicBlock loopbb)
        {
            var headbb = loopbb.Head;
            var jumpheadbb = headbb as JumpBasicBlock;
            var nonretheadbb = headbb as NonReturningBasicBlock;
            var group = new Set<BasicBlock> { loopbb };
            if ((jumpheadbb != null && jumpheadbb.Target.Equals(loopbb.Break) || nonretheadbb != null) &&
                group.Add(headbb) && !group.Contains(loopbb.Break))
            {
                var newbb = new JumpBasicBlock(nextBlockId++, headbb.Block, loopbb.Break);
                root.Coalesce(loopbb, group, newbb);
                return String.Format("loop with break from B{0}", loopbb.Id);
            }

            return null;
        }
Пример #10
0
        private string TryReduceTry(BasicBlock root, TryBasicBlock trybb)
        {
            var group = new Set<BasicBlock> { trybb };
            var leavetrybb = trybb.Body as LeaveTryBasicBlock;
            if (leavetrybb == null || leavetrybb.HandlerPopCount != 1)
                return null;
            group.Add(leavetrybb);

            var handlers = new Seq<TryInstructionHandler>();
            foreach (var tbbHandler in trybb.Handlers)
            {
                var catchh = tbbHandler as TBBCatchHandler;
                if (catchh != null)
                {
                    if (!(catchh.Body is NonReturningBasicBlock))
                    {
                        // If body is not already free of control flow, check if it can be made to fall
                        // through to after try
                        var leavecatchbb = catchh.Body as LeaveCatchBasicBlock;
                        if (leavecatchbb == null || leavecatchbb.HandlerPopCount != 1 ||
                            !leavecatchbb.Target.Equals(leavetrybb.Target))
                            return null;
                        if (!group.Add(leavecatchbb))
                            // Should never happen
                            return null;
                    }
                    // else: catch ends with a return, throw, rethrow, break or continue
                    // Catch body WILL NOT end with leave
                    handlers.Add(new CatchTryInstructionHandler(catchh.Type, catchh.Body.Block));
                }
                else
                {
                    var faulth = tbbHandler as TBBFaultHandler;
                    if (faulth != null)
                    {
                        // Body must be an end fault
                        var endfaultbb = faulth.Body as EndFaultBasicBlock;
                        if (endfaultbb == null)
                            return null;
                        if (!group.Add(endfaultbb))
                            // Should never happen
                            return null;
                        // Fault handler body WILL NOT end with endfinally/endfault
                        handlers.Add(new FaultTryInstructionHandler(endfaultbb.Block));
                    }
                    else
                    {
                        var finallyh = tbbHandler as TBBFinallyHandler;
                        if (finallyh != null)
                        {
                            // Body must be an end finally
                            var endfinallybb = finallyh.Body as EndFinallyBasicBlock;
                            if (endfinallybb == null)
                                return null;
                            if (!group.Add(endfinallybb))
                                // Should never happen
                                return null;
                            // Finally handler body WILL NOT end with endfinally/endfault
                            handlers.Add(new FinallyTryInstructionHandler(endfinallybb.Block));
                        }
                        else
                            throw new InvalidOperationException("unrecognized handler");
                    }
                }
            }

            // Try body WILL NOT end with leave
            var tryi = new TryInstruction(NextInstructionId--, leavetrybb.Block, handlers)
                       { BeforeState = trybb.Block.BeforeState, AfterState = leavetrybb.Block.AfterState };
            var newbb = new JumpBasicBlock(nextBlockId++, new Instructions(tryi), leavetrybb.Target);
            root.Coalesce(trybb, group, newbb);
            return String.Format("structural try/catch/finally/fault from B{0}", trybb.Id);
        }
Пример #11
0
        private string TryReduceSwitch(BasicBlock root, SwitchBasicBlock switchbb)
        {
            // Build a map from basic blocks to the cases which enter it.
            // Also collect some simple stats.
            var caseMap = new Map<BasicBlock, Set<int>> { { switchbb.Fallthrough, new Set<int> { -1 } } };
            var allNonReturning = switchbb.Fallthrough is NonReturningBasicBlock;
            var allHaveOneSource = switchbb.Fallthrough.Sources.Count == 1;
            var jumpTargets = new Set<BasicBlock>();
            var jumpbb = switchbb.Fallthrough as JumpBasicBlock;
            if (jumpbb != null)
                jumpTargets.Add(jumpbb.Target);
            for (var i = 0; i < switchbb.CaseTargets.Count; i++)
            {
                var t = switchbb.CaseTargets[i];
                if (!(t is NonReturningBasicBlock))
                    allNonReturning = false;
                if (t.Sources.Count != 1)
                    allHaveOneSource = false;
                jumpbb = t as JumpBasicBlock;
                if (jumpbb != null)
                    jumpTargets.Add(jumpbb.Target);
                var values = default(Set<int>);
                if (caseMap.TryGetValue(t, out values))
                    // Block is shared amongst switch cases
                    values.Add(i);
                else
                    caseMap.Add(t, new Set<int> { i });
            }

            if (caseMap.Count == 1)
            {
                // CASE 1: all switch cases go to the same block, so replace the switch with a pop and jump
                var instructions = switchbb.Block.CopyContents();
                instructions.Add
                    (new MiscInstruction(NextInstructionId--, MiscOp.Pop)
                     { BeforeState = switchbb.Block.BeforeState, AfterState = switchbb.Fallthrough.Block.BeforeState });
                var newbb = new JumpBasicBlock
                    (nextBlockId++, Peephole(switchbb.Block.BeforeState, instructions), switchbb.Fallthrough);
                root.Coalesce(switchbb, new Set<BasicBlock> { switchbb }, newbb);
                return string.Format("removed switch at B{0}", switchbb.Id);
            }

            if (allNonReturning && allHaveOneSource)
            {
                // CASE 2: all switch cases are non-returning and have one source, so move them all into
                //         a non-returing block with switch
                var group = new Set<BasicBlock> { switchbb };
                var cases = new Seq<StructuralCase>();
                foreach (var kv in caseMap)
                {
                    cases.Add(new StructuralCase(kv.Value, kv.Key.Block));
                    group.Add(kv.Key);
                }
                var ssi = new StructuralSwitchInstruction(NextInstructionId--, switchbb.Block, cases)
                              {
                                  BeforeState = switchbb.Block.BeforeState,
                                  AfterState = switchbb.Fallthrough.Block.AfterState
                              };
                var newbb = new NonReturningBasicBlock(nextBlockId++, new Instructions(ssi));
                root.Coalesce(switchbb, group, newbb);
                return String.Format("non-returning structural switch from B{0}", switchbb.Id);
            }

            if (jumpTargets.Count == 1)
            {
                var theJumpTarget = jumpTargets[0];
                var allIntermediateAreNonReturningOrJumpToTarget = true;
                foreach (var kv in caseMap)
                {
                    if (!kv.Key.Equals(theJumpTarget))
                    {
                        if (kv.Key.Sources.Count > 1 ||
                            !(kv.Key is NonReturningBasicBlock || kv.Key is JumpBasicBlock))
                            allIntermediateAreNonReturningOrJumpToTarget = false;
                    }
                }
                if (allIntermediateAreNonReturningOrJumpToTarget)
                {
                    // CASE 3: all switch cases either jump directly to the switch successor or go via jump block,
                    //         or don't return. Inline all the cases and place the switch in a jump to target block
                    var group = new Set<BasicBlock> { switchbb };
                    var cases = new Seq<StructuralCase>();
                    var afterState = default(MachineState);
                    foreach (var kv in caseMap)
                    {
                        var block = default(Instructions);
                        if (kv.Key.Equals(theJumpTarget))
                        {
                            var bci = new BreakContinueInstruction(NextInstructionId--, BreakContinueOp.Break, null)
                                          {
                                              BeforeState = theJumpTarget.Block.BeforeState,
                                              AfterState = theJumpTarget.Block.BeforeState
                                          };
                            // Fallthrough to final jump target
                            block = new Instructions(bci);
                        }
                        else
                        {
                            // Inline case block, and if not non-returning then fallthrough to final jump target
                            var instructions = kv.Key.Block.CopyContents();
                            if (!kv.Key.Block.NeverReturns)
                            {
                                var bci = new BreakContinueInstruction
                                    (NextInstructionId--, BreakContinueOp.Break, null)
                                          {
                                              BeforeState = kv.Key.Block.AfterState,
                                              AfterState = kv.Key.Block.AfterState
                                          };
                                instructions.Add(bci);
                            }
                            group.Add(kv.Key);
                            block = new Instructions(kv.Key.Block.BeforeState, instructions);
                        }
                        if (afterState == null)
                            afterState = block.AfterState;
                        cases.Add(new StructuralCase(kv.Value, block));
                    }
                    var ssi = new StructuralSwitchInstruction(NextInstructionId--, switchbb.Block, cases)
                                  {
                                      BeforeState = switchbb.Block.BeforeState,
                                      AfterState = afterState
                                  };
                    var newbb = new JumpBasicBlock(nextBlockId++, new Instructions(ssi), theJumpTarget);
                    root.Coalesce(switchbb, group, newbb);
                    return String.Format("structural switch from B{0}", switchbb.Id);
                }
            }

            return null;
        }