diff options
Diffstat (limited to 'ICSharpCode.Decompiler/ILAst/ILInlining.cs')
-rw-r--r-- | ICSharpCode.Decompiler/ILAst/ILInlining.cs | 524 |
1 files changed, 0 insertions, 524 deletions
diff --git a/ICSharpCode.Decompiler/ILAst/ILInlining.cs b/ICSharpCode.Decompiler/ILAst/ILInlining.cs deleted file mode 100644 index 11140ef0..00000000 --- a/ICSharpCode.Decompiler/ILAst/ILInlining.cs +++ /dev/null @@ -1,524 +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 Mono.Cecil; - -namespace ICSharpCode.Decompiler.ILAst -{ - /// <summary> - /// Performs inlining transformations. - /// </summary> - public class ILInlining - { - readonly ILBlock method; - internal Dictionary<ILVariable, int> numStloc = new Dictionary<ILVariable, int>(); - internal Dictionary<ILVariable, int> numLdloc = new Dictionary<ILVariable, int>(); - internal Dictionary<ILVariable, int> numLdloca = new Dictionary<ILVariable, int>(); - - public ILInlining(ILBlock method) - { - this.method = method; - AnalyzeMethod(); - } - - void AnalyzeMethod() - { - numStloc.Clear(); - numLdloc.Clear(); - numLdloca.Clear(); - - // Analyse the whole method - AnalyzeNode(method); - } - - /// <summary> - /// For each variable reference, adds <paramref name="direction"/> to the num* dicts. - /// Direction will be 1 for analysis, and -1 when removing a node from analysis. - /// </summary> - void AnalyzeNode(ILNode node, int direction = 1) - { - ILExpression expr = node as ILExpression; - if (expr != null) { - ILVariable locVar = expr.Operand as ILVariable; - if (locVar != null) { - if (expr.Code == ILCode.Stloc) { - numStloc[locVar] = numStloc.GetOrDefault(locVar) + direction; - } else if (expr.Code == ILCode.Ldloc) { - numLdloc[locVar] = numLdloc.GetOrDefault(locVar) + direction; - } else if (expr.Code == ILCode.Ldloca) { - numLdloca[locVar] = numLdloca.GetOrDefault(locVar) + direction; - } else { - throw new NotSupportedException(expr.Code.ToString()); - } - } - foreach (ILExpression child in expr.Arguments) - AnalyzeNode(child, direction); - } else { - var catchBlock = node as ILTryCatchBlock.CatchBlock; - if (catchBlock != null && catchBlock.ExceptionVariable != null) { - numStloc[catchBlock.ExceptionVariable] = numStloc.GetOrDefault(catchBlock.ExceptionVariable) + direction; - } - - foreach (ILNode child in node.GetChildren()) - AnalyzeNode(child, direction); - } - } - - public bool InlineAllVariables() - { - bool modified = false; - ILInlining i = new ILInlining(method); - foreach (ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) - modified |= i.InlineAllInBlock(block); - return modified; - } - - public bool InlineAllInBlock(ILBlock block) - { - bool modified = false; - List<ILNode> body = block.Body; - if (block is ILTryCatchBlock.CatchBlock && body.Count > 1) { - ILVariable v = ((ILTryCatchBlock.CatchBlock)block).ExceptionVariable; - if (v != null && v.IsGenerated) { - if (numLdloca.GetOrDefault(v) == 0 && numStloc.GetOrDefault(v) == 1 && numLdloc.GetOrDefault(v) == 1) { - ILVariable v2; - ILExpression ldException; - if (body[0].Match(ILCode.Stloc, out v2, out ldException) && ldException.MatchLdloc(v)) { - body.RemoveAt(0); - ((ILTryCatchBlock.CatchBlock)block).ExceptionVariable = v2; - modified = true; - } - } - } - } - for(int i = 0; i < body.Count - 1;) { - ILVariable locVar; - ILExpression expr; - if (body[i].Match(ILCode.Stloc, out locVar, out expr) && InlineOneIfPossible(block.Body, i, aggressive: false)) { - modified = true; - i = Math.Max(0, i - 1); // Go back one step - } else { - i++; - } - } - foreach(ILBasicBlock bb in body.OfType<ILBasicBlock>()) { - modified |= InlineAllInBasicBlock(bb); - } - return modified; - } - - public bool InlineAllInBasicBlock(ILBasicBlock bb) - { - bool modified = false; - List<ILNode> body = bb.Body; - for(int i = 0; i < body.Count;) { - ILVariable locVar; - ILExpression expr; - if (body[i].Match(ILCode.Stloc, out locVar, out expr) && InlineOneIfPossible(bb.Body, i, aggressive: false)) { - modified = true; - i = Math.Max(0, i - 1); // Go back one step - } else { - i++; - } - } - return modified; - } - - /// <summary> - /// Inlines instructions before pos into block.Body[pos]. - /// </summary> - /// <returns>The number of instructions that were inlined.</returns> - public int InlineInto(List<ILNode> body, int pos, bool aggressive) - { - if (pos >= body.Count) - return 0; - int count = 0; - while (--pos >= 0) { - ILExpression expr = body[pos] as ILExpression; - if (expr == null || expr.Code != ILCode.Stloc) - break; - if (InlineOneIfPossible(body, pos, aggressive)) - count++; - else - break; - } - return count; - } - - /// <summary> - /// Aggressively inlines the stloc instruction at block.Body[pos] into the next instruction, if possible. - /// If inlining was possible; we will continue to inline (non-aggressively) into the the combined instruction. - /// </summary> - /// <remarks> - /// After the operation, pos will point to the new combined instruction. - /// </remarks> - public bool InlineIfPossible(List<ILNode> body, ref int pos) - { - if (InlineOneIfPossible(body, pos, true)) { - pos -= InlineInto(body, pos, false); - return true; - } - return false; - } - - /// <summary> - /// Inlines the stloc instruction at block.Body[pos] into the next instruction, if possible. - /// </summary> - public bool InlineOneIfPossible(List<ILNode> body, int pos, bool aggressive) - { - ILVariable v; - ILExpression inlinedExpression; - if (body[pos].Match(ILCode.Stloc, out v, out inlinedExpression) && !v.IsPinned) { - if (InlineIfPossible(v, inlinedExpression, body.ElementAtOrDefault(pos+1), aggressive)) { - // Assign the ranges of the stloc instruction: - inlinedExpression.ILRanges.AddRange(((ILExpression)body[pos]).ILRanges); - // Remove the stloc instruction: - body.RemoveAt(pos); - return true; - } else if (numLdloc.GetOrDefault(v) == 0 && numLdloca.GetOrDefault(v) == 0) { - // The variable is never loaded - if (inlinedExpression.HasNoSideEffects()) { - // Remove completely - AnalyzeNode(body[pos], -1); - body.RemoveAt(pos); - return true; - } else if (inlinedExpression.CanBeExpressionStatement() && v.IsGenerated) { - // Assign the ranges of the stloc instruction: - inlinedExpression.ILRanges.AddRange(((ILExpression)body[pos]).ILRanges); - // Remove the stloc, but keep the inner expression - body[pos] = inlinedExpression; - return true; - } - } - } - return false; - } - - /// <summary> - /// Inlines 'expr' into 'next', if possible. - /// </summary> - bool InlineIfPossible(ILVariable v, ILExpression inlinedExpression, ILNode next, bool aggressive) - { - // ensure the variable is accessed only a single time - if (numStloc.GetOrDefault(v) != 1) - return false; - int ldloc = numLdloc.GetOrDefault(v); - if (ldloc > 1 || ldloc + numLdloca.GetOrDefault(v) != 1) - return false; - - if (next is ILCondition) - next = ((ILCondition)next).Condition; - else if (next is ILWhileLoop) - next = ((ILWhileLoop)next).Condition; - - ILExpression parent; - int pos; - if (FindLoadInNext(next as ILExpression, v, inlinedExpression, out parent, out pos) == true) { - if (ldloc == 0) { - if (!IsGeneratedValueTypeTemporary((ILExpression)next, parent, pos, v, inlinedExpression)) - return false; - } else { - if (!aggressive && !v.IsGenerated && !NonAggressiveInlineInto((ILExpression)next, parent, inlinedExpression)) - return false; - } - - // Assign the ranges of the ldloc instruction: - inlinedExpression.ILRanges.AddRange(parent.Arguments[pos].ILRanges); - - if (ldloc == 0) { - // it was an ldloca instruction, so we need to use the pseudo-opcode 'addressof' so that the types - // comes out correctly - parent.Arguments[pos] = new ILExpression(ILCode.AddressOf, null, inlinedExpression); - } else { - parent.Arguments[pos] = inlinedExpression; - } - return true; - } - return false; - } - - /// <summary> - /// Is this a temporary variable generated by the C# compiler for instance method calls on value type values - /// </summary> - /// <param name="next">The next top-level expression</param> - /// <param name="parent">The direct parent of the load within 'next'</param> - /// <param name="pos">Index of the load within 'parent'</param> - /// <param name="v">The variable being inlined.</param> - /// <param name="inlinedExpression">The expression being inlined</param> - bool IsGeneratedValueTypeTemporary(ILExpression next, ILExpression parent, int pos, ILVariable v, ILExpression inlinedExpression) - { - if (pos == 0 && v.Type != null && v.Type.IsValueType) { - // Inlining a value type variable is allowed only if the resulting code will maintain the semantics - // that the method is operating on a copy. - // Thus, we have to disallow inlining of other locals, fields, array elements, dereferenced pointers - switch (inlinedExpression.Code) { - case ILCode.Ldloc: - case ILCode.Stloc: - case ILCode.CompoundAssignment: - 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_R4: - case ILCode.Ldelem_R8: - case ILCode.Ldelem_Ref: - case ILCode.Ldelem_U1: - case ILCode.Ldelem_U2: - case ILCode.Ldelem_U4: - case ILCode.Ldobj: - case ILCode.Ldind_Ref: - return false; - case ILCode.Ldfld: - case ILCode.Stfld: - case ILCode.Ldsfld: - case ILCode.Stsfld: - // allow inlining field access only if it's a readonly field - FieldDefinition f = ((FieldReference)inlinedExpression.Operand).Resolve(); - if (!(f != null && f.IsInitOnly)) - return false; - break; - case ILCode.Call: - case ILCode.CallGetter: - // inlining runs both before and after IntroducePropertyAccessInstructions, - // so we have to handle both 'call' and 'callgetter' - MethodReference mr = (MethodReference)inlinedExpression.Operand; - // ensure that it's not an multi-dimensional array getter - if (mr.DeclaringType is ArrayType) - return false; - goto case ILCode.Callvirt; - case ILCode.Callvirt: - case ILCode.CallvirtGetter: - // don't inline foreach loop variables: - mr = (MethodReference)inlinedExpression.Operand; - if (mr.Name == "get_Current" && mr.HasThis) - return false; - break; - case ILCode.Castclass: - case ILCode.Unbox_Any: - // These are valid, but might occur as part of a foreach loop variable. - ILExpression arg = inlinedExpression.Arguments[0]; - if (arg.Code == ILCode.CallGetter || arg.Code == ILCode.CallvirtGetter || arg.Code == ILCode.Call || arg.Code == ILCode.Callvirt) { - mr = (MethodReference)arg.Operand; - if (mr.Name == "get_Current" && mr.HasThis) - return false; // looks like a foreach loop variable, so don't inline it - } - break; - } - - // inline the compiler-generated variable that are used when accessing a member on a value type: - switch (parent.Code) { - case ILCode.Call: - case ILCode.CallGetter: - case ILCode.CallSetter: - case ILCode.Callvirt: - case ILCode.CallvirtGetter: - case ILCode.CallvirtSetter: - MethodReference mr = (MethodReference)parent.Operand; - return mr.HasThis; - case ILCode.Stfld: - case ILCode.Ldfld: - case ILCode.Ldflda: - case ILCode.Await: - return true; - } - } - return false; - } - - /// <summary> - /// Determines whether a variable should be inlined in non-aggressive mode, even though it is not a generated variable. - /// </summary> - /// <param name="next">The next top-level expression</param> - /// <param name="parent">The direct parent of the load within 'next'</param> - /// <param name="inlinedExpression">The expression being inlined</param> - bool NonAggressiveInlineInto(ILExpression next, ILExpression parent, ILExpression inlinedExpression) - { - if (inlinedExpression.Code == ILCode.DefaultValue) - return true; - - switch (next.Code) { - case ILCode.Ret: - case ILCode.Brtrue: - return parent == next; - case ILCode.Switch: - return parent == next || (parent.Code == ILCode.Sub && parent == next.Arguments[0]); - default: - return false; - } - } - - /// <summary> - /// Gets whether 'expressionBeingMoved' can be inlined into 'expr'. - /// </summary> - public bool CanInlineInto(ILExpression expr, ILVariable v, ILExpression expressionBeingMoved) - { - ILExpression parent; - int pos; - return FindLoadInNext(expr, v, expressionBeingMoved, out parent, out pos) == true; - } - - /// <summary> - /// Finds the position to inline to. - /// </summary> - /// <returns>true = found; false = cannot continue search; null = not found</returns> - bool? FindLoadInNext(ILExpression expr, ILVariable v, ILExpression expressionBeingMoved, 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 || expr.Code == ILCode.NullCoalescing)) - return false; - - ILExpression arg = expr.Arguments[i]; - - if ((arg.Code == ILCode.Ldloc || arg.Code == ILCode.Ldloca) && arg.Operand == v) { - parent = expr; - pos = i; - return true; - } - bool? r = FindLoadInNext(arg, v, expressionBeingMoved, out parent, out pos); - if (r != null) - return r; - } - if (IsSafeForInlineOver(expr, expressionBeingMoved)) - return null; // continue searching - else - return false; // abort, inlining not possible - } - - /// <summary> - /// Determines whether it is safe to move 'expressionBeingMoved' past 'expr' - /// </summary> - bool IsSafeForInlineOver(ILExpression expr, ILExpression expressionBeingMoved) - { - switch (expr.Code) { - case ILCode.Ldloc: - ILVariable loadedVar = (ILVariable)expr.Operand; - if (numLdloca.GetOrDefault(loadedVar) != 0) { - // abort, inlining is not possible - return false; - } - foreach (ILExpression potentialStore in expressionBeingMoved.GetSelfAndChildrenRecursive<ILExpression>()) { - if (potentialStore.Code == ILCode.Stloc && potentialStore.Operand == loadedVar) - return false; - } - // the expression is loading a non-forbidden variable - return true; - case ILCode.Ldloca: - case ILCode.Ldflda: - case ILCode.Ldsflda: - case ILCode.Ldelema: - case ILCode.AddressOf: - case ILCode.ValueOf: - case ILCode.NullableOf: - // address-loading instructions are safe if their arguments are safe - foreach (ILExpression arg in expr.Arguments) { - if (!IsSafeForInlineOver(arg, expressionBeingMoved)) - return false; - } - return true; - default: - // instructions with no side-effects are safe (except for Ldloc and Ldloca which are handled separately) - return expr.HasNoSideEffects(); - } - } - - /// <summary> - /// Runs a very simple form of copy propagation. - /// Copy propagation is used in two cases: - /// 1) assignments from arguments to local variables - /// If the target variable is assigned to only once (so always is that argument) and the argument is never changed (no ldarga/starg), - /// then we can replace the variable with the argument. - /// 2) assignments of address-loading instructions to local variables - /// </summary> - public void CopyPropagation() - { - foreach (ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) { - for (int i = 0; i < block.Body.Count; i++) { - ILVariable v; - ILExpression copiedExpr; - if (block.Body[i].Match(ILCode.Stloc, out v, out copiedExpr) - && !v.IsParameter && numStloc.GetOrDefault(v) == 1 && numLdloca.GetOrDefault(v) == 0 - && CanPerformCopyPropagation(copiedExpr, v)) - { - // un-inline the arguments of the ldArg instruction - ILVariable[] uninlinedArgs = new ILVariable[copiedExpr.Arguments.Count]; - for (int j = 0; j < uninlinedArgs.Length; j++) { - uninlinedArgs[j] = new ILVariable { IsGenerated = true, Name = v.Name + "_cp_" + j }; - block.Body.Insert(i++, new ILExpression(ILCode.Stloc, uninlinedArgs[j], copiedExpr.Arguments[j])); - } - - // perform copy propagation: - foreach (var expr in method.GetSelfAndChildrenRecursive<ILExpression>()) { - if (expr.Code == ILCode.Ldloc && expr.Operand == v) { - expr.Code = copiedExpr.Code; - expr.Operand = copiedExpr.Operand; - for (int j = 0; j < uninlinedArgs.Length; j++) { - expr.Arguments.Add(new ILExpression(ILCode.Ldloc, uninlinedArgs[j])); - } - } - } - - block.Body.RemoveAt(i); - if (uninlinedArgs.Length > 0) { - // if we un-inlined stuff; we need to update the usage counters - AnalyzeMethod(); - } - InlineInto(block.Body, i, aggressive: false); // maybe inlining gets possible after the removal of block.Body[i] - i -= uninlinedArgs.Length + 1; - } - } - } - } - - bool CanPerformCopyPropagation(ILExpression expr, ILVariable copyVariable) - { - switch (expr.Code) { - case ILCode.Ldloca: - case ILCode.Ldelema: - case ILCode.Ldflda: - case ILCode.Ldsflda: - // All address-loading instructions always return the same value for a given operand/argument combination, - // so they can be safely copied. - return true; - case ILCode.Ldloc: - ILVariable v = (ILVariable)expr.Operand; - if (v.IsParameter) { - // Parameters can be copied only if they aren't assigned to (directly or indirectly via ldarga) - return numLdloca.GetOrDefault(v) == 0 && numStloc.GetOrDefault(v) == 0; - } else { - // Variables are be copied only if both they and the target copy variable are generated, - // and if the variable has only a single assignment - return v.IsGenerated && copyVariable.IsGenerated && numLdloca.GetOrDefault(v) == 0 && numStloc.GetOrDefault(v) == 1; - } - default: - return false; - } - } - } -} |