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; }
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); } }
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(); }