From 15d0a405db4908d42c974060e7ecc52bff47b854 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 3 Jul 2016 16:58:06 +0900 Subject: [PATCH] Properly inline single-use locals in case of ldloca --- .../CSharp/ExpressionBuilder.cs | 9 + .../IL/Transforms/ILInlining.cs | 167 ++---------------- 2 files changed, 24 insertions(+), 152 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index d638c33a5..8993c9bc0 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -1093,6 +1093,15 @@ namespace ICSharpCode.Decompiler.CSharp .WithRR(new ResolveResult(trueBranch.Type)); } + protected internal override TranslatedExpression VisitAddressOf(AddressOf inst) + { + // HACK: this is only correct if the argument is an R-value; otherwise we're missing the copy to the temporary + var value = Translate(inst.Value); + return new DirectionExpression(FieldDirection.Ref, value) + .WithILInstruction(inst) + .WithRR(new ByReferenceResolveResult(value.ResolveResult, false)); + } + protected internal override TranslatedExpression VisitInvalidInstruction(InvalidInstruction inst) { string message = "Invalid IL"; diff --git a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs index f4692d8fb..8fb52417f 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs @@ -152,8 +152,8 @@ namespace ICSharpCode.Decompiler.IL ILInstruction loadInst; if (FindLoadInNext(next, v, inlinedExpression, out loadInst) == true) { if (loadInst.OpCode == OpCode.LdLoca) { - //if (!IsGeneratedValueTypeTemporary((ILInstruction)next, loadInst, v, inlinedExpression)) - return false; + if (!IsGeneratedValueTypeTemporary(next, loadInst.Parent, loadInst.ChildIndex, v)) + return false; } else { Debug.Assert(loadInst.OpCode == OpCode.LdLoc); if (!aggressive && v.Kind != VariableKind.StackSlot && !NonAggressiveInlineInto(next, loadInst, inlinedExpression)) @@ -174,7 +174,7 @@ namespace ICSharpCode.Decompiler.IL } return false; } - /* + /// /// Is this a temporary variable generated by the C# compiler for instance method calls on value type values /// @@ -182,89 +182,25 @@ namespace ICSharpCode.Decompiler.IL /// The direct parent of the load within 'next' /// Index of the load within 'parent' /// The variable being inlined. - /// The expression being inlined - bool IsGeneratedValueTypeTemporary(ILInstruction next, ILInstruction parent, int pos, ILVariable v, ILInstruction inlinedExpression) + static bool IsGeneratedValueTypeTemporary(ILInstruction next, ILInstruction parent, int pos, ILVariable v) { - 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. - ILInstruction 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; - } - + if (pos == 0 && v.Type != null && v.Type.IsReferenceType == false) { // 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: + switch (parent.OpCode) { + case OpCode.Call: + return !((Call)parent).Method.IsStatic; + case OpCode.CallVirt: + return !((CallVirt)parent).Method.IsStatic; + case OpCode.StFld: + case OpCode.LdFld: + case OpCode.LdFlda: + // TODO : Reimplement Await + //case OpCode.Await: return true; } } return false; } - */ /// /// Determines whether a variable should be inlined in non-aggressive mode, even though it is not a generated variable. @@ -354,78 +290,5 @@ namespace ICSharpCode.Decompiler.IL } return SemanticHelper.MayReorder(expressionBeingMoved.Flags, expr.Flags); } - /* - /// - /// 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 - /// - public void CopyPropagation() - { - foreach (ILBlock block in method.GetSelfAndChildrenRecursive()) { - for (int i = 0; i < block.Body.Count; i++) { - ILVariable v; - ILInstruction 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 ILInstruction(ILCode.Stloc, uninlinedArgs[j], copiedExpr.Arguments[j])); - } - - // perform copy propagation: - foreach (var expr in method.GetSelfAndChildrenRecursive()) { - 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 ILInstruction(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(ILInstruction 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; - } - }*/ } }