private void ConnectBlock(BasicBlock block)
 {
     var cil = block.LastInstruction;
     switch (block.FlowControl) {
         case FlowControl.Branch:
         case FlowControl.Cond_Branch:
             ConnectBranch(block);
             break;
         case FlowControl.Call:
         case FlowControl.Next:
             if (cil.Next != null) {
                 this.graph.Connect(block, GetBlock(cil.Next));
             }
             break;
         case FlowControl.Return:
         case FlowControl.Throw:
             break;
         default:
             throw new NotSupportedException();
     }
 }
 private void RegisterBlock(BasicBlock block)
 {
     blocks.Add(block.FirstInstruction.Offset, block);
 }
 private BasicBlock MarkBlockStart(Instruction cil)
 {
     var block = GetBlock(cil);
     if (block == null) {
         block = new BasicBlock(this.body.Method);
         block.Instructions.Add(cil);
         RegisterBlock(block);
     }
     return block;
 }
 private BasicBlock[] GetBranchTargetsBlocks(Instruction cil)
 {
     var targets = GetBranchTargets(cil);
     var blocks = new BasicBlock[targets.Length];
     for (int i = 0; i < targets.Length; i++) {
         blocks[i] = GetBlock(targets[i]);
     }
     return blocks;
 }
        private bool WriteBasicBlock(BasicBlock block, List<CodeStatement> stmts)
        {
            foreach (var stmt in block.Statements) {
                AddStatement(stmts, stmt);
            }

            if (block.FlowControl == FlowControl.Return ||
                block.FlowControl == FlowControl.Throw) {

                var lastReturn = stmts.LastOrDefault() as CodeReturnStatement;
                if (lastReturn != null) {
                    var lastAssignment = lastReturn.Previous as CodeAssignStatement;
                    if (lastAssignment != null) {
                        var lhsVariable = lastAssignment.Left as CodeVariableReference;
                        var rhsVariable = lastReturn.Expression as CodeVariableReference;
                        if (lhsVariable != null && rhsVariable != null && lhsVariable.Variable == rhsVariable.Variable) {

                            lastReturn.Previous = lastAssignment.Previous;
                            stmts.Remove(lastAssignment);

                            lastReturn.Expression = lastAssignment.Right;
                        }
                    }
                }

                return true;
            }

            return false;
        }
        private bool WriteWhileLoop(BasicBlock block, Loop loop, List<CodeStatement> stmts, Context context)
        {
            var loopContext = context.NewLoop(loop);

            CodeWhileStatement whileStmt;
            if (block.Statements.Count > 1) {
                // emit a while(true) { $bb.stmts; if($condition) { $then_stmts; break; } ... }
                whileStmt = new CodeWhileStatement {
                    TestExpression = new CodePrimitiveExpression(true)
                };

                var conditional = new Conditional(this.graph, block);
                WriteIf(block, conditional, whileStmt.Statements, loopContext);
            }
            else {
                var last = (CodeExpressionStatement)block.Statements.Last();
                var test = last.Expression;

                if (block.ElseEdge == loop.Follow) {
                    test = test.Invert();
                }

                // emit a pre-tested loop
                whileStmt = new CodeWhileStatement {
                    TestExpression = test
                };
            }

            AddStatement(stmts, whileStmt);

            if (!loop.Tails.Contains(block)) {
                WriteLoopInner(block, loop, whileStmt.Statements, loopContext);
            }

            if (loop.Follow != null) {
                return WriteCode(null, (BasicBlock)loop.Follow, stmts, context);
            }
            return false;
        }
        private bool WriteTry(BasicBlock block, List<CodeStatement> stmts, Context context)
        {
            // find catch/finally blocks that match this try
            // write this block into the try
            // continue until we find the try follow, which is the first block that has a last != leave
            // if the try follow lands on a loop or conditional header, then use their follow instead
            // TODO: deal with nested trys
            var tryStmt = new CodeTryStatement();
            AddStatement(stmts, tryStmt);

            var tryContext = context.NewTry(block.Try);

            WriteCode(null, block, tryStmt.Try, tryContext);

            foreach (BasicBlock catchBlock in block.Try.Catches) {
                var catchType = catchBlock.ExceptionHandler.CatchType;
                var variable = Interpreter.CreateExceptionVariableReference(this.method.Definition, catchType);
                var catchClause = new CodeCatchClause {
                    Type = new CodeTypeReference(catchType),
                    Variable = variable
                };

                tryStmt.Catches.Add(catchClause);

                WriteCode(null, catchBlock, catchClause.Statements, tryContext);
            }

            if (block.Try.Finally != null) {
                WriteCode(null, (BasicBlock)block.Try.Finally, tryStmt.Finally, tryContext);
            }

            if (block.Try.Follow != null) {
                return WriteCode(block, (BasicBlock)block.Try.Follow, stmts, context);
            }

            return true;
        }
