diff options
Diffstat (limited to 'ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs')
-rw-r--r-- | ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs | 988 |
1 files changed, 0 insertions, 988 deletions
diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs deleted file mode 100644 index 876718bc..00000000 --- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ /dev/null @@ -1,988 +0,0 @@ -// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this -// software and associated documentation files (the "Software"), to deal in the Software -// without restriction, including without limitation the rights to use, copy, modify, merge, -// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons -// to whom the Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE -// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using ICSharpCode.Decompiler.FlowAnalysis; -using ICSharpCode.NRefactory.Utils; -using Mono.Cecil; -using Mono.Cecil.Cil; -using Mono.CSharp; - -namespace ICSharpCode.Decompiler.ILAst -{ - public enum ILAstOptimizationStep - { - RemoveRedundantCode, - ReduceBranchInstructionSet, - InlineVariables, - CopyPropagation, - YieldReturn, - AsyncAwait, - PropertyAccessInstructions, - SplitToMovableBlocks, - TypeInference, - HandlePointerArithmetic, - SimplifyShortCircuit, - SimplifyTernaryOperator, - SimplifyNullCoalescing, - JoinBasicBlocks, - SimplifyLogicNot, - SimplifyShiftOperators, - TypeConversionSimplifications, - SimplifyLdObjAndStObj, - SimplifyCustomShortCircuit, - SimplifyLiftedOperators, - TransformArrayInitializers, - TransformMultidimensionalArrayInitializers, - TransformObjectInitializers, - MakeAssignmentExpression, - IntroducePostIncrement, - InlineExpressionTreeParameterDeclarations, - InlineVariables2, - FindLoops, - FindConditions, - FlattenNestedMovableBlocks, - RemoveEndFinally, - RemoveRedundantCode2, - GotoRemoval, - DuplicateReturns, - GotoRemoval2, - ReduceIfNesting, - InlineVariables3, - CachedDelegateInitialization, - IntroduceFixedStatements, - RecombineVariables, - TypeInference2, - RemoveRedundantCode3, - None - } - - public partial class ILAstOptimizer - { - int nextLabelIndex = 0; - - DecompilerContext context; - TypeSystem typeSystem; - ILBlock method; - - public void Optimize(DecompilerContext context, ILBlock method, ILAstOptimizationStep abortBeforeStep = ILAstOptimizationStep.None) - { - this.context = context; - typeSystem = context.CurrentMethod.Module.TypeSystem; - this.method = method; - - if (abortBeforeStep == ILAstOptimizationStep.RemoveRedundantCode) return; - RemoveRedundantCode(method); - - if (abortBeforeStep == ILAstOptimizationStep.ReduceBranchInstructionSet) return; - foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) { - 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); - AsyncDecompiler.RunStep1(context, method); - - if (abortBeforeStep == ILAstOptimizationStep.AsyncAwait) return; - AsyncDecompiler.RunStep2(context, method); - - if (abortBeforeStep == ILAstOptimizationStep.PropertyAccessInstructions) return; - IntroducePropertyAccessInstructions(method); - - if (abortBeforeStep == ILAstOptimizationStep.SplitToMovableBlocks) return; - foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) { - SplitToBasicBlocks(block); - } - - if (abortBeforeStep == ILAstOptimizationStep.TypeInference) return; - // Types are needed for the ternary operator optimization - TypeAnalysis.Run(context, method); - - if (abortBeforeStep == ILAstOptimizationStep.HandlePointerArithmetic) return; - HandlePointerArithmetic(method); - - foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) { - bool modified; - do { - modified = false; - - if (abortBeforeStep == ILAstOptimizationStep.SimplifyShortCircuit) return; - modified |= block.RunOptimization(new SimpleControlFlow(context, method).SimplifyShortCircuit); - - if (abortBeforeStep == ILAstOptimizationStep.SimplifyTernaryOperator) return; - modified |= block.RunOptimization(new SimpleControlFlow(context, method).SimplifyTernaryOperator); - - if (abortBeforeStep == ILAstOptimizationStep.SimplifyNullCoalescing) return; - modified |= block.RunOptimization(new SimpleControlFlow(context, method).SimplifyNullCoalescing); - - if (abortBeforeStep == ILAstOptimizationStep.JoinBasicBlocks) return; - modified |= block.RunOptimization(new SimpleControlFlow(context, method).JoinBasicBlocks); - - if (abortBeforeStep == ILAstOptimizationStep.SimplifyLogicNot) return; - modified |= block.RunOptimization(SimplifyLogicNot); - - if (abortBeforeStep == ILAstOptimizationStep.SimplifyShiftOperators) return; - modified |= block.RunOptimization(SimplifyShiftOperators); - - if (abortBeforeStep == ILAstOptimizationStep.TypeConversionSimplifications) return; - modified |= block.RunOptimization(TypeConversionSimplifications); - - if (abortBeforeStep == ILAstOptimizationStep.SimplifyLdObjAndStObj) return; - modified |= block.RunOptimization(SimplifyLdObjAndStObj); - - if (abortBeforeStep == ILAstOptimizationStep.SimplifyCustomShortCircuit) return; - modified |= block.RunOptimization(new SimpleControlFlow(context, method).SimplifyCustomShortCircuit); - - if (abortBeforeStep == ILAstOptimizationStep.SimplifyLiftedOperators) return; - modified |= block.RunOptimization(SimplifyLiftedOperators); - - if (abortBeforeStep == ILAstOptimizationStep.TransformArrayInitializers) return; - modified |= block.RunOptimization(TransformArrayInitializers); - - if (abortBeforeStep == ILAstOptimizationStep.TransformMultidimensionalArrayInitializers) return; - modified |= block.RunOptimization(TransformMultidimensionalArrayInitializers); - - if (abortBeforeStep == ILAstOptimizationStep.TransformObjectInitializers) return; - modified |= block.RunOptimization(TransformObjectInitializers); - - if (abortBeforeStep == ILAstOptimizationStep.MakeAssignmentExpression) return; - if (context.Settings.MakeAssignmentExpressions) { - modified |= block.RunOptimization(MakeAssignmentExpression); - } - modified |= block.RunOptimization(MakeCompoundAssignments); - - if (abortBeforeStep == ILAstOptimizationStep.IntroducePostIncrement) return; - if (context.Settings.IntroduceIncrementAndDecrement) { - modified |= block.RunOptimization(IntroducePostIncrement); - } - - if (abortBeforeStep == ILAstOptimizationStep.InlineExpressionTreeParameterDeclarations) return; - if (context.Settings.ExpressionTrees) { - modified |= block.RunOptimization(InlineExpressionTreeParameterDeclarations); - } - - if (abortBeforeStep == ILAstOptimizationStep.InlineVariables2) return; - modified |= new ILInlining(method).InlineAllInBlock(block); - new ILInlining(method).CopyPropagation(); - - } while(modified); - } - - if (abortBeforeStep == ILAstOptimizationStep.FindLoops) return; - foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) { - new LoopsAndConditions(context).FindLoops(block); - } - - if (abortBeforeStep == ILAstOptimizationStep.FindConditions) return; - foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) { - new LoopsAndConditions(context).FindConditions(block); - } - - if (abortBeforeStep == ILAstOptimizationStep.FlattenNestedMovableBlocks) return; - FlattenBasicBlocks(method); - - if (abortBeforeStep == ILAstOptimizationStep.RemoveEndFinally) return; - RemoveEndFinally(method); - - if (abortBeforeStep == ILAstOptimizationStep.RemoveRedundantCode2) return; - RemoveRedundantCode(method); - - if (abortBeforeStep == ILAstOptimizationStep.GotoRemoval) return; - new GotoRemoval().RemoveGotos(method); - - if (abortBeforeStep == ILAstOptimizationStep.DuplicateReturns) return; - DuplicateReturnStatements(method); - - if (abortBeforeStep == ILAstOptimizationStep.GotoRemoval2) return; - new GotoRemoval().RemoveGotos(method); - - if (abortBeforeStep == ILAstOptimizationStep.ReduceIfNesting) return; - ReduceIfNesting(method); - - if (abortBeforeStep == ILAstOptimizationStep.InlineVariables3) return; - // The 2nd inlining pass is necessary because DuplicateReturns and the introduction of ternary operators - // open up additional inlining possibilities. - new ILInlining(method).InlineAllVariables(); - - if (abortBeforeStep == ILAstOptimizationStep.CachedDelegateInitialization) return; - if (context.Settings.AnonymousMethods) { - foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) { - for (int i = 0; i < block.Body.Count; i++) { - // TODO: Move before loops - CachedDelegateInitializationWithField(block, ref i); - CachedDelegateInitializationWithLocal(block, ref i); - } - } - } - - if (abortBeforeStep == ILAstOptimizationStep.IntroduceFixedStatements) return; - // we need post-order traversal, not pre-order, for "fixed" to work correctly - foreach (ILBlock block in TreeTraversal.PostOrder<ILNode>(method, n => n.GetChildren()).OfType<ILBlock>()) { - for (int i = block.Body.Count - 1; i >= 0; i--) { - // TODO: Move before loops - if (i < block.Body.Count) - IntroduceFixedStatements(block.Body, i); - } - } - - if (abortBeforeStep == ILAstOptimizationStep.RecombineVariables) return; - RecombineVariables(method); - - if (abortBeforeStep == ILAstOptimizationStep.TypeInference2) return; - TypeAnalysis.Reset(method); - TypeAnalysis.Run(context, method); - - if (abortBeforeStep == ILAstOptimizationStep.RemoveRedundantCode3) return; - GotoRemoval.RemoveRedundantCode(method); - - // ReportUnassignedILRanges(method); - } - - /// <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; - } - } - } - } - - /// <summary> - /// Reduces the branch codes to just br and brtrue. - /// Moves ILRanges to the branch argument - /// </summary> - 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) { - ILCode op; - switch(expr.Code) { - case ILCode.Switch: - case ILCode.Brtrue: - expr.Arguments.Single().ILRanges.AddRange(expr.ILRanges); - expr.ILRanges.Clear(); - continue; - case ILCode.__Brfalse: op = ILCode.LogicNot; break; - case ILCode.__Beq: op = ILCode.Ceq; break; - case ILCode.__Bne_Un: op = ILCode.Cne; break; - case ILCode.__Bgt: op = ILCode.Cgt; break; - case ILCode.__Bgt_Un: op = ILCode.Cgt_Un; break; - case ILCode.__Ble: op = ILCode.Cle; break; - case ILCode.__Ble_Un: op = ILCode.Cle_Un; break; - case ILCode.__Blt: op = ILCode.Clt; break; - case ILCode.__Blt_Un: op = ILCode.Clt_Un; break; - case ILCode.__Bge: op = ILCode.Cge; break; - case ILCode.__Bge_Un: op = ILCode.Cge_Un; break; - default: - continue; - } - var newExpr = new ILExpression(op, null, expr.Arguments); - block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, newExpr); - newExpr.ILRanges = expr.ILRanges; - } - } - } - - /// <summary> - /// Converts call and callvirt instructions that read/write properties into CallGetter/CallSetter instructions. - /// - /// CallGetter/CallSetter is used to allow the ILAst to represent "while ((SomeProperty = value) != null)". - /// - /// Also simplifies 'newobj(SomeDelegate, target, ldvirtftn(F, target))' to 'newobj(SomeDelegate, target, ldvirtftn(F))' - /// </summary> - void IntroducePropertyAccessInstructions(ILNode node) - { - ILExpression parentExpr = node as ILExpression; - if (parentExpr != null) { - for (int i = 0; i < parentExpr.Arguments.Count; i++) { - ILExpression expr = parentExpr.Arguments[i]; - IntroducePropertyAccessInstructions(expr); - IntroducePropertyAccessInstructions(expr, parentExpr, i); - } - } else { - foreach (ILNode child in node.GetChildren()) { - IntroducePropertyAccessInstructions(child); - ILExpression expr = child as ILExpression; - if (expr != null) { - IntroducePropertyAccessInstructions(expr, null, -1); - } - } - } - } - - void IntroducePropertyAccessInstructions(ILExpression expr, ILExpression parentExpr, int posInParent) - { - if (expr.Code == ILCode.Call || expr.Code == ILCode.Callvirt) { - MethodReference cecilMethod = (MethodReference)expr.Operand; - if (cecilMethod.DeclaringType is ArrayType) { - switch (cecilMethod.Name) { - case "Get": - expr.Code = ILCode.CallGetter; - break; - case "Set": - expr.Code = ILCode.CallSetter; - break; - case "Address": - ByReferenceType brt = cecilMethod.ReturnType as ByReferenceType; - if (brt != null) { - MethodReference getMethod = new MethodReference("Get", brt.ElementType, cecilMethod.DeclaringType); - foreach (var p in cecilMethod.Parameters) - getMethod.Parameters.Add(p); - getMethod.HasThis = cecilMethod.HasThis; - expr.Operand = getMethod; - } - expr.Code = ILCode.CallGetter; - if (parentExpr != null) { - parentExpr.Arguments[posInParent] = new ILExpression(ILCode.AddressOf, null, expr); - } - break; - } - } else { - MethodDefinition cecilMethodDef = cecilMethod.Resolve(); - if (cecilMethodDef != null) { - if (cecilMethodDef.IsGetter) - expr.Code = (expr.Code == ILCode.Call) ? ILCode.CallGetter : ILCode.CallvirtGetter; - else if (cecilMethodDef.IsSetter) - expr.Code = (expr.Code == ILCode.Call) ? ILCode.CallSetter : ILCode.CallvirtSetter; - } - } - } else if (expr.Code == ILCode.Newobj && expr.Arguments.Count == 2) { - // Might be 'newobj(SomeDelegate, target, ldvirtftn(F, target))'. - ILVariable target; - if (expr.Arguments[0].Match(ILCode.Ldloc, out target) - && expr.Arguments[1].Code == ILCode.Ldvirtftn - && expr.Arguments[1].Arguments.Count == 1 - && expr.Arguments[1].Arguments[0].MatchLdloc(target)) - { - // Remove the 'target' argument from the ldvirtftn instruction. - // It's not needed in the translation to C#, and needs to be eliminated so that the target expression - // can be inlined. - expr.Arguments[1].Arguments.Clear(); - } - } - } - - /// <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; - } - - 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); - } 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); - } - } - } - } - } - } - - /// <summary> - /// Flattens all nested basic blocks, except the the top level 'node' argument - /// </summary> - void FlattenBasicBlocks(ILNode node) - { - ILBlock block = node as ILBlock; - if (block != null) { - List<ILNode> flatBody = new List<ILNode>(); - foreach (ILNode child in block.GetChildren()) { - FlattenBasicBlocks(child); - ILBasicBlock childAsBB = child as ILBasicBlock; - if (childAsBB != null) { - if (!(childAsBB.Body.FirstOrDefault() is ILLabel)) - throw new Exception("Basic block has to start with a label. \n" + childAsBB.ToString()); - if (childAsBB.Body.LastOrDefault() is ILExpression && !childAsBB.Body.LastOrDefault().IsUnconditionalControlFlow()) - throw new Exception("Basci block has to end with unconditional control flow. \n" + childAsBB.ToString()); - flatBody.AddRange(childAsBB.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); - } - } - } - - /// <summary> - /// Replace endfinally with jump to the end of the finally block - /// </summary> - void RemoveEndFinally(ILBlock method) - { - // Go thought the list in reverse so that we do the nested blocks first - foreach(var tryCatch in method.GetSelfAndChildrenRecursive<ILTryCatchBlock>(tc => tc.FinallyBlock != null).Reverse()) { - ILLabel label = new ILLabel() { Name = "EndFinally_" + nextLabelIndex++ }; - tryCatch.FinallyBlock.Body.Add(label); - foreach(var block in tryCatch.FinallyBlock.GetSelfAndChildrenRecursive<ILBlock>()) { - for (int i = 0; i < block.Body.Count; i++) { - if (block.Body[i].Match(ILCode.Endfinally)) { - block.Body[i] = new ILExpression(ILCode.Br, label).WithILRanges(((ILExpression)block.Body[i]).ILRanges); - } - } - } - } - } - - /// <summary> - /// Reduce the nesting of conditions. - /// It should be done on flat data that already had most gotos removed - /// </summary> - void ReduceIfNesting(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.LastOrDefault().IsUnconditionalControlFlow(); - bool falseExits = cond.FalseBlock.Body.LastOrDefault().IsUnconditionalControlFlow(); - - 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)) - ReduceIfNesting(child); - } - } - - void RecombineVariables(ILBlock method) - { - // Recombine variables that were split when the ILAst was created - // This ensures that a single IL variable is a single C# variable (gets assigned only one name) - // The DeclareVariables transformation might then split up the C# variable again if it is used indendently in two separate scopes. - Dictionary<VariableDefinition, ILVariable> dict = new Dictionary<VariableDefinition, ILVariable>(); - ReplaceVariables( - method, - delegate(ILVariable v) { - if (v.OriginalVariable == null) - return v; - ILVariable combinedVariable; - if (!dict.TryGetValue(v.OriginalVariable, out combinedVariable)) { - dict.Add(v.OriginalVariable, v); - combinedVariable = v; - } - return combinedVariable; - }); - } - - static void HandlePointerArithmetic(ILNode method) - { - foreach (ILExpression expr in method.GetSelfAndChildrenRecursive<ILExpression>()) { - List<ILExpression> args = expr.Arguments; - switch (expr.Code) { - case ILCode.Localloc: - args[0] = DivideBySize(args[0], ((PointerType)expr.InferredType).ElementType); - break; - case ILCode.Add: - case ILCode.Add_Ovf: - case ILCode.Add_Ovf_Un: - if (expr.InferredType is PointerType) { - if (args[0].ExpectedType is PointerType) - args[1] = DivideBySize(args[1], ((PointerType)expr.InferredType).ElementType); - else if (args[1].ExpectedType is PointerType) - args[0] = DivideBySize(args[0], ((PointerType)expr.InferredType).ElementType); - } - break; - case ILCode.Sub: - case ILCode.Sub_Ovf: - case ILCode.Sub_Ovf_Un: - if (expr.InferredType is PointerType) { - if (args[0].ExpectedType is PointerType) - args[1] = DivideBySize(args[1], ((PointerType)expr.InferredType).ElementType); - } - break; - } - } - } - - static ILExpression UnwrapIntPtrCast(ILExpression expr) - { - if (expr.Code != ILCode.Conv_I && expr.Code != ILCode.Conv_U) - return expr; - - ILExpression arg = expr.Arguments[0]; - switch (arg.InferredType.MetadataType) { - case MetadataType.Byte: - case MetadataType.SByte: - case MetadataType.UInt16: - case MetadataType.Int16: - case MetadataType.UInt32: - case MetadataType.Int32: - case MetadataType.UInt64: - case MetadataType.Int64: - return arg; - } - - return expr; - } - - static ILExpression DivideBySize(ILExpression expr, TypeReference type) - { - expr = UnwrapIntPtrCast(expr); - - ILExpression sizeOfExpression; - switch (TypeAnalysis.GetInformationAmount(type)) { - case 1: - case 8: - sizeOfExpression = new ILExpression(ILCode.Ldc_I4, 1); - break; - case 16: - sizeOfExpression = new ILExpression(ILCode.Ldc_I4, 2); - break; - case 32: - sizeOfExpression = new ILExpression(ILCode.Ldc_I4, 4); - break; - case 64: - sizeOfExpression = new ILExpression(ILCode.Ldc_I4, 8); - break; - default: - sizeOfExpression = new ILExpression(ILCode.Sizeof, type); - break; - } - - if (expr.Code == ILCode.Mul || expr.Code == ILCode.Mul_Ovf || expr.Code == ILCode.Mul_Ovf_Un) { - ILExpression mulArg = expr.Arguments[1]; - if (mulArg.Code == sizeOfExpression.Code && sizeOfExpression.Operand.Equals(mulArg.Operand)) - return UnwrapIntPtrCast(expr.Arguments[0]); - } - - if (expr.Code == sizeOfExpression.Code) { - if (sizeOfExpression.Operand.Equals(expr.Operand)) - return new ILExpression(ILCode.Ldc_I4, 1); - - if (expr.Code == ILCode.Ldc_I4) { - int offsetInBytes = (int)expr.Operand; - int elementSize = (int)sizeOfExpression.Operand; - int offsetInElements = offsetInBytes / elementSize; - - // ensure integer division - if (offsetInElements * elementSize == offsetInBytes) { - expr.Operand = offsetInElements; - return expr; - } - } - } - - return new ILExpression(ILCode.Div_Un, null, expr, sizeOfExpression); - } - - public static void ReplaceVariables(ILNode node, Func<ILVariable, ILVariable> variableMapping) - { - ILExpression expr = node as ILExpression; - if (expr != null) { - ILVariable v = expr.Operand as ILVariable; - if (v != null) - expr.Operand = variableMapping(v); - foreach (ILExpression child in expr.Arguments) - ReplaceVariables(child, variableMapping); - } else { - var catchBlock = node as ILTryCatchBlock.CatchBlock; - if (catchBlock != null && catchBlock.ExceptionVariable != null) { - catchBlock.ExceptionVariable = variableMapping(catchBlock.ExceptionVariable); - } - - foreach (ILNode child in node.GetChildren()) - ReplaceVariables(child, variableMapping); - } - } - - void ReportUnassignedILRanges(ILBlock method) - { - var unassigned = ILRange.Invert(method.GetSelfAndChildrenRecursive<ILExpression>().SelectMany(e => e.ILRanges), context.CurrentMethod.Body.CodeSize).ToList(); - if (unassigned.Count > 0) - Debug.WriteLine(string.Format("Unassigned ILRanges for {0}.{1}: {2}", context.CurrentMethod.DeclaringType.Name, context.CurrentMethod.Name, string.Join(", ", unassigned.Select(r => r.ToString())))); - } - } - - public static class ILAstOptimizerExtensionMethods - { - /// <summary> - /// Perform one pass of a given optimization on this block. - /// This block must consist of only basicblocks. - /// </summary> - public static bool RunOptimization(this ILBlock block, Func<List<ILNode>, ILBasicBlock, int, bool> optimization) - { - bool modified = false; - List<ILNode> body = block.Body; - for (int i = body.Count - 1; i >= 0; i--) { - if (i < body.Count && optimization(body, (ILBasicBlock)body[i], i)) { - modified = true; - } - } - return modified; - } - - public static bool RunOptimization(this ILBlock block, Func<List<ILNode>, ILExpression, int, bool> optimization) - { - bool modified = false; - foreach (ILBasicBlock bb in block.Body) { - for (int i = bb.Body.Count - 1; i >= 0; i--) { - ILExpression expr = bb.Body.ElementAtOrDefault(i) as ILExpression; - if (expr != null && optimization(bb.Body, expr, i)) { - modified = true; - } - } - } - return modified; - } - - public static bool IsConditionalControlFlow(this ILNode node) - { - ILExpression expr = node as ILExpression; - return expr != null && expr.Code.IsConditionalControlFlow(); - } - - public static bool IsUnconditionalControlFlow(this ILNode node) - { - ILExpression expr = node as ILExpression; - return expr != null && expr.Code.IsUnconditionalControlFlow(); - } - - /// <summary> - /// The expression has no effect on the program and can be removed - /// if its return value is not needed. - /// </summary> - 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: - case ILCode.Ldc_Decimal: - return true; - default: - return false; - } - } - - public static bool IsStoreToArray(this ILCode code) - { - switch (code) { - case ILCode.Stelem_Any: - case ILCode.Stelem_I: - case ILCode.Stelem_I1: - case ILCode.Stelem_I2: - case ILCode.Stelem_I4: - case ILCode.Stelem_I8: - case ILCode.Stelem_R4: - case ILCode.Stelem_R8: - case ILCode.Stelem_Ref: - return true; - default: - return false; - } - } - - public static bool IsLoadFromArray(this ILCode code) - { - switch (code) { - case ILCode.Ldelem_Any: - case ILCode.Ldelem_I: - case ILCode.Ldelem_I1: - case ILCode.Ldelem_I2: - case ILCode.Ldelem_I4: - case ILCode.Ldelem_I8: - case ILCode.Ldelem_U1: - case ILCode.Ldelem_U2: - case ILCode.Ldelem_U4: - case ILCode.Ldelem_R4: - case ILCode.Ldelem_R8: - case ILCode.Ldelem_Ref: - return true; - default: - return false; - } - } - - /// <summary> - /// Can the expression be used as a statement in C#? - /// </summary> - 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.CallSetter: - case ILCode.CallvirtSetter: - case ILCode.Newobj: - case ILCode.Newarr: - case ILCode.Stloc: - case ILCode.Stobj: - case ILCode.Stsfld: - case ILCode.Stfld: - case ILCode.Stind_Ref: - case ILCode.Stelem_Any: - case ILCode.Stelem_I: - case ILCode.Stelem_I1: - case ILCode.Stelem_I2: - case ILCode.Stelem_I4: - case ILCode.Stelem_I8: - case ILCode.Stelem_R4: - case ILCode.Stelem_R8: - case ILCode.Stelem_Ref: - return true; - default: - return false; - } - } - - public static ILExpression WithILRanges(this ILExpression expr, IEnumerable<ILRange> ilranges) - { - expr.ILRanges.AddRange(ilranges); - return expr; - } - - public static void RemoveTail(this List<ILNode> body, params ILCode[] codes) - { - for (int i = 0; i < codes.Length; i++) { - if (((ILExpression)body[body.Count - codes.Length + i]).Code != codes[i]) - throw new Exception("Tailing code does not match expected."); - } - body.RemoveRange(body.Count - codes.Length, codes.Length); - } - - public static V GetOrDefault<K,V>(this Dictionary<K, V> dict, K key) - { - V ret; - dict.TryGetValue(key, out ret); - return ret; - } - - public static void RemoveOrThrow<T>(this ICollection<T> collection, T item) - { - if (!collection.Remove(item)) - throw new Exception("The item was not found in the collection"); - } - - public static void RemoveOrThrow<K,V>(this Dictionary<K,V> collection, K key) - { - if (!collection.Remove(key)) - throw new Exception("The key was not found in the dictionary"); - } - - public static bool ContainsReferenceTo(this ILExpression expr, ILVariable v) - { - if (expr.Operand == v) - return true; - foreach (var arg in expr.Arguments) { - if (ContainsReferenceTo(arg, v)) - return true; - } - return false; - } - } -} |