diff options
Diffstat (limited to 'ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs')
-rw-r--r-- | ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs | 1103 |
1 files changed, 0 insertions, 1103 deletions
diff --git a/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs b/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs deleted file mode 100644 index f514aa45..00000000 --- a/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs +++ /dev/null @@ -1,1103 +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.NRefactory.Utils; -using Mono.Cecil; - -namespace ICSharpCode.Decompiler.ILAst -{ - public partial class ILAstOptimizer - { - #region TypeConversionSimplifications - static bool TypeConversionSimplifications(List<ILNode> body, ILExpression expr, int pos) - { - bool modified = false; - modified |= TransformDecimalCtorToConstant(expr); - modified |= SimplifyLdcI4ConvI8(expr); - modified |= RemoveConvIFromArrayCreation(expr); - foreach(ILExpression arg in expr.Arguments) { - modified |= TypeConversionSimplifications(null, arg, -1); - } - return modified; - } - - static bool TransformDecimalCtorToConstant(ILExpression expr) - { - MethodReference r; - List<ILExpression> args; - if (expr.Match(ILCode.Newobj, out r, out args) && - r.DeclaringType.Namespace == "System" && - r.DeclaringType.Name == "Decimal") - { - if (args.Count == 1) { - int val; - if (args[0].Match(ILCode.Ldc_I4, out val)) { - expr.Code = ILCode.Ldc_Decimal; - expr.Operand = new decimal(val); - expr.InferredType = r.DeclaringType; - expr.Arguments.Clear(); - return true; - } - } else if (args.Count == 5) { - int lo, mid, hi, isNegative, scale; - if (expr.Arguments[0].Match(ILCode.Ldc_I4, out lo) && - expr.Arguments[1].Match(ILCode.Ldc_I4, out mid) && - expr.Arguments[2].Match(ILCode.Ldc_I4, out hi) && - expr.Arguments[3].Match(ILCode.Ldc_I4, out isNegative) && - expr.Arguments[4].Match(ILCode.Ldc_I4, out scale)) - { - expr.Code = ILCode.Ldc_Decimal; - expr.Operand = new decimal(lo, mid, hi, isNegative != 0, (byte)scale); - expr.InferredType = r.DeclaringType; - expr.Arguments.Clear(); - return true; - } - } - } - return false; - } - - static bool SimplifyLdcI4ConvI8(ILExpression expr) - { - ILExpression ldc; - int val; - if (expr.Match(ILCode.Conv_I8, out ldc) && ldc.Match(ILCode.Ldc_I4, out val)) { - expr.Code = ILCode.Ldc_I8; - expr.Operand = (long)val; - expr.Arguments.Clear(); - return true; - } - return false; - } - - static bool RemoveConvIFromArrayCreation(ILExpression expr) - { - TypeReference typeRef; - ILExpression length; - ILExpression input; - if (expr.Match(ILCode.Newarr, out typeRef, out length)) { - if (length.Match(ILCode.Conv_Ovf_I, out input) || length.Match(ILCode.Conv_I, out input) - || length.Match(ILCode.Conv_Ovf_I_Un, out input) || length.Match(ILCode.Conv_U, out input)) - { - expr.Arguments[0] = input; - return true; - } - } - return false; - } - #endregion - - #region SimplifyLdObjAndStObj - static bool SimplifyLdObjAndStObj(List<ILNode> body, ILExpression expr, int pos) - { - bool modified = false; - expr = SimplifyLdObjAndStObj(expr, ref modified); - if (modified && body != null) - body[pos] = expr; - for (int i = 0; i < expr.Arguments.Count; i++) { - expr.Arguments[i] = SimplifyLdObjAndStObj(expr.Arguments[i], ref modified); - modified |= SimplifyLdObjAndStObj(null, expr.Arguments[i], -1); - } - return modified; - } - - static ILExpression SimplifyLdObjAndStObj(ILExpression expr, ref bool modified) - { - if (expr.Code == ILCode.Initobj) { - expr.Code = ILCode.Stobj; - expr.Arguments.Add(new ILExpression(ILCode.DefaultValue, expr.Operand)); - modified = true; - } else if (expr.Code == ILCode.Cpobj) { - expr.Code = ILCode.Stobj; - expr.Arguments[1] = new ILExpression(ILCode.Ldobj, expr.Operand, expr.Arguments[1]); - modified = true; - } - ILExpression arg, arg2; - TypeReference type; - ILCode? newCode = null; - if (expr.Match(ILCode.Stobj, out type, out arg, out arg2)) { - switch (arg.Code) { - case ILCode.Ldelema: newCode = ILCode.Stelem_Any; break; - case ILCode.Ldloca: newCode = ILCode.Stloc; break; - case ILCode.Ldflda: newCode = ILCode.Stfld; break; - case ILCode.Ldsflda: newCode = ILCode.Stsfld; break; - } - } else if (expr.Match(ILCode.Ldobj, out type, out arg)) { - switch (arg.Code) { - case ILCode.Ldelema: newCode = ILCode.Ldelem_Any; break; - case ILCode.Ldloca: newCode = ILCode.Ldloc; break; - case ILCode.Ldflda: newCode = ILCode.Ldfld; break; - case ILCode.Ldsflda: newCode = ILCode.Ldsfld; break; - } - } - if (newCode != null) { - arg.Code = newCode.Value; - if (expr.Code == ILCode.Stobj) { - arg.InferredType = expr.InferredType; - arg.ExpectedType = expr.ExpectedType; - arg.Arguments.Add(arg2); - } - arg.ILRanges.AddRange(expr.ILRanges); - modified = true; - return arg; - } else { - return expr; - } - } - #endregion - - #region CachedDelegateInitialization - void CachedDelegateInitializationWithField(ILBlock block, ref int i) - { - // if (logicnot(ldsfld(field))) { - // stsfld(field, newobj(Action::.ctor, ldnull(), ldftn(method))) - // } else { - // } - // ...(..., ldsfld(field), ...) - - ILCondition c = block.Body[i] as ILCondition; - if (c == null || c.Condition == null && c.TrueBlock == null || c.FalseBlock == null) - return; - if (!(c.TrueBlock.Body.Count == 1 && c.FalseBlock.Body.Count == 0)) - return; - if (!c.Condition.Match(ILCode.LogicNot)) - return; - ILExpression condition = c.Condition.Arguments.Single() as ILExpression; - if (condition == null || condition.Code != ILCode.Ldsfld) - return; - FieldDefinition field = ((FieldReference)condition.Operand).ResolveWithinSameModule(); // field is defined in current assembly - if (field == null || !field.IsCompilerGeneratedOrIsInCompilerGeneratedClass()) - return; - ILExpression stsfld = c.TrueBlock.Body[0] as ILExpression; - if (!(stsfld != null && stsfld.Code == ILCode.Stsfld && ((FieldReference)stsfld.Operand).ResolveWithinSameModule() == field)) - return; - ILExpression newObj = stsfld.Arguments[0]; - if (!(newObj.Code == ILCode.Newobj && newObj.Arguments.Count == 2)) - return; - if (newObj.Arguments[0].Code != ILCode.Ldnull) - return; - if (newObj.Arguments[1].Code != ILCode.Ldftn) - return; - MethodDefinition anonymousMethod = ((MethodReference)newObj.Arguments[1].Operand).ResolveWithinSameModule(); // method is defined in current assembly - if (!Ast.Transforms.DelegateConstruction.IsAnonymousMethod(context, anonymousMethod)) - return; - - ILNode followingNode = block.Body.ElementAtOrDefault(i + 1); - if (followingNode != null && followingNode.GetSelfAndChildrenRecursive<ILExpression>().Count( - e => e.Code == ILCode.Ldsfld && ((FieldReference)e.Operand).ResolveWithinSameModule() == field) == 1) - { - foreach (ILExpression parent in followingNode.GetSelfAndChildrenRecursive<ILExpression>()) { - for (int j = 0; j < parent.Arguments.Count; j++) { - if (parent.Arguments[j].Code == ILCode.Ldsfld && ((FieldReference)parent.Arguments[j].Operand).ResolveWithinSameModule() == field) { - parent.Arguments[j] = newObj; - block.Body.RemoveAt(i); - i -= new ILInlining(method).InlineInto(block.Body, i, aggressive: false); - return; - } - } - } - } - } - - void CachedDelegateInitializationWithLocal(ILBlock block, ref int i) - { - // if (logicnot(ldloc(v))) { - // stloc(v, newobj(Action::.ctor, ldloc(displayClass), ldftn(method))) - // } else { - // } - // ...(..., ldloc(v), ...) - - ILCondition c = block.Body[i] as ILCondition; - if (c == null || c.Condition == null && c.TrueBlock == null || c.FalseBlock == null) - return; - if (!(c.TrueBlock.Body.Count == 1 && c.FalseBlock.Body.Count == 0)) - return; - if (!c.Condition.Match(ILCode.LogicNot)) - return; - ILExpression condition = c.Condition.Arguments.Single() as ILExpression; - if (condition == null || condition.Code != ILCode.Ldloc) - return; - ILVariable v = (ILVariable)condition.Operand; - ILExpression stloc = c.TrueBlock.Body[0] as ILExpression; - if (!(stloc != null && stloc.Code == ILCode.Stloc && (ILVariable)stloc.Operand == v)) - return; - ILExpression newObj = stloc.Arguments[0]; - if (!(newObj.Code == ILCode.Newobj && newObj.Arguments.Count == 2)) - return; - if (newObj.Arguments[0].Code != ILCode.Ldloc) - return; - if (newObj.Arguments[1].Code != ILCode.Ldftn) - return; - MethodDefinition anonymousMethod = ((MethodReference)newObj.Arguments[1].Operand).ResolveWithinSameModule(); // method is defined in current assembly - if (!Ast.Transforms.DelegateConstruction.IsAnonymousMethod(context, anonymousMethod)) - return; - - ILNode followingNode = block.Body.ElementAtOrDefault(i + 1); - if (followingNode != null && followingNode.GetSelfAndChildrenRecursive<ILExpression>().Count( - e => e.Code == ILCode.Ldloc && (ILVariable)e.Operand == v) == 1) - { - ILInlining inlining = new ILInlining(method); - if (!(inlining.numLdloc.GetOrDefault(v) == 2 && inlining.numStloc.GetOrDefault(v) == 2 && inlining.numLdloca.GetOrDefault(v) == 0)) - return; - - // Find the store instruction that initializes the local to null: - foreach (ILBlock storeBlock in method.GetSelfAndChildrenRecursive<ILBlock>()) { - for (int j = 0; j < storeBlock.Body.Count; j++) { - ILVariable storedVar; - ILExpression storedExpr; - if (storeBlock.Body[j].Match(ILCode.Stloc, out storedVar, out storedExpr) && storedVar == v && storedExpr.Match(ILCode.Ldnull)) { - // Remove the instruction - storeBlock.Body.RemoveAt(j); - if (storeBlock == block && j < i) - i--; - break; - } - } - } - - block.Body[i] = stloc; // remove the 'if (v==null)' - inlining = new ILInlining(method); - inlining.InlineIfPossible(block.Body, ref i); - } - } - #endregion - - #region MakeAssignmentExpression - bool MakeAssignmentExpression(List<ILNode> body, ILExpression expr, int pos) - { - // exprVar = ... - // stloc(v, exprVar) - // -> - // exprVar = stloc(v, ...)) - ILVariable exprVar; - ILExpression initializer; - if (!(expr.Match(ILCode.Stloc, out exprVar, out initializer) && exprVar.IsGenerated)) - return false; - ILExpression nextExpr = body.ElementAtOrDefault(pos + 1) as ILExpression; - ILVariable v; - ILExpression stLocArg; - if (nextExpr.Match(ILCode.Stloc, out v, out stLocArg) && stLocArg.MatchLdloc(exprVar)) { - ILExpression store2 = body.ElementAtOrDefault(pos + 2) as ILExpression; - if (StoreCanBeConvertedToAssignment(store2, exprVar)) { - // expr_44 = ... - // stloc(v1, expr_44) - // anystore(v2, expr_44) - // -> - // stloc(v1, anystore(v2, ...)) - ILInlining inlining = new ILInlining(method); - if (inlining.numLdloc.GetOrDefault(exprVar) == 2 && inlining.numStloc.GetOrDefault(exprVar) == 1) { - body.RemoveAt(pos + 2); // remove store2 - body.RemoveAt(pos); // remove expr = ... - nextExpr.Arguments[0] = store2; - store2.Arguments[store2.Arguments.Count - 1] = initializer; - - inlining.InlineIfPossible(body, ref pos); - - return true; - } - } - - body.RemoveAt(pos + 1); // remove stloc - nextExpr.Arguments[0] = initializer; - ((ILExpression)body[pos]).Arguments[0] = nextExpr; - return true; - } else if ((nextExpr.Code == ILCode.Stsfld || nextExpr.Code == ILCode.CallSetter || nextExpr.Code == ILCode.CallvirtSetter) && nextExpr.Arguments.Count == 1) { - // exprVar = ... - // stsfld(fld, exprVar) - // -> - // exprVar = stsfld(fld, ...)) - if (nextExpr.Arguments[0].MatchLdloc(exprVar)) { - body.RemoveAt(pos + 1); // remove stsfld - nextExpr.Arguments[0] = initializer; - ((ILExpression)body[pos]).Arguments[0] = nextExpr; - return true; - } - } - return false; - } - - bool StoreCanBeConvertedToAssignment(ILExpression store, ILVariable exprVar) - { - if (store == null) - return false; - switch (store.Code) { - case ILCode.Stloc: - case ILCode.Stfld: - case ILCode.Stsfld: - case ILCode.Stobj: - case ILCode.CallSetter: - case ILCode.CallvirtSetter: - break; - default: - if (!store.Code.IsStoreToArray()) - return false; - break; - } - return store.Arguments.Last().Code == ILCode.Ldloc && store.Arguments.Last().Operand == exprVar; - } - #endregion - - #region MakeCompoundAssignments - bool MakeCompoundAssignments(List<ILNode> body, ILExpression expr, int pos) - { - bool modified = false; - modified |= MakeCompoundAssignment(expr); - // Static fields and local variables are not handled here - those are expressions without side effects - // and get handled by ReplaceMethodCallsWithOperators - // (which does a reversible transform to the short operator form, as the introduction of checked/unchecked might have to revert to the long form). - foreach (ILExpression arg in expr.Arguments) { - modified |= MakeCompoundAssignments(null, arg, -1); - } - if (modified && body != null) - new ILInlining(method).InlineInto(body, pos, aggressive: false); - return modified; - } - - bool MakeCompoundAssignment(ILExpression expr) - { - // stelem.any(T, ldloc(array), ldloc(pos), <OP>(ldelem.any(T, ldloc(array), ldloc(pos)), <RIGHT>)) - // or - // stobj(T, ldloc(ptr), <OP>(ldobj(T, ldloc(ptr)), <RIGHT>)) - ILCode expectedLdelemCode; - switch (expr.Code) { - case ILCode.Stelem_Any: - expectedLdelemCode = ILCode.Ldelem_Any; - break; - case ILCode.Stfld: - expectedLdelemCode = ILCode.Ldfld; - break; - case ILCode.Stobj: - expectedLdelemCode = ILCode.Ldobj; - break; - case ILCode.CallSetter: - expectedLdelemCode = ILCode.CallGetter; - break; - case ILCode.CallvirtSetter: - expectedLdelemCode = ILCode.CallvirtGetter; - break; - default: - return false; - } - - // all arguments except the last (so either array+pos, or ptr): - bool hasGeneratedVar = false; - for (int i = 0; i < expr.Arguments.Count - 1; i++) { - ILVariable inputVar; - if (!expr.Arguments[i].Match(ILCode.Ldloc, out inputVar)) - return false; - hasGeneratedVar |= inputVar.IsGenerated; - } - // At least one of the variables must be generated; otherwise we just keep the expanded form. - // We do this because we want compound assignments to be represented in ILAst only when strictly necessary; - // other compound assignments will be introduced by ReplaceMethodCallsWithOperator - // (which uses a reversible transformation, see ReplaceMethodCallsWithOperator.RestoreOriginalAssignOperatorAnnotation) - if (!hasGeneratedVar) - return false; - - ILExpression op = expr.Arguments.Last(); - // in case of compound assignments with a lifted operator the result is inside NullableOf and the operand is inside ValueOf - bool liftedOperator = false; - if (op.Code == ILCode.NullableOf) { - op = op.Arguments[0]; - liftedOperator = true; - } - if (!CanBeRepresentedAsCompoundAssignment(op)) - return false; - - ILExpression ldelem = op.Arguments[0]; - if (liftedOperator) { - if (ldelem.Code != ILCode.ValueOf) - return false; - ldelem = ldelem.Arguments[0]; - } - if (ldelem.Code != expectedLdelemCode) - return false; - Debug.Assert(ldelem.Arguments.Count == expr.Arguments.Count - 1); - for (int i = 0; i < ldelem.Arguments.Count; i++) { - if (!ldelem.Arguments[i].MatchLdloc((ILVariable)expr.Arguments[i].Operand)) - return false; - } - expr.Code = ILCode.CompoundAssignment; - expr.Operand = null; - expr.Arguments.RemoveRange(0, ldelem.Arguments.Count); - // result is "CompoundAssignment(<OP>(ldelem.any(...), <RIGHT>))" - return true; - } - - static bool CanBeRepresentedAsCompoundAssignment(ILExpression expr) - { - switch (expr.Code) { - case ILCode.Add: - case ILCode.Add_Ovf: - case ILCode.Add_Ovf_Un: - case ILCode.Sub: - case ILCode.Sub_Ovf: - case ILCode.Sub_Ovf_Un: - case ILCode.Mul: - case ILCode.Mul_Ovf: - case ILCode.Mul_Ovf_Un: - case ILCode.Div: - case ILCode.Div_Un: - case ILCode.Rem: - case ILCode.Rem_Un: - case ILCode.And: - case ILCode.Or: - case ILCode.Xor: - case ILCode.Shl: - case ILCode.Shr: - case ILCode.Shr_Un: - return true; - case ILCode.Call: - var m = expr.Operand as MethodReference; - if (m == null || m.HasThis || expr.Arguments.Count != 2) return false; - switch (m.Name) { - case "op_Addition": - case "op_Subtraction": - case "op_Multiply": - case "op_Division": - case "op_Modulus": - case "op_BitwiseAnd": - case "op_BitwiseOr": - case "op_ExclusiveOr": - case "op_LeftShift": - case "op_RightShift": - return true; - default: - return false; - } - default: - return false; - } - } - #endregion - - #region IntroducePostIncrement - - bool IntroducePostIncrement(List<ILNode> body, ILExpression expr, int pos) - { - bool modified = IntroducePostIncrementForVariables(body, expr, pos); - Debug.Assert(body[pos] == expr); // IntroducePostIncrementForVariables shouldn't change the expression reference - ILExpression newExpr = IntroducePostIncrementForInstanceFields(expr); - if (newExpr != null) { - modified = true; - body[pos] = newExpr; - new ILInlining(method).InlineIfPossible(body, ref pos); - } - return modified; - } - - bool IntroducePostIncrementForVariables(List<ILNode> body, ILExpression expr, int pos) - { - // Works for variables and static fields/properties - - // expr = ldloc(i) - // stloc(i, add(expr, ldc.i4(1))) - // -> - // expr = postincrement(1, ldloca(i)) - ILVariable exprVar; - ILExpression exprInit; - if (!(expr.Match(ILCode.Stloc, out exprVar, out exprInit) && exprVar.IsGenerated)) - return false; - - //The next expression - ILExpression nextExpr = body.ElementAtOrDefault(pos + 1) as ILExpression; - if (nextExpr == null) - return false; - - ILCode loadInstruction = exprInit.Code; - ILCode storeInstruction = nextExpr.Code; - bool recombineVariable = false; - - // We only recognise local variables, static fields, and static getters with no arguments - switch (loadInstruction) { - case ILCode.Ldloc: - //Must be a matching store type - if (storeInstruction != ILCode.Stloc) - return false; - ILVariable loadVar = (ILVariable)exprInit.Operand; - ILVariable storeVar = (ILVariable)nextExpr.Operand; - if (loadVar != storeVar) { - if (loadVar.OriginalVariable != null && loadVar.OriginalVariable == storeVar.OriginalVariable) - recombineVariable = true; - else - return false; - } - break; - case ILCode.Ldsfld: - if (storeInstruction != ILCode.Stsfld) - return false; - if (exprInit.Operand != nextExpr.Operand) - return false; - break; - case ILCode.CallGetter: - // non-static getters would have the 'this' argument - if (exprInit.Arguments.Count != 0) - return false; - if (storeInstruction != ILCode.CallSetter) - return false; - if (!IsGetterSetterPair(exprInit.Operand, nextExpr.Operand)) - return false; - break; - default: - return false; - } - - ILExpression addExpr = nextExpr.Arguments[0]; - - int incrementAmount; - ILCode incrementCode = GetIncrementCode(addExpr, out incrementAmount); - if (!(incrementAmount != 0 && addExpr.Arguments[0].MatchLdloc(exprVar))) - return false; - - if (recombineVariable) { - // Split local variable, unsplit these two instances - // replace nextExpr.Operand with exprInit.Operand - ReplaceVariables(method, oldVar => oldVar == nextExpr.Operand ? (ILVariable)exprInit.Operand : oldVar); - } - - switch (loadInstruction) { - case ILCode.Ldloc: - exprInit.Code = ILCode.Ldloca; - break; - case ILCode.Ldsfld: - exprInit.Code = ILCode.Ldsflda; - break; - case ILCode.CallGetter: - exprInit = new ILExpression(ILCode.AddressOf, null, exprInit); - break; - } - expr.Arguments[0] = new ILExpression(incrementCode, incrementAmount, exprInit); - body.RemoveAt(pos + 1); // TODO ILRanges - return true; - } - - static bool IsGetterSetterPair(object getterOperand, object setterOperand) - { - MethodReference getter = getterOperand as MethodReference; - MethodReference setter = setterOperand as MethodReference; - if (getter == null || setter == null) - return false; - if (!TypeAnalysis.IsSameType(getter.DeclaringType, setter.DeclaringType)) - return false; - MethodDefinition getterDef = getter.Resolve(); - MethodDefinition setterDef = setter.Resolve(); - if (getterDef == null || setterDef == null) - return false; - foreach (PropertyDefinition prop in getterDef.DeclaringType.Properties) { - if (prop.GetMethod == getterDef) - return prop.SetMethod == setterDef; - } - return false; - } - - ILExpression IntroducePostIncrementForInstanceFields(ILExpression expr) - { - // stfld(field, ldloc(instance), add(stloc(helperVar, ldfld(field, ldloc(instance))), ldc.i4(1))) - // -> stloc(helperVar, postincrement(1, ldflda(field, ldloc(instance)))) - - // Also works for array elements and pointers: - - // stelem.any(T, ldloc(instance), ldloc(pos), add(stloc(helperVar, ldelem.any(T, ldloc(instance), ldloc(pos))), ldc.i4(1))) - // -> stloc(helperVar, postincrement(1, ldelema(ldloc(instance), ldloc(pos)))) - - // stobj(T, ldloc(ptr), add(stloc(helperVar, ldobj(T, ldloc(ptr)), ldc.i4(1)))) - // -> stloc(helperVar, postIncrement(1, ldloc(ptr))) - - // callsetter(set_P, ldloc(instance), add(stloc(helperVar, callgetter(get_P, ldloc(instance))), ldc.i4(1))) - // -> stloc(helperVar, postIncrement(1, propertyaddress. callgetter(get_P, ldloc(instance)))) - - if (!(expr.Code == ILCode.Stfld || expr.Code.IsStoreToArray() || expr.Code == ILCode.Stobj || expr.Code == ILCode.CallSetter || expr.Code == ILCode.CallvirtSetter)) - return null; - - // Test that all arguments except the last are ldloc (1 arg for fields and pointers, 2 args for arrays) - for (int i = 0; i < expr.Arguments.Count - 1; i++) { - if (expr.Arguments[i].Code != ILCode.Ldloc) - return null; - } - - ILExpression addExpr = expr.Arguments[expr.Arguments.Count - 1]; - int incrementAmount; - ILCode incrementCode = GetIncrementCode(addExpr, out incrementAmount); - ILVariable helperVar; - ILExpression initialValue; - if (!(incrementAmount != 0 && addExpr.Arguments[0].Match(ILCode.Stloc, out helperVar, out initialValue))) - return null; - - if (expr.Code == ILCode.Stfld) { - if (initialValue.Code != ILCode.Ldfld) - return null; - // There might be two different FieldReference instances, so we compare the field's signatures: - FieldReference getField = (FieldReference)initialValue.Operand; - FieldReference setField = (FieldReference)expr.Operand; - if (!(TypeAnalysis.IsSameType(getField.DeclaringType, setField.DeclaringType) - && getField.Name == setField.Name && TypeAnalysis.IsSameType(getField.FieldType, setField.FieldType))) - { - return null; - } - } else if (expr.Code == ILCode.Stobj) { - if (!(initialValue.Code == ILCode.Ldobj && initialValue.Operand == expr.Operand)) - return null; - } else if (expr.Code == ILCode.CallSetter) { - if (!(initialValue.Code == ILCode.CallGetter && IsGetterSetterPair(initialValue.Operand, expr.Operand))) - return null; - } else if (expr.Code == ILCode.CallvirtSetter) { - if (!(initialValue.Code == ILCode.CallvirtGetter && IsGetterSetterPair(initialValue.Operand, expr.Operand))) - return null; - } else { - if (!initialValue.Code.IsLoadFromArray()) - return null; - } - Debug.Assert(expr.Arguments.Count - 1 == initialValue.Arguments.Count); - for (int i = 0; i < initialValue.Arguments.Count; i++) { - if (!initialValue.Arguments[i].MatchLdloc((ILVariable)expr.Arguments[i].Operand)) - return null; - } - - ILExpression stloc = addExpr.Arguments[0]; - if (expr.Code == ILCode.Stobj) { - stloc.Arguments[0] = new ILExpression(ILCode.PostIncrement, incrementAmount, initialValue.Arguments[0]); - } else if (expr.Code == ILCode.CallSetter || expr.Code == ILCode.CallvirtSetter) { - initialValue = new ILExpression(ILCode.AddressOf, null, initialValue); - stloc.Arguments[0] = new ILExpression(ILCode.PostIncrement, incrementAmount, initialValue); - } else { - stloc.Arguments[0] = new ILExpression(ILCode.PostIncrement, incrementAmount, initialValue); - initialValue.Code = (expr.Code == ILCode.Stfld ? ILCode.Ldflda : ILCode.Ldelema); - } - // TODO: ILRanges? - - return stloc; - } - - ILCode GetIncrementCode(ILExpression addExpr, out int incrementAmount) - { - ILCode incrementCode; - bool decrement = false; - switch (addExpr.Code) { - case ILCode.Add: - incrementCode = ILCode.PostIncrement; - break; - case ILCode.Add_Ovf: - incrementCode = ILCode.PostIncrement_Ovf; - break; - case ILCode.Add_Ovf_Un: - incrementCode = ILCode.PostIncrement_Ovf_Un; - break; - case ILCode.Sub: - incrementCode = ILCode.PostIncrement; - decrement = true; - break; - case ILCode.Sub_Ovf: - incrementCode = ILCode.PostIncrement_Ovf; - decrement = true; - break; - case ILCode.Sub_Ovf_Un: - incrementCode = ILCode.PostIncrement_Ovf_Un; - decrement = true; - break; - default: - incrementAmount = 0; - return ILCode.Nop; - } - if (addExpr.Arguments[1].Match(ILCode.Ldc_I4, out incrementAmount)) { - if (incrementAmount == -1 || incrementAmount == 1) { // TODO pointer increment? - if (decrement) - incrementAmount = -incrementAmount; - return incrementCode; - } - } - incrementAmount = 0; - return ILCode.Nop; - } - #endregion - - #region IntroduceFixedStatements - bool IntroduceFixedStatements(List<ILNode> body, int i) - { - ILExpression initValue; - ILVariable pinnedVar; - int initEndPos; - if (!MatchFixedInitializer(body, i, out pinnedVar, out initValue, out initEndPos)) - return false; - - ILFixedStatement fixedStmt = body.ElementAtOrDefault(initEndPos) as ILFixedStatement; - if (fixedStmt != null) { - ILExpression expr = fixedStmt.BodyBlock.Body.LastOrDefault() as ILExpression; - if (expr != null && expr.Code == ILCode.Stloc && expr.Operand == pinnedVar && IsNullOrZero(expr.Arguments[0])) { - // we found a second initializer for the existing fixed statement - fixedStmt.Initializers.Insert(0, initValue); - body.RemoveRange(i, initEndPos - i); - fixedStmt.BodyBlock.Body.RemoveAt(fixedStmt.BodyBlock.Body.Count - 1); - if (pinnedVar.Type.IsByReference) - pinnedVar.Type = new PointerType(((ByReferenceType)pinnedVar.Type).ElementType); - return true; - } - } - - // find where pinnedVar is reset to 0: - int j; - for (j = initEndPos; j < body.Count; j++) { - ILVariable v2; - ILExpression storedVal; - // stloc(pinned_Var, conv.u(ldc.i4(0))) - if (body[j].Match(ILCode.Stloc, out v2, out storedVal) && v2 == pinnedVar) { - if (IsNullOrZero(storedVal)) { - break; - } - } - } - // Create fixed statement from i to j - fixedStmt = new ILFixedStatement(); - fixedStmt.Initializers.Add(initValue); - fixedStmt.BodyBlock = new ILBlock(body.GetRange(initEndPos, j - initEndPos)); // from initEndPos to j-1 (inclusive) - body.RemoveRange(i + 1, Math.Min(j, body.Count - 1) - i); // from i+1 to j (inclusive) - body[i] = fixedStmt; - if (pinnedVar.Type.IsByReference) - pinnedVar.Type = new PointerType(((ByReferenceType)pinnedVar.Type).ElementType); - - return true; - } - - bool IsNullOrZero(ILExpression expr) - { - if (expr.Code == ILCode.Conv_U || expr.Code == ILCode.Conv_I) - expr = expr.Arguments[0]; - return (expr.Code == ILCode.Ldc_I4 && (int)expr.Operand == 0) || expr.Code == ILCode.Ldnull; - } - - bool MatchFixedInitializer(List<ILNode> body, int i, out ILVariable pinnedVar, out ILExpression initValue, out int nextPos) - { - if (body[i].Match(ILCode.Stloc, out pinnedVar, out initValue) && pinnedVar.IsPinned && !IsNullOrZero(initValue)) { - initValue = (ILExpression)body[i]; - nextPos = i + 1; - HandleStringFixing(pinnedVar, body, ref nextPos, ref initValue); - return true; - } - ILCondition ifStmt = body[i] as ILCondition; - ILExpression arrayLoadingExpr; - if (ifStmt != null && MatchFixedArrayInitializerCondition(ifStmt.Condition, out arrayLoadingExpr)) { - ILVariable arrayVariable = (ILVariable)arrayLoadingExpr.Operand; - ILExpression trueValue; - if (ifStmt.TrueBlock != null && ifStmt.TrueBlock.Body.Count == 1 - && ifStmt.TrueBlock.Body[0].Match(ILCode.Stloc, out pinnedVar, out trueValue) - && pinnedVar.IsPinned && IsNullOrZero(trueValue)) - { - if (ifStmt.FalseBlock != null && ifStmt.FalseBlock.Body.Count == 1 && ifStmt.FalseBlock.Body[0] is ILFixedStatement) { - ILFixedStatement fixedStmt = (ILFixedStatement)ifStmt.FalseBlock.Body[0]; - ILVariable stlocVar; - ILExpression falseValue; - if (fixedStmt.Initializers.Count == 1 && fixedStmt.BodyBlock.Body.Count == 0 - && fixedStmt.Initializers[0].Match(ILCode.Stloc, out stlocVar, out falseValue) && stlocVar == pinnedVar) - { - ILVariable loadedVariable; - if (falseValue.Code == ILCode.Ldelema - && falseValue.Arguments[0].Match(ILCode.Ldloc, out loadedVariable) && loadedVariable == arrayVariable - && IsNullOrZero(falseValue.Arguments[1])) - { - // OK, we detected the pattern for fixing an array. - // Now check whether the loading expression was a store ot a temp. var - // that can be eliminated. - if (arrayLoadingExpr.Code == ILCode.Stloc) { - ILInlining inlining = new ILInlining(method); - if (inlining.numLdloc.GetOrDefault(arrayVariable) == 2 && - inlining.numStloc.GetOrDefault(arrayVariable) == 1 && inlining.numLdloca.GetOrDefault(arrayVariable) == 0) - { - arrayLoadingExpr = arrayLoadingExpr.Arguments[0]; - } - } - initValue = new ILExpression(ILCode.Stloc, pinnedVar, arrayLoadingExpr); - nextPos = i + 1; - return true; - } - } - } - } - } - initValue = null; - nextPos = -1; - return false; - } - - bool MatchFixedArrayInitializerCondition(ILExpression condition, out ILExpression initValue) - { - ILExpression logicAnd; - ILVariable arrayVar; - if (condition.Match(ILCode.LogicNot, out logicAnd) && logicAnd.Code == ILCode.LogicAnd) { - initValue = UnpackDoubleNegation(logicAnd.Arguments[0]); - ILExpression arrayVarInitializer; - if (initValue.Match(ILCode.Ldloc, out arrayVar) - || initValue.Match(ILCode.Stloc, out arrayVar, out arrayVarInitializer)) - { - ILExpression arrayLength = logicAnd.Arguments[1]; - if (arrayLength.Code == ILCode.Conv_I4) - arrayLength = arrayLength.Arguments[0]; - return arrayLength.Code == ILCode.Ldlen && arrayLength.Arguments[0].MatchLdloc(arrayVar); - } - } - initValue = null; - return false; - } - - ILExpression UnpackDoubleNegation(ILExpression expr) - { - ILExpression negated; - if (expr.Match(ILCode.LogicNot, out negated) && negated.Match(ILCode.LogicNot, out negated)) - return negated; - else - return expr; - } - - bool HandleStringFixing(ILVariable pinnedVar, List<ILNode> body, ref int pos, ref ILExpression fixedStmtInitializer) - { - // fixed (stloc(pinnedVar, ldloc(text))) { - // var1 = var2 = conv.i(ldloc(pinnedVar)) - // if (logicnot(logicnot(var1))) { - // var2 = add(var1, call(RuntimeHelpers::get_OffsetToStringData)) - // } - // stloc(ptrVar, var2) - // ... - - if (pos >= body.Count) - return false; - - ILVariable var1, var2; - ILExpression varAssignment, ptrInitialization; - if (!(body[pos].Match(ILCode.Stloc, out var1, out varAssignment) && varAssignment.Match(ILCode.Stloc, out var2, out ptrInitialization))) - return false; - if (!(var1.IsGenerated && var2.IsGenerated)) - return false; - if (ptrInitialization.Code == ILCode.Conv_I || ptrInitialization.Code == ILCode.Conv_U) - ptrInitialization = ptrInitialization.Arguments[0]; - if (!ptrInitialization.MatchLdloc(pinnedVar)) - return false; - - ILCondition ifStmt = body[pos + 1] as ILCondition; - if (!(ifStmt != null && ifStmt.TrueBlock != null && ifStmt.TrueBlock.Body.Count == 1 && (ifStmt.FalseBlock == null || ifStmt.FalseBlock.Body.Count == 0))) - return false; - if (!UnpackDoubleNegation(ifStmt.Condition).MatchLdloc(var1)) - return false; - ILVariable assignedVar; - ILExpression assignedExpr; - if (!(ifStmt.TrueBlock.Body[0].Match(ILCode.Stloc, out assignedVar, out assignedExpr) && assignedVar == var2 && assignedExpr.Code == ILCode.Add)) - return false; - MethodReference calledMethod; - if (!(assignedExpr.Arguments[0].MatchLdloc(var1))) - return false; - if (!(assignedExpr.Arguments[1].Match(ILCode.Call, out calledMethod) || assignedExpr.Arguments[1].Match(ILCode.CallGetter, out calledMethod))) - return false; - if (!(calledMethod.Name == "get_OffsetToStringData" && calledMethod.DeclaringType.FullName == "System.Runtime.CompilerServices.RuntimeHelpers")) - return false; - - ILVariable pointerVar; - if (body[pos + 2].Match(ILCode.Stloc, out pointerVar, out assignedExpr) && assignedExpr.MatchLdloc(var2)) { - pos += 3; - fixedStmtInitializer.Operand = pointerVar; - return true; - } - return false; - } - #endregion - - #region SimplifyLogicNot - static bool SimplifyLogicNot(List<ILNode> body, ILExpression expr, int pos) - { - bool modified = false; - expr = SimplifyLogicNot(expr, ref modified); - Debug.Assert(expr == null); - return modified; - } - - static ILExpression SimplifyLogicNot(ILExpression expr, ref bool modified) - { - ILExpression a; - // "ceq(a, ldc.i4.0)" becomes "logicnot(a)" if the inferred type for expression "a" is boolean - if (expr.Code == ILCode.Ceq && TypeAnalysis.IsBoolean(expr.Arguments[0].InferredType) && (a = expr.Arguments[1]).Code == ILCode.Ldc_I4 && (int)a.Operand == 0) { - expr.Code = ILCode.LogicNot; - expr.ILRanges.AddRange(a.ILRanges); - expr.Arguments.RemoveAt(1); - modified = true; - } - - ILExpression res = null; - while (expr.Code == ILCode.LogicNot) { - a = expr.Arguments[0]; - // remove double negation - if (a.Code == ILCode.LogicNot) { - res = a.Arguments[0]; - res.ILRanges.AddRange(expr.ILRanges); - res.ILRanges.AddRange(a.ILRanges); - expr = res; - } else { - if (SimplifyLogicNotArgument(expr)) res = expr = a; - break; - } - } - - for (int i = 0; i < expr.Arguments.Count; i++) { - a = SimplifyLogicNot(expr.Arguments[i], ref modified); - if (a != null) { - expr.Arguments[i] = a; - modified = true; - } - } - - return res; - } - - /// <summary> - /// If the argument is a binary comparison operation then the negation is pushed through it - /// </summary> - static bool SimplifyLogicNotArgument(ILExpression expr) - { - var a = expr.Arguments[0]; - ILCode c; - switch (a.Code) { - case ILCode.Ceq: c = ILCode.Cne; break; - case ILCode.Cne: c = ILCode.Ceq; break; - case ILCode.Cgt: c = ILCode.Cle; break; - case ILCode.Cgt_Un: c = ILCode.Cle_Un; break; - case ILCode.Cge: c = ILCode.Clt; break; - case ILCode.Cge_Un: c = ILCode.Clt_Un; break; - case ILCode.Clt: c = ILCode.Cge; break; - case ILCode.Clt_Un: c = ILCode.Cge_Un; break; - case ILCode.Cle: c = ILCode.Cgt; break; - case ILCode.Cle_Un: c = ILCode.Cgt_Un; break; - default: return false; - } - a.Code = c; - a.ILRanges.AddRange(expr.ILRanges); - return true; - } - #endregion - - #region SimplifyShiftOperators - static bool SimplifyShiftOperators(List<ILNode> body, ILExpression expr, int pos) - { - // C# compiles "a << b" to "a << (b & 31)", so we will remove the "& 31" if possible. - bool modified = false; - SimplifyShiftOperators(expr, ref modified); - return modified; - } - - static void SimplifyShiftOperators(ILExpression expr, ref bool modified) - { - for (int i = 0; i < expr.Arguments.Count; i++) - SimplifyShiftOperators(expr.Arguments[i], ref modified); - if (expr.Code != ILCode.Shl && expr.Code != ILCode.Shr && expr.Code != ILCode.Shr_Un) - return; - var a = expr.Arguments[1]; - if (a.Code != ILCode.And || a.Arguments[1].Code != ILCode.Ldc_I4 || expr.InferredType == null) - return; - int mask; - switch (expr.InferredType.MetadataType) { - case MetadataType.Int32: - case MetadataType.UInt32: mask = 31; break; - case MetadataType.Int64: - case MetadataType.UInt64: mask = 63; break; - default: return; - } - if ((int)a.Arguments[1].Operand != mask) return; - var res = a.Arguments[0]; - res.ILRanges.AddRange(a.ILRanges); - res.ILRanges.AddRange(a.Arguments[1].ILRanges); - expr.Arguments[1] = res; - modified = true; - } - #endregion - - #region InlineExpressionTreeParameterDeclarations - bool InlineExpressionTreeParameterDeclarations(List<ILNode> body, ILExpression expr, int pos) - { - // When there is a Expression.Lambda() call, and the parameters are declared in the - // IL statement immediately prior to the one containing the Lambda() call, - // using this code for the3 declaration: - // stloc(v, call(Expression::Parameter, call(Type::GetTypeFromHandle, ldtoken(...)), ldstr(...))) - // and the variables v are assigned only once (in that statements), and read only in a Expression::Lambda - // call that immediately follows the assignment statements, then we will inline those assignments - // into the Lambda call using ILCode.ExpressionTreeParameterDeclarations. - - // This is sufficient to allow inlining over the expression tree construction. The remaining translation - // of expression trees into C# will be performed by a C# AST transformer. - - for (int i = expr.Arguments.Count - 1; i >= 0; i--) { - if (InlineExpressionTreeParameterDeclarations(body, expr.Arguments[i], pos)) - return true; - } - - MethodReference mr; - ILExpression lambdaBodyExpr, parameterArray; - if (!(expr.Match(ILCode.Call, out mr, out lambdaBodyExpr, out parameterArray) && mr.Name == "Lambda")) - return false; - if (!(parameterArray.Code == ILCode.InitArray && mr.DeclaringType.FullName == "System.Linq.Expressions.Expression")) - return false; - int firstParameterPos = pos - parameterArray.Arguments.Count; - if (firstParameterPos < 0) - return false; - - ILExpression[] parameterInitExpressions = new ILExpression[parameterArray.Arguments.Count + 1]; - for (int i = 0; i < parameterArray.Arguments.Count; i++) { - parameterInitExpressions[i] = body[firstParameterPos + i] as ILExpression; - if (!MatchParameterVariableAssignment(parameterInitExpressions[i])) - return false; - ILVariable v = (ILVariable)parameterInitExpressions[i].Operand; - if (!parameterArray.Arguments[i].MatchLdloc(v)) - return false; - // TODO: validate that the variable is only used here and within 'body' - } - - parameterInitExpressions[parameterInitExpressions.Length - 1] = lambdaBodyExpr; - Debug.Assert(expr.Arguments[0] == lambdaBodyExpr); - expr.Arguments[0] = new ILExpression(ILCode.ExpressionTreeParameterDeclarations, null, parameterInitExpressions); - - body.RemoveRange(firstParameterPos, parameterArray.Arguments.Count); - - return true; - } - - bool MatchParameterVariableAssignment(ILExpression expr) - { - // stloc(v, call(Expression::Parameter, call(Type::GetTypeFromHandle, ldtoken(...)), ldstr(...))) - ILVariable v; - ILExpression init; - if (!expr.Match(ILCode.Stloc, out v, out init)) - return false; - if (v.IsGenerated || v.IsParameter || v.IsPinned) - return false; - if (v.Type == null || v.Type.FullName != "System.Linq.Expressions.ParameterExpression") - return false; - MethodReference parameterMethod; - ILExpression typeArg, nameArg; - if (!init.Match(ILCode.Call, out parameterMethod, out typeArg, out nameArg)) - return false; - if (!(parameterMethod.Name == "Parameter" && parameterMethod.DeclaringType.FullName == "System.Linq.Expressions.Expression")) - return false; - MethodReference getTypeFromHandle; - ILExpression typeToken; - if (!typeArg.Match(ILCode.Call, out getTypeFromHandle, out typeToken)) - return false; - if (!(getTypeFromHandle.Name == "GetTypeFromHandle" && getTypeFromHandle.DeclaringType.FullName == "System.Type")) - return false; - return typeToken.Code == ILCode.Ldtoken && nameArg.Code == ILCode.Ldstr; - } - #endregion - } -} |