diff --git a/ICSharpCode.Decompiler/ILAst/ArrayInitializers.cs b/ICSharpCode.Decompiler/ILAst/ArrayInitializers.cs index 08c2aaca9..961a58aa2 100644 --- a/ICSharpCode.Decompiler/ILAst/ArrayInitializers.cs +++ b/ICSharpCode.Decompiler/ILAst/ArrayInitializers.cs @@ -33,7 +33,7 @@ namespace ICSharpCode.Decompiler.ILAst if (arg1.Match(block.Body.ElementAtOrDefault(i + 1)) && arg2.Match(block.Body.ElementAtOrDefault(i + 2))) { if (initializeArrayPattern.Match(block.Body.ElementAtOrDefault(i + 3))) { if (HandleStaticallyInitializedArray(arg2, block, i, newArrInst, arrayLength)) { - i -= ILInlining.InlineInto(block, i + 1, method) - 1; + i -= new ILInlining(method).InlineInto(block, i + 1) - 1; } return; } @@ -55,7 +55,7 @@ namespace ICSharpCode.Decompiler.ILAst ((ILExpression)block.Body[i]).Arguments[0] = new ILExpression( ILCode.InitArray, newArrInst.Operand, operands.ToArray()); block.Body.RemoveRange(i + 1, arrayLength); - i -= ILInlining.InlineInto(block, i + 1, method) - 1; + i -= new ILInlining(method).InlineInto(block, i + 1) - 1; } }; } diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs index 708dbd123..a58f7b1aa 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -23,8 +23,8 @@ namespace ICSharpCode.Decompiler.ILAst GotoRemoval, DuplicateReturns, FlattenIfStatements, - PeepholeTransforms, InlineVariables2, + PeepholeTransforms, TypeInference, None } @@ -42,7 +42,7 @@ namespace ICSharpCode.Decompiler.ILAst if (abortBeforeStep == ILAstOptimizationStep.InlineVariables) return; // Works better after simple goto removal because of the following debug pattern: stloc X; br Next; Next:; ldloc X - InlineVariables(method); + ILInlining.InlineAllVariables(method); if (abortBeforeStep == ILAstOptimizationStep.ReduceBranchInstructionSet) return; foreach(ILBlock block in method.GetSelfAndChildrenRecursive().ToList()) { @@ -97,12 +97,12 @@ namespace ICSharpCode.Decompiler.ILAst if (abortBeforeStep == ILAstOptimizationStep.FlattenIfStatements) return; FlattenIfStatements(method); + if (abortBeforeStep == ILAstOptimizationStep.InlineVariables2) return; + ILInlining.InlineAllVariables(method); + if (abortBeforeStep == ILAstOptimizationStep.PeepholeTransforms) return; PeepholeTransforms.Run(context, method); - if (abortBeforeStep == ILAstOptimizationStep.InlineVariables2) return; - InlineVariables(method); - if (abortBeforeStep == ILAstOptimizationStep.TypeInference) return; TypeAnalysis.Run(context, method); @@ -135,88 +135,6 @@ namespace ICSharpCode.Decompiler.ILAst } } - void InlineVariables(ILBlock method) - { - // Analyse the whole method - Dictionary numStloc = new Dictionary(); - Dictionary numLdloc = new Dictionary(); - Dictionary numLdloca = new Dictionary(); - - foreach(ILExpression expr in method.GetSelfAndChildrenRecursive()) { - ILVariable locVar = expr.Operand as ILVariable; - if (locVar != null) { - if (expr.Code == ILCode.Stloc) { - numStloc[locVar] = numStloc.GetOrDefault(locVar) + 1; - } else if (expr.Code == ILCode.Ldloc) { - numLdloc[locVar] = numLdloc.GetOrDefault(locVar) + 1; - } else if (expr.Code == ILCode.Ldloca) { - numLdloca[locVar] = numLdloca.GetOrDefault(locVar) + 1; - } else { - throw new NotSupportedException(expr.Code.ToString()); - } - } - } - - // Inline all blocks - foreach(ILBlock block in method.GetSelfAndChildrenRecursive()) { - List body = block.Body; - for(int i = 0; i < body.Count - 1;) { - ILExpression nextExpr = body[i + 1] as ILExpression; - ILVariable locVar; - ILExpression expr; - ILExpression ldParent; - int ldPos; - if (body[i].Match(ILCode.Stloc, out locVar, out expr) && - numStloc.GetOrDefault(locVar) == 1 && - numLdloc.GetOrDefault(locVar) == 1 && - numLdloca.GetOrDefault(locVar) == 0 && - nextExpr != null && - FindLdloc(nextExpr, locVar, out ldParent, out ldPos) == true && - ldParent != null) - { - // Assign the ranges of the optimized instrustions - expr.ILRanges.AddRange(((ILExpression)body[i]).ILRanges); - expr.ILRanges.AddRange(ldParent.Arguments[ldPos].ILRanges); - - // We are moving the expression evaluation past the other aguments. - // It is ok to pass ldloc because the expression can not contain stloc and thus the ldloc will still return the same value - body.RemoveAt(i); - ldParent.Arguments[ldPos] = expr; // Inline the stloc body - i = Math.Max(0, i - 1); // Go back one step - } else { - i++; - } - } - } - } - - /// - /// Finds the position to inline to. - /// - /// true = found; false = cannot continue search; null = not found - static bool? FindLdloc(ILExpression expr, ILVariable v, out ILExpression parent, out int pos) - { - parent = null; - pos = 0; - for (int i = 0; i < expr.Arguments.Count; i++) { - // Stop when seeing an opcode that does not guarantee that its operands will be evaluated - // Inlining in that case migth result in the inlined expresion not being evaluted - if (i == 1 && (expr.Code == ILCode.LogicAnd || expr.Code == ILCode.LogicOr || expr.Code == ILCode.TernaryOp)) - return false; - - ILExpression arg = expr.Arguments[i]; - if (arg.Code == ILCode.Ldloc && arg.Operand == v) { - parent = expr; - pos = i; - return true; - } - bool? r = FindLdloc(arg, v, out parent, out pos); - if (r != null) - return r; - } - return expr.Code == ILCode.Ldloc ? (bool?)null : false; - } - /// /// Reduces the branch codes to just br and brtrue. /// Moves ILRanges to the branch argument diff --git a/ICSharpCode.Decompiler/ILAst/ILInlining.cs b/ICSharpCode.Decompiler/ILAst/ILInlining.cs index 7ec19e7a2..073c01087 100644 --- a/ICSharpCode.Decompiler/ILAst/ILInlining.cs +++ b/ICSharpCode.Decompiler/ILAst/ILInlining.cs @@ -2,6 +2,7 @@ // This code is distributed under MIT X11 license (for details please see \doc\license.txt) using System; +using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -12,18 +13,70 @@ namespace ICSharpCode.Decompiler.ILAst /// public class ILInlining { + public static void InlineAllVariables(ILBlock method) + { + ILInlining i = new ILInlining(method); + foreach (ILBlock block in method.GetSelfAndChildrenRecursive()) + i.InlineAllInBlock(block); + } + + Dictionary numStloc = new Dictionary(); + Dictionary numLdloc = new Dictionary(); + Dictionary numLdloca = new Dictionary(); + + public ILInlining(ILBlock method) + { + // Analyse the whole method + foreach(ILExpression expr in method.GetSelfAndChildrenRecursive()) { + ILVariable locVar = expr.Operand as ILVariable; + if (locVar != null) { + if (expr.Code == ILCode.Stloc) { + numStloc[locVar] = numStloc.GetOrDefault(locVar) + 1; + } else if (expr.Code == ILCode.Ldloc) { + numLdloc[locVar] = numLdloc.GetOrDefault(locVar) + 1; + } else if (expr.Code == ILCode.Ldloca) { + numLdloca[locVar] = numLdloca.GetOrDefault(locVar) + 1; + } else { + throw new NotSupportedException(expr.Code.ToString()); + } + } + } + } + + public void InlineAllInBlock(ILBlock block) + { + List body = block.Body; + for(int i = 0; i < body.Count - 1;) { + ILExpression nextExpr = body[i + 1] as ILExpression; + ILVariable locVar; + ILExpression expr; + ILExpression ldParent; + int ldPos; + if (body[i].Match(ILCode.Stloc, out locVar, out expr) && InlineIfPossible(block, i)) { + + + // We are moving the expression evaluation past the other aguments. + // It is ok to pass ldloc because the expression can not contain stloc and thus the ldloc will still return the same value + + i = Math.Max(0, i - 1); // Go back one step + } else { + i++; + } + } + } + /// /// Inlines instructions before pos into block.Body[pos]. /// /// The number of instructions that were inlined. - public static int InlineInto(ILBlock block, int pos, ILBlock method) + public int InlineInto(ILBlock block, int pos) { int count = 0; while (--pos >= 0) { ILExpression expr = block.Body[pos] as ILExpression; if (expr == null || expr.Code != ILCode.Stloc) break; - if (InlineIfPossible(block, pos, method)) + if (InlineIfPossible(block, pos)) count++; else break; @@ -34,9 +87,16 @@ namespace ICSharpCode.Decompiler.ILAst /// /// Inlines the stloc instruction at block.Body[pos] into the next instruction, if possible. /// - public static bool InlineIfPossible(ILBlock block, int pos, ILBlock method) + public bool InlineIfPossible(ILBlock block, int pos) { - if (InlineIfPossible((ILExpression)block.Body[pos], block.Body.ElementAtOrDefault(pos+1), method)) { + ILVariable v; + ILExpression inlinedExpression; + if (block.Body[pos].Match(ILCode.Stloc, out v, out inlinedExpression) + && InlineIfPossible(v, inlinedExpression, block.Body.ElementAtOrDefault(pos+1))) + { + // Assign the ranges of the stloc instruction: + inlinedExpression.ILRanges.AddRange(((ILExpression)block.Body[pos]).ILRanges); + // Remove the stloc instruction: block.Body.RemoveAt(pos); return true; } @@ -46,17 +106,24 @@ namespace ICSharpCode.Decompiler.ILAst /// /// Inlines 'expr' into 'next', if possible. /// - public static bool InlineIfPossible(ILExpression expr, ILNode next, ILBlock method) + bool InlineIfPossible(ILVariable v, ILExpression inlinedExpression, ILNode next) { - if (expr.Code != ILCode.Stloc) - throw new ArgumentException("expr must be stloc"); // ensure the variable is accessed only a single time - if (method.GetSelfAndChildrenRecursive().Count(e => e != expr && e.Operand == expr.Operand) != 1) + if (!(numStloc.GetOrDefault(v) == 1 && numLdloc.GetOrDefault(v) == 1 && numLdloca.GetOrDefault(v) == 0)) return false; + HashSet forbiddenVariables = new HashSet(); + foreach (ILExpression potentialStore in inlinedExpression.GetSelfAndChildrenRecursive()) { + if (potentialStore.Code == ILCode.Stloc) + forbiddenVariables.Add((ILVariable)potentialStore.Operand); + } ILExpression parent; int pos; - if (FindLoadInNext(next as ILExpression, (ILVariable)expr.Operand, out parent, out pos) == true) { - parent.Arguments[pos] = expr.Arguments[0]; + if (FindLoadInNext(next as ILExpression, v, forbiddenVariables, out parent, out pos) == true) { + // Assign the ranges of the ldloc instruction: + inlinedExpression.ILRanges.AddRange(parent.Arguments[pos].ILRanges); + + parent.Arguments[pos] = inlinedExpression; + return true; } return false; @@ -66,29 +133,39 @@ namespace ICSharpCode.Decompiler.ILAst /// Finds the position to inline to. /// /// true = found; false = cannot continue search; null = not found - static bool? FindLoadInNext(ILExpression expr, ILVariable v, out ILExpression parent, out int pos) + bool? FindLoadInNext(ILExpression expr, ILVariable v, HashSet forbiddenVariables, out ILExpression parent, out int pos) { parent = null; pos = 0; if (expr == null) return false; for (int i = 0; i < expr.Arguments.Count; i++) { + // Stop when seeing an opcode that does not guarantee that its operands will be evaluated. + // Inlining in that case might result in the inlined expresion not being evaluted. + if (i == 1 && (expr.Code == ILCode.LogicAnd || expr.Code == ILCode.LogicOr || expr.Code == ILCode.TernaryOp)) + return false; + ILExpression arg = expr.Arguments[i]; + if (arg.Code == ILCode.Ldloc && arg.Operand == v) { parent = expr; pos = i; return true; } - bool? r = FindLoadInNext(arg, v, out parent, out pos); + bool? r = FindLoadInNext(arg, v, forbiddenVariables, out parent, out pos); if (r != null) return r; } - return IsWithoutSideEffects(expr.Code) ? (bool?)null : false; - } - - static bool IsWithoutSideEffects(ILCode code) - { - return code == ILCode.Ldloc; + if (expr.Code == ILCode.Ldloc) { + ILVariable loadedVar = (ILVariable)expr.Operand; + if (!forbiddenVariables.Contains(loadedVar) && numLdloca.GetOrDefault(loadedVar) == 0) { + // the expression is loading a non-forbidden variable: + // we're allowed to continue searching + return null; + } + } + // otherwise: abort, inlining is not possible + return false; } } } diff --git a/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs b/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs index 31e1f1afb..bf5dd5d4d 100644 --- a/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs +++ b/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs @@ -163,7 +163,7 @@ namespace ICSharpCode.Decompiler.ILAst if (parent.Arguments[j].Code == ILCode.Ldsfld && parent.Arguments[j].Operand == field) { parent.Arguments[j] = newObj; block.Body.RemoveAt(i); - i -= ILInlining.InlineInto(block, i, method); + i -= new ILInlining(method).InlineInto(block, i); return; } }