mirror of https://github.com/icsharpcode/ILSpy.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1103 lines
40 KiB
1103 lines
40 KiB
// 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 |
|
} |
|
}
|
|
|