void AnalyzeMoveNext() { MethodDefinition moveNextMethod = enumeratorType.Methods.FirstOrDefault(m => m.Name == "MoveNext"); ILBlock ilMethod = CreateILAst(moveNextMethod); if (ilMethod.Body.Count == 0) { throw new SymbolicAnalysisFailedException(); } ILExpression lastReturnArg; if (!ilMethod.Body.Last().Match(ILCode.Ret, out lastReturnArg)) { throw new SymbolicAnalysisFailedException(); } // There are two possibilities: if (lastReturnArg.Code == ILCode.Ldloc) { // a) the compiler uses a variable for returns (in debug builds, or when there are try-finally blocks) returnVariable = (ILVariable)lastReturnArg.Operand; returnLabel = ilMethod.Body.ElementAtOrDefault(ilMethod.Body.Count - 2) as ILLabel; if (returnLabel == null) { throw new SymbolicAnalysisFailedException(); } } else { // b) the compiler directly returns constants returnVariable = null; returnLabel = null; // In this case, the last return must return false. if (lastReturnArg.Code != ILCode.Ldc_I4 || (int)lastReturnArg.Operand != 0) { throw new SymbolicAnalysisFailedException(); } } ILTryCatchBlock tryFaultBlock = ilMethod.Body[0] as ILTryCatchBlock; List <ILNode> body; int bodyLength; if (tryFaultBlock != null) { // there are try-finally blocks if (returnVariable == null) // in this case, we must use a return variable { throw new SymbolicAnalysisFailedException(); } // must be a try-fault block: if (tryFaultBlock.CatchBlocks.Count != 0 || tryFaultBlock.FinallyBlock != null || tryFaultBlock.FaultBlock == null) { throw new SymbolicAnalysisFailedException(); } ILBlock faultBlock = tryFaultBlock.FaultBlock; // Ensure the fault block contains the call to Dispose(). if (faultBlock.Body.Count != 2) { throw new SymbolicAnalysisFailedException(); } MethodReference disposeMethodRef; ILExpression disposeArg; if (!faultBlock.Body[0].Match(ILCode.Call, out disposeMethodRef, out disposeArg)) { throw new SymbolicAnalysisFailedException(); } if (GetMethodDefinition(disposeMethodRef) != disposeMethod || !disposeArg.MatchThis()) { throw new SymbolicAnalysisFailedException(); } if (!faultBlock.Body[1].Match(ILCode.Endfinally)) { throw new SymbolicAnalysisFailedException(); } body = tryFaultBlock.TryBlock.Body; bodyLength = body.Count; } else { // no try-finally blocks body = ilMethod.Body; if (returnVariable == null) { bodyLength = body.Count - 1; // all except for the return statement } else { bodyLength = body.Count - 2; // all except for the return label and statement } } // Now verify that the last instruction in the body is 'ret(false)' if (returnVariable != null) { // If we don't have a return variable, we already verified that above. // If we do have one, check for 'stloc(returnVariable, ldc.i4(0))' // Maybe might be a jump to the return label after the stloc: ILExpression leave = body.ElementAtOrDefault(bodyLength - 1) as ILExpression; if (leave != null && (leave.Code == ILCode.Br || leave.Code == ILCode.Leave) && leave.Operand == returnLabel) { bodyLength--; } ILExpression store0 = body.ElementAtOrDefault(bodyLength - 1) as ILExpression; if (store0 == null || store0.Code != ILCode.Stloc || store0.Operand != returnVariable) { throw new SymbolicAnalysisFailedException(); } if (store0.Arguments[0].Code != ILCode.Ldc_I4 || (int)store0.Arguments[0].Operand != 0) { throw new SymbolicAnalysisFailedException(); } bodyLength--; // don't conside the stloc instruction to be part of the body } // verify that the last element in the body is a label pointing to the 'ret(false)' returnFalseLabel = body.ElementAtOrDefault(bodyLength - 1) as ILLabel; if (returnFalseLabel == null) { throw new SymbolicAnalysisFailedException(); } var rangeAnalysis = new StateRangeAnalysis(body[0], StateRangeAnalysisMode.IteratorMoveNext, stateField); int pos = rangeAnalysis.AssignStateRanges(body, bodyLength); rangeAnalysis.EnsureLabelAtPos(body, ref pos, ref bodyLength); var labels = rangeAnalysis.CreateLabelRangeMapping(body, pos, bodyLength); ConvertBody(body, pos, bodyLength, labels); }
void ConvertBody(List <ILNode> body, int startPos, int bodyLength, List <KeyValuePair <ILLabel, StateRange> > labels) { newBody = new List <ILNode>(); newBody.Add(MakeGoTo(labels, 0)); List <SetState> stateChanges = new List <SetState>(); int currentState = -1; // Copy all instructions from the old body to newBody. for (int pos = startPos; pos < bodyLength; pos++) { ILExpression expr = body[pos] as ILExpression; if (expr != null && expr.Code == ILCode.Stfld && expr.Arguments[0].MatchThis()) { // Handle stores to 'state' or 'current' if (GetFieldDefinition(expr.Operand as FieldReference) == stateField) { if (expr.Arguments[1].Code != ILCode.Ldc_I4) { throw new SymbolicAnalysisFailedException(); } currentState = (int)expr.Arguments[1].Operand; stateChanges.Add(new SetState(newBody.Count, currentState)); } else if (GetFieldDefinition(expr.Operand as FieldReference) == currentField) { newBody.Add(new ILExpression(ILCode.YieldReturn, null, expr.Arguments[1])); } else { newBody.Add(body[pos]); } } else if (returnVariable != null && expr != null && expr.Code == ILCode.Stloc && expr.Operand == returnVariable) { // handle store+branch to the returnVariable ILExpression br = body.ElementAtOrDefault(++pos) as ILExpression; if (br == null || !(br.Code == ILCode.Br || br.Code == ILCode.Leave) || br.Operand != returnLabel || expr.Arguments[0].Code != ILCode.Ldc_I4) { throw new SymbolicAnalysisFailedException(); } int val = (int)expr.Arguments[0].Operand; if (val == 0) { newBody.Add(MakeGoTo(returnFalseLabel)); } else if (val == 1) { newBody.Add(MakeGoTo(labels, currentState)); } else { throw new SymbolicAnalysisFailedException(); } } else if (expr != null && expr.Code == ILCode.Ret) { if (expr.Arguments.Count != 1 || expr.Arguments[0].Code != ILCode.Ldc_I4) { throw new SymbolicAnalysisFailedException(); } // handle direct return (e.g. in release builds) int val = (int)expr.Arguments[0].Operand; if (val == 0) { newBody.Add(MakeGoTo(returnFalseLabel)); } else if (val == 1) { newBody.Add(MakeGoTo(labels, currentState)); } else { throw new SymbolicAnalysisFailedException(); } } else if (expr != null && expr.Code == ILCode.Call && expr.Arguments.Count == 1 && expr.Arguments[0].MatchThis()) { MethodDefinition method = GetMethodDefinition(expr.Operand as MethodReference); if (method == null) { throw new SymbolicAnalysisFailedException(); } Interval interval; if (method == disposeMethod) { // Explicit call to dispose is used for "yield break;" within the method. ILExpression br = body.ElementAtOrDefault(++pos) as ILExpression; if (br == null || !(br.Code == ILCode.Br || br.Code == ILCode.Leave) || br.Operand != returnFalseLabel) { throw new SymbolicAnalysisFailedException(); } newBody.Add(MakeGoTo(returnFalseLabel)); } else if (finallyMethodToStateInterval.TryGetValue(method, out interval)) { // Call to Finally-method int index = stateChanges.FindIndex(ss => ss.NewState >= interval.Start && ss.NewState <= interval.End); if (index < 0) { throw new SymbolicAnalysisFailedException(); } ILLabel label = new ILLabel(); label.Name = "JumpOutOfTryFinally" + interval.Start + "_" + interval.End; newBody.Add(new ILExpression(ILCode.Leave, label)); SetState stateChange = stateChanges[index]; // Move all instructions from stateChange.Pos to newBody.Count into a try-block stateChanges.RemoveRange(index, stateChanges.Count - index); // remove all state changes up to the one we found ILTryCatchBlock tryFinally = new ILTryCatchBlock(); tryFinally.TryBlock = new ILBlock(newBody.GetRange(stateChange.NewBodyPos, newBody.Count - stateChange.NewBodyPos)); newBody.RemoveRange(stateChange.NewBodyPos, newBody.Count - stateChange.NewBodyPos); // remove all nodes that we just moved into the try block tryFinally.CatchBlocks = new List <ILTryCatchBlock.CatchBlock>(); tryFinally.FinallyBlock = ConvertFinallyBlock(method); newBody.Add(tryFinally); newBody.Add(label); } } else { newBody.Add(body[pos]); } } newBody.Add(new ILExpression(ILCode.YieldBreak, null)); }
ControlFlowGraph BuildGraph(List <ILNode> nodes, ILLabel entryLabel) { int index = 0; List <ControlFlowNode> cfNodes = new List <ControlFlowNode>(); ControlFlowNode entryPoint = new ControlFlowNode(index++, 0, ControlFlowNodeType.EntryPoint); cfNodes.Add(entryPoint); ControlFlowNode regularExit = new ControlFlowNode(index++, -1, ControlFlowNodeType.RegularExit); cfNodes.Add(regularExit); ControlFlowNode exceptionalExit = new ControlFlowNode(index++, -1, ControlFlowNodeType.ExceptionalExit); cfNodes.Add(exceptionalExit); // Create graph nodes labelToCfNode = new Dictionary <ILLabel, ControlFlowNode>(); Dictionary <ILNode, ControlFlowNode> astNodeToCfNode = new Dictionary <ILNode, ControlFlowNode>(); foreach (ILBasicBlock node in nodes) { ControlFlowNode cfNode = new ControlFlowNode(index++, -1, ControlFlowNodeType.Normal); cfNodes.Add(cfNode); astNodeToCfNode[node] = cfNode; cfNode.UserData = node; // Find all contained labels foreach (ILLabel label in node.GetSelfAndChildrenRecursive <ILLabel>()) { labelToCfNode[label] = cfNode; } } // Entry endge ControlFlowNode entryNode = labelToCfNode[entryLabel]; ControlFlowEdge entryEdge = new ControlFlowEdge(entryPoint, entryNode, JumpType.Normal); entryPoint.Outgoing.Add(entryEdge); entryNode.Incoming.Add(entryEdge); // Create edges foreach (ILBasicBlock node in nodes) { ControlFlowNode source = astNodeToCfNode[node]; // Find all branches foreach (ILLabel target in node.GetSelfAndChildrenRecursive <ILExpression>(e => e.IsBranch()).SelectMany(e => e.GetBranchTargets())) { ControlFlowNode destination; // Labels which are out of out scope will not be in the collection // Insert self edge only if we are sure we are a loop if (labelToCfNode.TryGetValue(target, out destination) && (destination != source || target == node.Body.FirstOrDefault())) { ControlFlowEdge edge = new ControlFlowEdge(source, destination, JumpType.Normal); source.Outgoing.Add(edge); destination.Incoming.Add(edge); } } } return(new ControlFlowGraph(cfNodes.ToArray())); }
/// <summary> /// Get the first expression to be excecuted if the instruction pointer is at the start of the given node. /// Try blocks may not be entered in any way. If possible, the try block is returned as the node to be executed. /// </summary> ILNode Enter(ILNode node, HashSet <ILNode> visitedNodes) { if (node == null) { throw new ArgumentNullException(); } if (!visitedNodes.Add(node)) { return(null); // Infinite loop } ILLabel label = node as ILLabel; if (label != null) { return(Exit(label, visitedNodes)); } ILExpression expr = node as ILExpression; if (expr != null) { if (expr.Code == ILCode.Br || expr.Code == ILCode.Leave) { ILLabel target = (ILLabel)expr.Operand; // Early exit - same try-block if (GetParents(expr).OfType <ILTryCatchBlock>().FirstOrDefault() == GetParents(target).OfType <ILTryCatchBlock>().FirstOrDefault()) { return(Enter(target, visitedNodes)); } // Make sure we are not entering any try-block var srcTryBlocks = GetParents(expr).OfType <ILTryCatchBlock>().Reverse().ToList(); var dstTryBlocks = GetParents(target).OfType <ILTryCatchBlock>().Reverse().ToList(); // Skip blocks that we are already in int i = 0; while (i < srcTryBlocks.Count && i < dstTryBlocks.Count && srcTryBlocks[i] == dstTryBlocks[i]) { i++; } if (i == dstTryBlocks.Count) { return(Enter(target, visitedNodes)); } else { ILTryCatchBlock dstTryBlock = dstTryBlocks[i]; // Check that the goto points to the start ILTryCatchBlock current = dstTryBlock; while (current != null) { foreach (ILNode n in current.TryBlock.Body) { if (n is ILLabel) { if (n == target) { return(dstTryBlock); } } else if (!n.Match(ILCode.Nop)) { current = n as ILTryCatchBlock; break; } } } return(null); } } else if (expr.Code == ILCode.Nop) { return(Exit(expr, visitedNodes)); } else if (expr.Code == ILCode.LoopOrSwitchBreak) { ILNode breakBlock = GetParents(expr).First(n => n is ILWhileLoop || n is ILSwitch); return(Exit(breakBlock, new HashSet <ILNode>() { expr })); } else if (expr.Code == ILCode.LoopContinue) { ILNode continueBlock = GetParents(expr).First(n => n is ILWhileLoop); return(Enter(continueBlock, new HashSet <ILNode>() { expr })); } else { return(expr); } } ILBlock block = node as ILBlock; if (block != null) { if (block.EntryGoto != null) { return(Enter(block.EntryGoto, visitedNodes)); } else if (block.Body.Count > 0) { return(Enter(block.Body[0], visitedNodes)); } else { return(Exit(block, visitedNodes)); } } ILCondition cond = node as ILCondition; if (cond != null) { return(cond.Condition); } ILWhileLoop loop = node as ILWhileLoop; if (loop != null) { if (loop.Condition != null) { return(loop.Condition); } else { return(Enter(loop.BodyBlock, visitedNodes)); } } ILTryCatchBlock tryCatch = node as ILTryCatchBlock; if (tryCatch != null) { return(tryCatch); } ILSwitch ilSwitch = node as ILSwitch; if (ilSwitch != null) { return(ilSwitch.Condition); } throw new NotSupportedException(node.GetType().ToString()); }
List <ILNode> FindLoops(HashSet <ControlFlowNode> scope, ControlFlowNode entryPoint, bool excludeEntryPoint) { List <ILNode> result = new List <ILNode>(); // Do not modify entry data scope = new HashSet <ControlFlowNode>(scope); Queue <ControlFlowNode> agenda = new Queue <ControlFlowNode>(); agenda.Enqueue(entryPoint); while (agenda.Count > 0) { ControlFlowNode node = agenda.Dequeue(); // If the node is a loop header if (scope.Contains(node) && node.DominanceFrontier.Contains(node) && (node != entryPoint || !excludeEntryPoint)) { HashSet <ControlFlowNode> loopContents = FindLoopContent(scope, node); // If the first expression is a loop condition ILBasicBlock basicBlock = (ILBasicBlock)node.UserData; ILExpression condExpr; ILLabel trueLabel; ILLabel falseLabel; // It has to be just brtrue - any preceding code would introduce goto if (basicBlock.MatchSingleAndBr(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) { ControlFlowNode trueTarget; labelToCfNode.TryGetValue(trueLabel, out trueTarget); ControlFlowNode falseTarget; labelToCfNode.TryGetValue(falseLabel, out falseTarget); // If one point inside the loop and the other outside if ((!loopContents.Contains(trueTarget) && loopContents.Contains(falseTarget)) || (loopContents.Contains(trueTarget) && !loopContents.Contains(falseTarget))) { loopContents.RemoveOrThrow(node); scope.RemoveOrThrow(node); // If false means enter the loop if (loopContents.Contains(falseTarget) || falseTarget == node) { // Negate the condition condExpr = new ILExpression(ILCode.LogicNot, null, condExpr); ILLabel tmp = trueLabel; trueLabel = falseLabel; falseLabel = tmp; } ControlFlowNode postLoopTarget; labelToCfNode.TryGetValue(falseLabel, out postLoopTarget); if (postLoopTarget != null) { // Pull more nodes into the loop HashSet <ControlFlowNode> postLoopContents = FindDominatedNodes(scope, postLoopTarget); var pullIn = scope.Except(postLoopContents).Where(n => node.Dominates(n)); loopContents.UnionWith(pullIn); } // Use loop to implement the brtrue basicBlock.Body.RemoveTail(ILCode.Brtrue, ILCode.Br); basicBlock.Body.Add(new ILWhileLoop() { Condition = condExpr, BodyBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, trueLabel), Body = FindLoops(loopContents, node, false) } }); basicBlock.Body.Add(new ILExpression(ILCode.Br, falseLabel)); result.Add(basicBlock); scope.ExceptWith(loopContents); } } // Fallback method: while(true) if (scope.Contains(node)) { result.Add(new ILBasicBlock() { Body = new List <ILNode>() { new ILLabel() { Name = "Loop_" + (nextLabelIndex++) }, new ILWhileLoop() { BodyBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, (ILLabel)basicBlock.Body.First()), Body = FindLoops(loopContents, node, true) } }, }, }); scope.ExceptWith(loopContents); } } // Using the dominator tree should ensure we find the the widest loop first foreach (var child in node.DominatorTreeChildren) { agenda.Enqueue(child); } } // Add whatever is left foreach (var node in scope) { result.Add((ILNode)node.UserData); } scope.Clear(); return(result); }
List <ILNode> FindConditions(HashSet <ControlFlowNode> scope, ControlFlowNode entryNode) { List <ILNode> result = new List <ILNode>(); // Do not modify entry data scope = new HashSet <ControlFlowNode>(scope); Stack <ControlFlowNode> agenda = new Stack <ControlFlowNode>(); agenda.Push(entryNode); while (agenda.Count > 0) { ControlFlowNode node = agenda.Pop(); // Find a block that represents a simple condition if (scope.Contains(node)) { ILBasicBlock block = (ILBasicBlock)node.UserData; { // Switch ILLabel[] caseLabels; ILExpression switchArg; ILLabel fallLabel; if (block.MatchLastAndBr(ILCode.Switch, out caseLabels, out switchArg, out fallLabel)) { // Replace the switch code with ILSwitch ILSwitch ilSwitch = new ILSwitch() { Condition = switchArg }; block.Body.RemoveTail(ILCode.Switch, ILCode.Br); block.Body.Add(ilSwitch); block.Body.Add(new ILExpression(ILCode.Br, fallLabel)); result.Add(block); // Remove the item so that it is not picked up as content scope.RemoveOrThrow(node); // Find the switch offset int addValue = 0; List <ILExpression> subArgs; if (ilSwitch.Condition.Match(ILCode.Sub, out subArgs) && subArgs[1].Match(ILCode.Ldc_I4, out addValue)) { ilSwitch.Condition = subArgs[0]; } // Pull in code of cases ControlFlowNode fallTarget = null; labelToCfNode.TryGetValue(fallLabel, out fallTarget); HashSet <ControlFlowNode> frontiers = new HashSet <ControlFlowNode>(); if (fallTarget != null) { frontiers.UnionWith(fallTarget.DominanceFrontier.Except(new [] { fallTarget })); } foreach (ILLabel condLabel in caseLabels) { ControlFlowNode condTarget = null; labelToCfNode.TryGetValue(condLabel, out condTarget); if (condTarget != null) { frontiers.UnionWith(condTarget.DominanceFrontier.Except(new [] { condTarget })); } } for (int i = 0; i < caseLabels.Length; i++) { ILLabel condLabel = caseLabels[i]; // Find or create new case block ILSwitch.CaseBlock caseBlock = ilSwitch.CaseBlocks.FirstOrDefault(b => b.EntryGoto.Operand == condLabel); if (caseBlock == null) { caseBlock = new ILSwitch.CaseBlock() { Values = new List <int>(), EntryGoto = new ILExpression(ILCode.Br, condLabel) }; ilSwitch.CaseBlocks.Add(caseBlock); ControlFlowNode condTarget = null; labelToCfNode.TryGetValue(condLabel, out condTarget); if (condTarget != null && !frontiers.Contains(condTarget)) { HashSet <ControlFlowNode> content = FindDominatedNodes(scope, condTarget); scope.ExceptWith(content); caseBlock.Body.AddRange(FindConditions(content, condTarget)); // Add explicit break which should not be used by default, but the goto removal might decide to use it caseBlock.Body.Add(new ILBasicBlock() { Body = { new ILLabel() { Name = "SwitchBreak_" + (nextLabelIndex++) }, new ILExpression(ILCode.LoopOrSwitchBreak, null) } }); } } caseBlock.Values.Add(i + addValue); } // Heuristis to determine if we want to use fallthough as default case if (fallTarget != null && !frontiers.Contains(fallTarget)) { HashSet <ControlFlowNode> content = FindDominatedNodes(scope, fallTarget); if (content.Any()) { var caseBlock = new ILSwitch.CaseBlock() { EntryGoto = new ILExpression(ILCode.Br, fallLabel) }; ilSwitch.CaseBlocks.Add(caseBlock); block.Body.RemoveTail(ILCode.Br); scope.ExceptWith(content); caseBlock.Body.AddRange(FindConditions(content, fallTarget)); // Add explicit break which should not be used by default, but the goto removal might decide to use it caseBlock.Body.Add(new ILBasicBlock() { Body = { new ILLabel() { Name = "SwitchBreak_" + (nextLabelIndex++) }, new ILExpression(ILCode.LoopOrSwitchBreak, null) } }); } } } // Two-way branch ILExpression condExpr; ILLabel trueLabel; ILLabel falseLabel; if (block.MatchLastAndBr(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) { // Swap bodies since that seems to be the usual C# order ILLabel temp = trueLabel; trueLabel = falseLabel; falseLabel = temp; condExpr = new ILExpression(ILCode.LogicNot, null, condExpr); // Convert the brtrue to ILCondition ILCondition ilCond = new ILCondition() { Condition = condExpr, TrueBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, trueLabel) }, FalseBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, falseLabel) } }; block.Body.RemoveTail(ILCode.Brtrue, ILCode.Br); block.Body.Add(ilCond); result.Add(block); // Remove the item immediately so that it is not picked up as content scope.RemoveOrThrow(node); ControlFlowNode trueTarget = null; labelToCfNode.TryGetValue(trueLabel, out trueTarget); ControlFlowNode falseTarget = null; labelToCfNode.TryGetValue(falseLabel, out falseTarget); // Pull in the conditional code if (trueTarget != null && HasSingleEdgeEnteringBlock(trueTarget)) { HashSet <ControlFlowNode> content = FindDominatedNodes(scope, trueTarget); scope.ExceptWith(content); ilCond.TrueBlock.Body.AddRange(FindConditions(content, trueTarget)); } if (falseTarget != null && HasSingleEdgeEnteringBlock(falseTarget)) { HashSet <ControlFlowNode> content = FindDominatedNodes(scope, falseTarget); scope.ExceptWith(content); ilCond.FalseBlock.Body.AddRange(FindConditions(content, falseTarget)); } } } // Add the node now so that we have good ordering if (scope.Contains(node)) { result.Add((ILNode)node.UserData); scope.Remove(node); } } // depth-first traversal of dominator tree for (int i = node.DominatorTreeChildren.Count - 1; i >= 0; i--) { agenda.Push(node.DominatorTreeChildren[i]); } } // Add whatever is left foreach (var node in scope) { result.Add((ILNode)node.UserData); } return(result); }
void DuplicateReturnStatements(ILBlock method) { Dictionary <ILLabel, ILNode> nextSibling = new Dictionary <ILLabel, ILNode>(); // Build navigation data foreach (ILBlock block in method.GetSelfAndChildrenRecursive <ILBlock>()) { for (int i = 0; i < block.Body.Count - 1; i++) { ILLabel curr = block.Body[i] as ILLabel; if (curr != null) { nextSibling[curr] = block.Body[i + 1]; } } } // Duplicate returns foreach (ILBlock block in method.GetSelfAndChildrenRecursive <ILBlock>()) { for (int i = 0; i < block.Body.Count; i++) { ILLabel targetLabel; if (block.Body[i].Match(ILCode.Br, out targetLabel) || block.Body[i].Match(ILCode.Leave, out targetLabel)) { // Skip extra labels while (nextSibling.ContainsKey(targetLabel) && nextSibling[targetLabel] is ILLabel) { targetLabel = (ILLabel)nextSibling[targetLabel]; } // Inline return statement ILNode target; List <ILExpression> retArgs; if (nextSibling.TryGetValue(targetLabel, out target)) { if (target.Match(ILCode.Ret, out retArgs)) { ILVariable locVar; object constValue; if (retArgs.Count == 0) { block.Body[i] = new ILExpression(ILCode.Ret, null) { Original = new List <ILExpression> { (ILExpression)block.Body[i] } }; } else if (retArgs.Single().Match(ILCode.Ldloc, out locVar)) { block.Body[i] = new ILExpression(ILCode.Ret, null, new ILExpression(ILCode.Ldloc, locVar)) { Original = new List <ILExpression> { (ILExpression)block.Body[i] } }; } else if (retArgs.Single().Match(ILCode.Ldc_I4, out constValue)) { block.Body[i] = new ILExpression(ILCode.Ret, null, new ILExpression(ILCode.Ldc_I4, constValue)) { Original = new List <ILExpression> { (ILExpression)block.Body[i] } }; } } } else { if (method.Body.Count > 0 && method.Body.Last() == targetLabel) { // It exits the main method - so it is same as return; block.Body[i] = new ILExpression(ILCode.Ret, null); } } } } } }
/// <summary> /// Group input into a set of blocks that can be later arbitraliby schufled. /// The method adds necessary branches to make control flow between blocks /// explicit and thus order independent. /// </summary> void SplitToBasicBlocks(ILBlock block) { List <ILNode> basicBlocks = new List <ILNode>(); ILLabel entryLabel = block.Body.FirstOrDefault() as ILLabel ?? new ILLabel() { Name = "Block_" + (nextLabelIndex++) }; ILBasicBlock basicBlock = new ILBasicBlock(); basicBlocks.Add(basicBlock); basicBlock.Body.Add(entryLabel); block.EntryGoto = new ILExpression(ILCode.Br, entryLabel); if (block.Body.Count > 0) { if (block.Body[0] != entryLabel) { basicBlock.Body.Add(block.Body[0]); } for (int i = 1; i < block.Body.Count; i++) { ILNode lastNode = block.Body[i - 1]; ILNode currNode = block.Body[i]; // Start a new basic block if necessary if (currNode is ILLabel || currNode is ILTryCatchBlock || // Counts as label lastNode.IsConditionalControlFlow() || lastNode.IsUnconditionalControlFlow()) { // Try to reuse the label ILLabel label = currNode as ILLabel ?? new ILLabel() { Name = "Block_" + (nextLabelIndex++).ToString() }; // Terminate the last block if (!lastNode.IsUnconditionalControlFlow()) { // Explicit branch from one block to other basicBlock.Body.Add(new ILExpression(ILCode.Br, label)); } // Start the new block basicBlock = new ILBasicBlock(); basicBlocks.Add(basicBlock); basicBlock.Body.Add(label); // Add the node to the basic block if (currNode != label) { basicBlock.Body.Add(currNode); } } else { basicBlock.Body.Add(currNode); } } } block.Body = basicBlocks; return; }
/// <summary> /// Removes redundatant Br, Nop, Dup, Pop /// Ignore arguments of 'leave' /// </summary> /// <param name="method"></param> internal static void RemoveRedundantCode(ILBlock method) { Dictionary <ILLabel, int> labelRefCount = new Dictionary <ILLabel, int>(); foreach (ILLabel target in method.GetSelfAndChildrenRecursive <ILExpression>(e => e.IsBranch()).SelectMany(e => e.GetBranchTargets())) { labelRefCount[target] = labelRefCount.GetOrDefault(target) + 1; } foreach (ILBlock block in method.GetSelfAndChildrenRecursive <ILBlock>()) { List <ILNode> body = block.Body; List <ILNode> newBody = new List <ILNode>(body.Count); for (int i = 0; i < body.Count; i++) { ILLabel target; ILExpression popExpr; if (body[i].Match(ILCode.Br, out target) && i + 1 < body.Count && body[i + 1] == target) { // Ignore the branch if (labelRefCount[target] == 1) { i++; // Ignore the label as well } } else if (body[i].Match(ILCode.Nop)) { // Ignore nop } else if (body[i].Match(ILCode.Pop, out popExpr)) { ILVariable v; if (!popExpr.Match(ILCode.Ldloc, out v)) { throw new Exception("Pop should have just ldloc at this stage"); } // Best effort to move the ILRange to previous statement ILVariable prevVar; ILExpression prevExpr; if (i - 1 >= 0 && body[i - 1].Match(ILCode.Stloc, out prevVar, out prevExpr) && prevVar == v) { prevExpr.ILRanges.AddRange(((ILExpression)body[i]).ILRanges); } // Ignore pop } else { ILLabel label = body[i] as ILLabel; if (label != null) { if (labelRefCount.GetOrDefault(label) > 0) { newBody.Add(label); } } else { newBody.Add(body[i]); } } } block.Body = newBody; } // Ignore arguments of 'leave' foreach (ILExpression expr in method.GetSelfAndChildrenRecursive <ILExpression>(e => e.Code == ILCode.Leave)) { if (expr.Arguments.Any(arg => !arg.Match(ILCode.Ldloc))) { throw new Exception("Leave should have just ldloc at this stage"); } expr.Arguments.Clear(); } // 'dup' removal foreach (ILExpression expr in method.GetSelfAndChildrenRecursive <ILExpression>()) { for (int i = 0; i < expr.Arguments.Count; i++) { ILExpression child; if (expr.Arguments[i].Match(ILCode.Dup, out child)) { child.ILRanges.AddRange(expr.Arguments[i].ILRanges); expr.Arguments[i] = child; } } } }
public static bool MatchLastAndBr <T>(this ILBasicBlock bb, ILCode code, out T operand, out ILExpression arg, out ILLabel brLabel) { if (bb.Body.ElementAtOrDefault(bb.Body.Count - 2).Match(code, out operand, out arg) && bb.Body.LastOrDefault().Match(ILCode.Br, out brLabel)) { return(true); } operand = default(T); arg = null; brLabel = null; return(false); }
public static bool MatchSingleAndBr <T>(this ILBasicBlock bb, ILCode code, out T operand, out ILExpression arg, out ILLabel brLabel) { if (bb.Body.Count == 3 && bb.Body[0] is ILLabel && bb.Body[1].Match(code, out operand, out arg) && bb.Body[2].Match(ILCode.Br, out brLabel)) { return(true); } operand = default(T); arg = null; brLabel = null; return(false); }