using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using ICSharpCode.Decompiler.FlowAnalysis; using Mono.Cecil; using Mono.Cecil.Cil; using Mono.CSharp; namespace ICSharpCode.Decompiler.ILAst { public enum ILAstOptimizationStep { RemoveRedundantCode, ReduceBranchInstructionSet, InlineVariables, CopyPropagation, YieldReturn, SplitToMovableBlocks, TypeInference, PeepholeOptimizations, SimplifyShortCircuit, SimplifyTernaryOperator, SimplifyNullCoalescing, MoreSimplifyPasses, FindLoops, FindConditions, FlattenNestedMovableBlocks, GotoRemoval, DuplicateReturns, FlattenIfStatements, InlineVariables2, PeepholeTransforms, TypeInference2, None } public class ILAstOptimizer { int nextLabelIndex = 0; DecompilerContext context; TypeSystem typeSystem; public void Optimize(DecompilerContext context, ILBlock method, ILAstOptimizationStep abortBeforeStep = ILAstOptimizationStep.None) { this.context = context; this.typeSystem = context.CurrentMethod.Module.TypeSystem; if (abortBeforeStep == ILAstOptimizationStep.RemoveRedundantCode) return; RemoveRedundantCode(method); if (abortBeforeStep == ILAstOptimizationStep.ReduceBranchInstructionSet) return; foreach(ILBlock block in method.GetSelfAndChildrenRecursive()) { ReduceBranchInstructionSet(block); } // ReduceBranchInstructionSet runs before inlining because the non-aggressive inlining heuristic // looks at which type of instruction consumes the inlined variable. if (abortBeforeStep == ILAstOptimizationStep.InlineVariables) return; // Works better after simple goto removal because of the following debug pattern: stloc X; br Next; Next:; ldloc X ILInlining inlining1 = new ILInlining(method); inlining1.InlineAllVariables(); if (abortBeforeStep == ILAstOptimizationStep.CopyPropagation) return; inlining1.CopyPropagation(); if (abortBeforeStep == ILAstOptimizationStep.YieldReturn) return; YieldReturnDecompiler.Run(context, method); if (abortBeforeStep == ILAstOptimizationStep.SplitToMovableBlocks) return; foreach(ILBlock block in method.GetSelfAndChildrenRecursive()) { SplitToBasicBlocks(block); } if (abortBeforeStep == ILAstOptimizationStep.TypeInference) return; // Types are needed for the ternary operator optimization TypeAnalysis.Run(context, method); if (abortBeforeStep == ILAstOptimizationStep.PeepholeOptimizations) return; AnalyseLabels(method); foreach(ILBlock block in method.GetSelfAndChildrenRecursive()) { bool modified; do { modified = false; if (abortBeforeStep == ILAstOptimizationStep.SimplifyShortCircuit) return; modified |= block.RunPeepholeOptimization(TrySimplifyShortCircuit); if (abortBeforeStep == ILAstOptimizationStep.SimplifyTernaryOperator) return; modified |= block.RunPeepholeOptimization(TrySimplifyTernaryOperator); if (abortBeforeStep == ILAstOptimizationStep.SimplifyNullCoalescing) return; modified |= block.RunPeepholeOptimization(TrySimplifyNullCoalescing); if (abortBeforeStep == ILAstOptimizationStep.MoreSimplifyPasses) return; } while(modified); } if (abortBeforeStep == ILAstOptimizationStep.FindLoops) return; foreach(ILBlock block in method.GetSelfAndChildrenRecursive()) { new LoopsAndConditions(context).FindLoops(block); } if (abortBeforeStep == ILAstOptimizationStep.FindConditions) return; foreach(ILBlock block in method.GetSelfAndChildrenRecursive()) { new LoopsAndConditions(context).FindConditions(block); } if (abortBeforeStep == ILAstOptimizationStep.FlattenNestedMovableBlocks) return; FlattenBasicBlocks(method); if (abortBeforeStep == ILAstOptimizationStep.GotoRemoval) return; RemoveRedundantCode(method); new GotoRemoval().RemoveGotos(method); if (abortBeforeStep == ILAstOptimizationStep.DuplicateReturns) return; DuplicateReturnStatements(method); if (abortBeforeStep == ILAstOptimizationStep.FlattenIfStatements) return; FlattenIfStatements(method); if (abortBeforeStep == ILAstOptimizationStep.InlineVariables2) return; // The 2nd inlining pass is necessary because DuplicateReturns and the introduction of ternary operators // open up additional inlining possibilities. new ILInlining(method).InlineAllVariables(); TypeAnalysis.Reset(method); if (abortBeforeStep == ILAstOptimizationStep.PeepholeTransforms) return; PeepholeTransforms.Run(context, method); if (abortBeforeStep == ILAstOptimizationStep.TypeInference2) return; TypeAnalysis.Run(context, method); GotoRemoval.RemoveRedundantCode(method); // ReportUnassignedILRanges(method); } /// /// Removes redundatant Br, Nop, Dup, Pop /// /// void RemoveRedundantCode(ILBlock method) { Dictionary labelRefCount = new Dictionary(); foreach (ILLabel target in method.GetSelfAndChildrenRecursive(e => e.IsBranch()).SelectMany(e => e.GetBranchTargets())) { labelRefCount[target] = labelRefCount.GetOrDefault(target) + 1; } foreach(ILBlock block in method.GetSelfAndChildrenRecursive()) { List body = block.Body; List newBody = new List(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 { newBody.Add(body[i]); } } block.Body = newBody; } // 'dup' removal foreach (ILExpression expr in method.GetSelfAndChildrenRecursive()) { 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; } } } } /// /// Reduces the branch codes to just br and brtrue. /// Moves ILRanges to the branch argument /// void ReduceBranchInstructionSet(ILBlock block) { for (int i = 0; i < block.Body.Count; i++) { ILExpression expr = block.Body[i] as ILExpression; if (expr != null && expr.Prefixes == null) { switch(expr.Code) { case ILCode.Switch: case ILCode.Brtrue: expr.Arguments.Single().ILRanges.AddRange(expr.ILRanges); expr.ILRanges.Clear(); continue; case ILCode.__Brfalse: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.LogicNot, null, expr.Arguments.Single())); break; case ILCode.__Beq: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.Ceq, null, expr.Arguments)); break; case ILCode.__Bne_Un: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.LogicNot, null, new ILExpression(ILCode.Ceq, null, expr.Arguments))); break; case ILCode.__Bgt: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.Cgt, null, expr.Arguments)); break; case ILCode.__Bgt_Un: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.Cgt_Un, null, expr.Arguments)); break; case ILCode.__Ble: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.LogicNot, null, new ILExpression(ILCode.Cgt, null, expr.Arguments))); break; case ILCode.__Ble_Un: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.LogicNot, null, new ILExpression(ILCode.Cgt_Un, null, expr.Arguments))); break; case ILCode.__Blt: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.Clt, null, expr.Arguments)); break; case ILCode.__Blt_Un: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.Clt_Un, null, expr.Arguments)); break; case ILCode.__Bge: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.LogicNot, null, new ILExpression(ILCode.Clt, null, expr.Arguments))); break; case ILCode.__Bge_Un: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.LogicNot, null, new ILExpression(ILCode.Clt_Un, null, expr.Arguments))); break; default: continue; } ((ILExpression)block.Body[i]).Arguments.Single().ILRanges.AddRange(expr.ILRanges); } } } /// /// 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. /// void SplitToBasicBlocks(ILBlock block) { List basicBlocks = new List(); ILBasicBlock basicBlock = new ILBasicBlock() { EntryLabel = new ILLabel() { Name = "Block_" + (nextLabelIndex++) } }; basicBlocks.Add(basicBlock); block.EntryGoto = new ILExpression(ILCode.Br, basicBlock.EntryLabel); if (block.Body.Count > 0) { 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]; // Insert split if (currNode is ILLabel || lastNode is ILTryCatchBlock || currNode is ILTryCatchBlock || (lastNode is ILExpression) && ((ILExpression)lastNode).IsBranch() || (currNode is ILExpression) && (((ILExpression)currNode).IsBranch() && ((ILExpression)currNode).Code.CanFallThough() && basicBlock.Body.Count > 0)) { ILBasicBlock lastBlock = basicBlock; basicBlock = new ILBasicBlock(); basicBlocks.Add(basicBlock); if (currNode is ILLabel) { // Insert as entry label basicBlock.EntryLabel = (ILLabel)currNode; } else { basicBlock.EntryLabel = new ILLabel() { Name = "Block_" + (nextLabelIndex++) }; basicBlock.Body.Add(currNode); } // Explicit branch from one block to other // (unless the last expression was unconditional branch) if (!(lastNode is ILExpression) || ((ILExpression)lastNode).Code.CanFallThough()) { lastBlock.FallthoughGoto = new ILExpression(ILCode.Br, basicBlock.EntryLabel); } } else { basicBlock.Body.Add(currNode); } } } foreach (ILBasicBlock bb in basicBlocks) { if (bb.Body.Count > 0 && bb.Body.Last() is ILExpression && ((ILExpression)bb.Body.Last()).Code == ILCode.Br) { Debug.Assert(bb.FallthoughGoto == null); bb.FallthoughGoto = (ILExpression)bb.Body.Last(); bb.Body.RemoveAt(bb.Body.Count - 1); } } block.Body = basicBlocks; return; } Dictionary labelGlobalRefCount; Dictionary labelToBasicBlock; void AnalyseLabels(ILBlock method) { labelGlobalRefCount = new Dictionary(); foreach(ILLabel target in method.GetSelfAndChildrenRecursive(e => e.IsBranch()).SelectMany(e => e.GetBranchTargets())) { if (!labelGlobalRefCount.ContainsKey(target)) labelGlobalRefCount[target] = 0; labelGlobalRefCount[target]++; } labelToBasicBlock = new Dictionary(); foreach(ILBasicBlock bb in method.GetSelfAndChildrenRecursive()) { foreach(ILLabel label in bb.GetChildren().OfType()) { labelToBasicBlock[label] = bb; } } } // scope is modified if successful bool TrySimplifyTernaryOperator(List scope, ILBasicBlock head, int index) { Debug.Assert(scope.Contains(head)); ILExpression condExpr; ILLabel trueLabel; ILLabel falseLabel; ILVariable trueLocVar = null; ILExpression trueExpr; ILLabel trueFall; ILVariable falseLocVar = null; ILExpression falseExpr; ILLabel falseFall; object unused; if (head.Match(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel) && labelGlobalRefCount[trueLabel] == 1 && labelGlobalRefCount[falseLabel] == 1 && ((labelToBasicBlock[trueLabel].Match(ILCode.Stloc, out trueLocVar, out trueExpr, out trueFall) && labelToBasicBlock[falseLabel].Match(ILCode.Stloc, out falseLocVar, out falseExpr, out falseFall)) || (labelToBasicBlock[trueLabel].Match(ILCode.Ret, out unused, out trueExpr, out trueFall) && labelToBasicBlock[falseLabel].Match(ILCode.Ret, out unused, out falseExpr, out falseFall))) && trueLocVar == falseLocVar && trueFall == falseFall && scope.Contains(labelToBasicBlock[trueLabel]) && scope.Contains(labelToBasicBlock[falseLabel]) ) { ILCode opCode = trueLocVar != null ? ILCode.Stloc : ILCode.Ret; TypeReference retType = trueLocVar != null ? trueLocVar.Type : this.context.CurrentMethod.ReturnType; int leftBoolVal; int rightBoolVal; ILExpression newExpr; // a ? true:false is equivalent to a // a ? false:true is equivalent to !a // a ? true : b is equivalent to a || b // a ? b : true is equivalent to !a || b // a ? b : false is equivalent to a && b // a ? false : b is equivalent to !a && b if (retType == typeSystem.Boolean && trueExpr.Match(ILCode.Ldc_I4, out leftBoolVal) && falseExpr.Match(ILCode.Ldc_I4, out rightBoolVal) && ((leftBoolVal != 0 && rightBoolVal == 0) || (leftBoolVal == 0 && rightBoolVal != 0)) ) { // It can be expressed as trivilal expression if (leftBoolVal != 0) { newExpr = condExpr; } else { newExpr = new ILExpression(ILCode.LogicNot, null, condExpr); } } else if (retType == typeSystem.Boolean && trueExpr.Match(ILCode.Ldc_I4, out leftBoolVal)) { // It can be expressed as logical expression if (leftBoolVal != 0) { newExpr = new ILExpression(ILCode.LogicOr, null, condExpr, falseExpr); } else { newExpr = new ILExpression(ILCode.LogicAnd, null, new ILExpression(ILCode.LogicNot, null, condExpr), falseExpr); } } else if (retType == typeSystem.Boolean && falseExpr.Match(ILCode.Ldc_I4, out rightBoolVal)) { // It can be expressed as logical expression if (rightBoolVal != 0) { newExpr = new ILExpression(ILCode.LogicOr, null, new ILExpression(ILCode.LogicNot, null, condExpr), trueExpr); } else { newExpr = new ILExpression(ILCode.LogicAnd, null, condExpr, trueExpr); } } else { // Ternary operator tends to create long complicated return statements if (opCode == ILCode.Ret) return false; // Create ternary expression newExpr = new ILExpression(ILCode.TernaryOp, null, condExpr, trueExpr, falseExpr); } head.Body = new List() { new ILExpression(opCode, trueLocVar, newExpr) }; head.FallthoughGoto = trueFall != null ? new ILExpression(ILCode.Br, trueFall) : null; // Remove the old basic blocks foreach(ILLabel deleteLabel in new [] { trueLabel, falseLabel }) { scope.RemoveOrThrow(labelToBasicBlock[deleteLabel]); labelGlobalRefCount.RemoveOrThrow(deleteLabel); labelToBasicBlock.RemoveOrThrow(deleteLabel); } return true; } return false; } bool TrySimplifyNullCoalescing(List scope, ILBasicBlock head, int index) { // ... // v = ldloc(leftVar) // br(condBBLabel) // // condBBLabel: // brtrue(endBBLabel, ldloc(leftVar)) // br(rightBBLabel) // // rightBBLabel: // v = rightExpr // br(endBBLabel) // // endBBLabel: // ... ILVariable v, v2; ILExpression leftExpr, leftExpr2; ILVariable leftVar, leftVar2; ILLabel condBBLabel; ILBasicBlock condBB; ILLabel endBBLabel, endBBLabel2; ILLabel rightBBLabel; ILBasicBlock rightBB; ILExpression rightExpr; ILBasicBlock endBB; if (head.Body.LastOrDefault().Match(ILCode.Stloc, out v, out leftExpr) && leftExpr.Match(ILCode.Ldloc, out leftVar) && head.FallthoughGoto.Match(ILCode.Br, out condBBLabel) && labelToBasicBlock.TryGetValue(condBBLabel, out condBB) && condBB.Match(ILCode.Brtrue, out endBBLabel, out leftExpr2, out rightBBLabel) && leftExpr2.Match(ILCode.Ldloc, out leftVar2) && leftVar == leftVar2 && labelToBasicBlock.TryGetValue(rightBBLabel, out rightBB) && rightBB.Match(ILCode.Stloc, out v2, out rightExpr, out endBBLabel2) && v == v2 && endBBLabel == endBBLabel2 && labelToBasicBlock.TryGetValue(endBBLabel, out endBB) && labelGlobalRefCount.GetOrDefault(condBBLabel) == 1 && labelGlobalRefCount.GetOrDefault(rightBBLabel) == 1 && labelGlobalRefCount.GetOrDefault(endBBLabel) == 2 && scope.Contains(condBB) && scope.Contains(rightBB) && scope.Contains(endBB) ) { head.Body[head.Body.Count - 1] = new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.NullCoalescing, null, leftExpr, rightExpr)); head.FallthoughGoto = new ILExpression(ILCode.Br, endBBLabel); foreach(ILLabel deleteLabel in new [] { condBBLabel, rightBBLabel }) { scope.RemoveOrThrow(labelToBasicBlock[deleteLabel]); labelGlobalRefCount.RemoveOrThrow(deleteLabel); labelToBasicBlock.RemoveOrThrow(deleteLabel); } return true; } return false; } // scope is modified if successful bool TrySimplifyShortCircuit(List scope, ILBasicBlock head, int index) { Debug.Assert(scope.Contains(head)); ILExpression condExpr; ILLabel trueLabel; ILLabel falseLabel; if(head.Match(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) { for (int pass = 0; pass < 2; pass++) { // On the second pass, swap labels and negate expression of the first branch // It is slightly ugly, but much better then copy-pasting this whole block ILLabel nextLabel = (pass == 0) ? trueLabel : falseLabel; ILLabel otherLablel = (pass == 0) ? falseLabel : trueLabel; bool negate = (pass == 1); ILBasicBlock nextBasicBlock = labelToBasicBlock[nextLabel]; ILExpression nextCondExpr; ILLabel nextTrueLablel; ILLabel nextFalseLabel; if (scope.Contains(nextBasicBlock) && nextBasicBlock != head && labelGlobalRefCount[nextBasicBlock.EntryLabel] == 1 && nextBasicBlock.Match(ILCode.Brtrue, out nextTrueLablel, out nextCondExpr, out nextFalseLabel) && (otherLablel == nextFalseLabel || otherLablel == nextTrueLablel)) { // Create short cicuit branch if (otherLablel == nextFalseLabel) { head.Body[0] = new ILExpression(ILCode.Brtrue, nextTrueLablel, new ILExpression(ILCode.LogicAnd, null, negate ? new ILExpression(ILCode.LogicNot, null, condExpr) : condExpr, nextCondExpr)); } else { head.Body[0] = new ILExpression(ILCode.Brtrue, nextTrueLablel, new ILExpression(ILCode.LogicOr, null, negate ? condExpr : new ILExpression(ILCode.LogicNot, null, condExpr), nextCondExpr)); } head.FallthoughGoto = new ILExpression(ILCode.Br, nextFalseLabel); // Remove the inlined branch from scope labelGlobalRefCount.RemoveOrThrow(nextBasicBlock.EntryLabel); labelToBasicBlock.RemoveOrThrow(nextBasicBlock.EntryLabel); scope.RemoveOrThrow(nextBasicBlock); return true; } } } return false; } void DuplicateReturnStatements(ILBlock method) { Dictionary nextSibling = new Dictionary(); // Build navigation data foreach(ILBlock block in method.GetSelfAndChildrenRecursive()) { 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()) { 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 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); } else if (retArgs.Single().Match(ILCode.Ldloc, out locVar)) { block.Body[i] = new ILExpression(ILCode.Ret, null, new ILExpression(ILCode.Ldloc, locVar)); } else if (retArgs.Single().Match(ILCode.Ldc_I4, out constValue)) { block.Body[i] = new ILExpression(ILCode.Ret, null, new ILExpression(ILCode.Ldc_I4, constValue)); } } } 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); } } } } } } /// /// Flattens all nested basic blocks, except the the top level 'node' argument /// void FlattenBasicBlocks(ILNode node) { ILBlock block = node as ILBlock; if (block != null) { List flatBody = new List(); foreach (ILNode child in block.GetChildren()) { FlattenBasicBlocks(child); if (child is ILBasicBlock) { flatBody.AddRange(child.GetChildren()); } else { flatBody.Add(child); } } block.EntryGoto = null; block.Body = flatBody; } else if (node is ILExpression) { // Optimization - no need to check expressions } else if (node != null) { // Recursively find all ILBlocks foreach(ILNode child in node.GetChildren()) { FlattenBasicBlocks(child); } } } /// /// Reduce the nesting of conditions. /// It should be done on flat data that already had most gotos removed /// void FlattenIfStatements(ILNode node) { ILBlock block = node as ILBlock; if (block != null) { for (int i = 0; i < block.Body.Count; i++) { ILCondition cond = block.Body[i] as ILCondition; if (cond != null) { bool trueExits = cond.TrueBlock.Body.Count > 0 && !cond.TrueBlock.Body.Last().CanFallThough(); bool falseExits = cond.FalseBlock.Body.Count > 0 && !cond.FalseBlock.Body.Last().CanFallThough(); if (trueExits) { // Move the false block after the condition block.Body.InsertRange(i + 1, cond.FalseBlock.GetChildren()); cond.FalseBlock = new ILBlock(); } else if (falseExits) { // Move the true block after the condition block.Body.InsertRange(i + 1, cond.TrueBlock.GetChildren()); cond.TrueBlock = new ILBlock(); } // Eliminate empty true block if (!cond.TrueBlock.GetChildren().Any() && cond.FalseBlock.GetChildren().Any()) { // Swap bodies ILBlock tmp = cond.TrueBlock; cond.TrueBlock = cond.FalseBlock; cond.FalseBlock = tmp; cond.Condition = new ILExpression(ILCode.LogicNot, null, cond.Condition); } } } } // We are changing the number of blocks so we use plain old recursion to get all blocks foreach(ILNode child in node.GetChildren()) { if (child != null && !(child is ILExpression)) FlattenIfStatements(child); } } void ReportUnassignedILRanges(ILBlock method) { var unassigned = ILRange.Invert(method.GetSelfAndChildrenRecursive().SelectMany(e => e.ILRanges), context.CurrentMethod.Body.CodeSize).ToList(); if (unassigned.Count > 0) Debug.WriteLine(string.Format("Unassigned ILRanges for {0}.{1}: {2}", this.context.CurrentMethod.DeclaringType.Name, this.context.CurrentMethod.Name, string.Join(", ", unassigned.Select(r => r.ToString())))); } } public static class ILAstOptimizerExtensionMethods { /// /// Perform one pass of a given optimization on this block. /// This block must consist of only basicblocks. /// public static bool RunPeepholeOptimization(this ILBlock block, Func, ILBasicBlock, int, bool> optimization) { bool modified = false; List body = block.Body; for (int i = 0; i < body.Count;) { if (optimization(body, (ILBasicBlock)body[i], i)) { modified = true; i = Math.Max(0, i - 1); // Go back one step } else { i++; } } return modified; } public static bool CanFallThough(this ILNode node) { ILExpression expr = node as ILExpression; if (expr != null) { return expr.Code.CanFallThough(); } return true; } /// /// The expression has no effect on the program and can be removed /// if its return value is not needed. /// public static bool HasNoSideEffects(this ILExpression expr) { // Remember that if expression can throw an exception, it is a side effect switch(expr.Code) { case ILCode.Ldloc: case ILCode.Ldloca: case ILCode.Ldstr: case ILCode.Ldnull: case ILCode.Ldc_I4: case ILCode.Ldc_I8: case ILCode.Ldc_R4: case ILCode.Ldc_R8: return true; default: return false; } } /// /// Can the expression be used as a statement in C#? /// public static bool CanBeExpressionStatement(this ILExpression expr) { switch(expr.Code) { case ILCode.Call: case ILCode.Callvirt: // property getters can't be expression statements, but all other method calls can be MethodReference mr = (MethodReference)expr.Operand; return !mr.Name.StartsWith("get_", StringComparison.Ordinal); case ILCode.Newobj: case ILCode.Newarr: return true; default: return false; } } public static V GetOrDefault(this Dictionary dict, K key) { V ret; dict.TryGetValue(key, out ret); return ret; } public static void RemoveOrThrow(this ICollection collection, T item) { if (!collection.Remove(item)) throw new Exception("The item was not found in the collection"); } public static void RemoveOrThrow(this Dictionary collection, K key) { if (!collection.Remove(key)) throw new Exception("The key was not found in the dictionary"); } } }