Exemple #8
0
        public void ProcessBlock(BasicBlock block)
        {
            this.block = block;

            if (this.block.ExceptionHandler != null &&
                this.block.ExceptionHandler.TryStart.Offset != this.block.FirstInstruction.Offset &&
                this.block.ExceptionHandler.HandlerType == ExceptionHandlerType.Catch) {
                Debug.Assert(!this.stack.Any());

                // this is a catch handler
                // the exception object is expected to be sitting on the top of the stack
                // on entry to the catch block
                var catchType = this.block.ExceptionHandler.CatchType;
                var catchTypeDef = catchType.Resolve();
                this.ExternalMethods.Add(catchTypeDef.Methods.Where(x => x.IsConstructor).First());
                var exception = CreateExceptionVariableReference(this.method, catchType);
                Push(exception);
            }

            foreach (var cil in block.Instructions) {
                HandleInstruction(cil);
            }

            // In debug builds, the compiler emits CIL that helps the debugger with locality,
            // but screws up the stack because we assume that each block defines a stack boundry.
            // That is, at the start and end of each block, the stack is assumed to be empty.
            // This is an attempt to have instructions that cause the stack to become empty be
            // consumed by the predecessors that need them in order to make this assumption true.
            // Another case occurs when the compiler decides to push a value in one block
            // and then pop it in another.
            // This new strategy involves creating temporary register variables that can
            // be used to transfer extra stack values from one block to another.
            if (this.stack.Any()) {
                StashStackExtras(block);
            }
        }
        private bool WriteRepeatLoop(BasicBlock block, Loop loop, List<CodeStatement> stmts, Context context)
        {
            var loopContext = context.NewLoop(loop);

            var last = (CodeExpressionStatement)block.Statements.Last();
            var test = last.Expression;
            CodeRepeatStatement repeatStmt = new CodeRepeatStatement {
                TestExpression = test
            };

            AddStatement(stmts, repeatStmt);

            if (!loop.Tails.Contains(block)) {
                WriteLoopInner(block, loop, repeatStmt.Statements, loopContext);
            }

            var preTestStmts = block.Statements.Take(block.Statements.Count - 1);
            repeatStmt.Statements.AddRange(preTestStmts);

            if (loop.Follow != null) {
                return WriteCode(null, (BasicBlock)loop.Follow, stmts, context);
            }
            return false;
        }
        private void WriteLoopInner(BasicBlock block, Loop loop, List<CodeStatement> stmts, Context context)
        {
            Node succ;
            if (block.Successors.Count > 1) {
                succ = block.ThenEdge;
                if (succ == loop.Follow) {
                    succ = block.ElseEdge;
                }
            }
            else {
                succ = block.Successors.First();
            }

            WriteCode(block, (BasicBlock)succ, stmts, context);
        }
 private bool WriteLoop(BasicBlock block, List<CodeStatement> stmts, Context context)
 {
     var loop = block.Loop;
     Debug.Assert(loop.Header == block);
     switch (loop.LoopType) {
         case LoopType.While:
             return WriteWhileLoop(block, loop, stmts, context);
         case LoopType.Repeat:
             return WriteRepeatLoop(block, loop, stmts, context);
         case LoopType.Endless:
             return WriteEndlessLoop(block, loop, stmts, context);
         default:
             throw new InvalidOperationException();
     }
 }
        private bool WriteIf(BasicBlock block, Conditional conditional, List<CodeStatement> stmts, Context context)
        {
            var preStmts = block.Statements.Take(block.Statements.Count - 1);
            AddManyStatements(stmts, preStmts);

            var last = (CodeExpressionStatement)block.Statements.Last();
            var ifStmt = new CodeIfStatement {
                Condition = last.Expression
            };

            AddStatement(stmts, ifStmt);

            bool emptyThen = false;
            // is there a follow?
            if (conditional.Follow != null && conditional.Follow != context.LoopFollow) {
                // process the THEN part
                bool isBreak = false;
                var succ = block.ThenEdge;
                if (succ.DfsTraversed != DfsTraversal.Alpha) {
                    if (succ != conditional.Follow) {
                        // THEN part
                        ifStmt.Condition = ifStmt.Condition.Invert();
                        isBreak = WriteCode(block, (BasicBlock)succ, ifStmt.TrueStatements, context.NewUntil(conditional.Follow));
                    }
                    else {
                        // empty THEN part => negate ELSE part
                        isBreak = WriteCode(block, (BasicBlock)block.ElseEdge, ifStmt.TrueStatements, context.NewUntil(conditional.Follow));
                        emptyThen = true;
                    }
                }

                // process the ELSE part
                succ = block.ElseEdge;
                if (succ.DfsTraversed != DfsTraversal.Alpha) {
                    if (succ != conditional.Follow) {
                        // ELSE part
                        List<CodeStatement> output;
                        if (isBreak)
                            output = stmts;
                        else
                            output = ifStmt.FalseStatements;
                        isBreak = WriteCode(block, (BasicBlock)succ, output, context.NewUntil(conditional.Follow));
                        if (block == conditional.Follow)
                            return isBreak;
                    }
                }
                else if (!emptyThen) {
                    // already visited => emit label
                    throw new InvalidOperationException();
                }

                // Continue with the follow
                return WriteCode(null, (BasicBlock)conditional.Follow, stmts, context);
            }
            else {
                // no follow => if..then..else
                ifStmt.Condition = ifStmt.Condition.Invert();
                var isBreak = WriteCode(block, (BasicBlock)block.ThenEdge, ifStmt.TrueStatements, context);
                var output = ifStmt.FalseStatements;
                if (isBreak)
                    output = stmts;
                return WriteCode(block, (BasicBlock)block.ElseEdge, output, context);
            }
        }
        private bool WriteEndlessLoop(BasicBlock block, Loop loop, List<CodeStatement> stmts, Context context)
        {
            var loopContext = context.NewLoop(loop);

            CodeWhileStatement whileStmt = new CodeWhileStatement {
                TestExpression = new CodePrimitiveExpression(true)
            };

            AddStatement(stmts, whileStmt);

            if (block.Successors.Count > 1) {
                var conditional = new Conditional(this.graph, block);
                WriteIf(block, conditional, whileStmt.Statements, loopContext);
            }
            else {
                WriteBasicBlock(block, whileStmt.Statements);
            }

            if (!loop.Tails.Contains(block)) {
                WriteLoopInner(block, loop, whileStmt.Statements, loopContext);
            }

            if (loop.Follow != null) {
                return WriteCode(null, (BasicBlock)loop.Follow, stmts, context);
            }
            return false;
        }
        private bool WriteCode(BasicBlock previous, BasicBlock block, List<CodeStatement> stmts, Context context)
        {
            if (block == context.LoopFollow) {
                AddStatement(stmts, new CodeBreakStatement());
                return true;
            }

            if (block == context.LoopHeader && (previous == null || !previous.IsLoopLatch)) {
                AddStatement(stmts, new CodeContinueStatement());
                return true;
            }

            if (context.IsUntil(block) ||
                block.DfsTraversed == DfsTraversal.Alpha) {
                return false;
            }

            if (block.IsTryHeader && context.Try != block.Try) {
                return WriteTry(block, stmts, context);
            }

            block.DfsTraversed = DfsTraversal.Alpha;

            if (block.IsLoopHeader) {
                return WriteLoop(block, stmts, context);
            }
            else if (block.IsMultiWay) {
                return WriteSwitch(block, stmts, context);
            }
            else if (block.Conditional != null) {
                return WriteIf(block, block.Conditional, stmts, context);
            }
            else {
                var isReturn = WriteBasicBlock(block, stmts);
                if (isReturn)
                    return true;

                var succ = (BasicBlock)block.Successors.FirstOrDefault();
                return WriteCode(block, succ, stmts, context);
            }
        }
        private bool WriteSwitch(BasicBlock block, List<CodeStatement> stmts, Context context)
        {
            AddManyStatements(stmts, block.Statements);
            var switchStmt = (CodeSwitchStatement)block.Statements.Last();

            var targets = (Instruction[])block.LastInstruction.Operand;
            var casesByOffset = new Dictionary<int, CodeCase>();
            var follow = block.Conditional.Follow;

            foreach (BasicBlock succ in block.Successors) {
                CodeCase codeCase = new CodeCase();
                if (succ.DfsTraversed != DfsTraversal.Alpha) {
                    var followContext = context;
                    if (follow != null) {
                        followContext = context.NewUntil(follow);
                    }
                    if (!WriteCode(block, succ, codeCase.Statements, followContext)) {
                        codeCase.Statements.Add(new CodeBreakStatement());
                    }
                }
                casesByOffset.Add(succ.BeginOffset, codeCase);
            }

            for (int i = 0; i < targets.Length; i++) {
                int offset = targets[i].Offset;
                var codeCase = casesByOffset[offset];
                codeCase.Expressions.Add(new CodePrimitiveExpression(i));
            }

            switchStmt.Cases.AddRange(casesByOffset.Values);

            if (follow != null) {
                return WriteCode(null, (BasicBlock)follow, stmts, context);
            }

            return false;
        }
 private void ConnectBranch(BasicBlock block)
 {
     var cil = block.LastInstruction;
     if (cil.IsMultiWay()) {
         var blocks = GetBranchTargetsBlocks(cil);
         if (cil.Next != null) {
             this.graph.Connect(block, GetBlock(cil.Next));
         }
         foreach (var target in blocks) {
             this.graph.Connect(block, target);
         }
     }
     else {
         var target = GetBranchTargetBlock(cil);
         if (block.FlowControl == FlowControl.Cond_Branch && cil.Next != null) {
             this.graph.Connect(block, GetBlock(cil.Next));
         }
         this.graph.Connect(block, target);
     }
 }
Exemple #17
0
 private void StashStackExtras(BasicBlock block)
 {
     var items = this.stack.Reverse();
     foreach (var item in items) {
         var lhs = block.PushStash(this.typeSystem, item);
         var stmt = new CodeAssignStatement(lhs, item.Expression);
         this.block.Statements.Insert(item.Index, stmt);
     }
     this.stack.Clear();
 }