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; } 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); } }
bool MatchLogicNot(ILExpression expr, out ILExpression arg) { ILExpression loadZero; object unused; if (expr.Match(ILCode.Ceq, out unused, out arg, out loadZero)) { int num; return loadZero.Match(ILCode.Ldc_I4, out num) && num == 0; } return expr.Match(ILCode.LogicNot, out arg); }
static bool SimplifyLdObjAndStObj(List <ILNode> body, ILExpression expr, int pos) { bool modified = false; if (expr.Code == ILCode.Initobj) { expr.Code = ILCode.Stobj; expr.Arguments.Add(new ILExpression(ILCode.DefaultValue, expr.Operand)); 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.Arguments.Add(arg2); } arg.ILRanges.AddRange(expr.ILRanges); body[pos] = arg; modified = true; } return(modified); }
/// <summary> /// Gets whether 'expr' represents the invocation of an 'Add' method in a collection initializer. /// </summary> private static bool IsAddMethodCall(ILExpression expr) { MethodReference addMethod; List <ILExpression> args; if (expr.Match(ILCode.Callvirt, out addMethod, out args) || expr.Match(ILCode.Call, out addMethod, out args)) { if (addMethod.Name == "Add" && addMethod.HasThis) { return(args.Count >= 2); } } return(false); }
/// <summary> /// Gets whether 'expr' represents the invocation of an 'Add' method in a collection initializer. /// </summary> static bool IsAddMethodCall(ILExpression expr) { IMethod addMethod; List <ILExpression> args; if (expr.Match(ILCode.Callvirt, out addMethod, out args) || expr.Match(ILCode.Call, out addMethod, out args)) { if (addMethod.Name == "Add" && addMethod.MethodSig != null && addMethod.MethodSig.HasThis) { return(args.Count >= 2); } } return(false); }
private ILExpression MakeLeftAssociativeShortCircuit(ILCode code, ILExpression left, ILExpression right) { // Assuming that the inputs are already left associative if (right.Match(code)) { // Find the leftmost logical expression ILExpression current = right; while (current.Arguments[0].Match(code)) { current = current.Arguments[0]; } current.Arguments[0] = new ILExpression(code, null, left, current.Arguments[0]) { InferredType = typeSystem.Boolean }; return(right); } else { return(new ILExpression(code, null, left, right) { InferredType = typeSystem.Boolean }); } }
ILExpression MakeLeftAssociativeShortCircuit(ILCode code, ILExpression left, ILExpression right) { // Assuming that the inputs are already left associative if (right.Match(code)) { // Find the leftmost logical expression ILExpression current = right; while (current.Arguments[0].Match(code)) { current = current.Arguments[0]; } current.Arguments[0].AddSelfAndChildrenRecursiveILRanges(current.ILRanges); current.Arguments[0] = new ILExpression(code, null, left, current.Arguments[0]) { InferredType = corLib.Boolean }; return(right); } else { return(new ILExpression(code, null, left, right) { InferredType = corLib.Boolean }); } }
bool IsBuilderFieldOnThis(ILExpression builderExpr) { // ldflda(StateMachine::<>t__builder, ldloc(this)) IField fieldRef; ILExpression target; return builderExpr.Match(ILCode.Ldflda, out fieldRef, out target) && fieldRef.ResolveFieldWithinSameModule() == builderField && target.MatchThis(); }
ILExpression UnpackDoubleNegation(ILExpression expr) { ILExpression negated; if (expr.Match(ILCode.LogicNot, out negated) && negated.Match(ILCode.LogicNot, out negated)) { return(negated); } else { return(expr); } }
public static bool TransformCollectionInitializers(List <ILNode> body, ILExpression expr, int pos) { ILVariable v, v2; ILExpression newObjExpr; MethodReference ctor; List <ILExpression> ctorArgs; if (expr.Match(ILCode.Stloc, out v, out newObjExpr) && newObjExpr.Match(ILCode.Newobj, out ctor, out ctorArgs)) { TypeDefinition td = ctor.DeclaringType.Resolve(); if (td == null || !td.Interfaces.Any(intf => intf.Name == "IEnumerable" && intf.Namespace == "System.Collections")) { return(false); } // This is a collection: we can convert Add() calls into a collection initializer ILExpression collectionInitializer = new ILExpression(ILCode.InitCollection, null, newObjExpr); bool anyAdded = false; while (pos + 1 < body.Count) { ILExpression nextExpr = body[pos + 1] as ILExpression; MethodReference addMethod; List <ILExpression> args; if (nextExpr.Match(ILCode.Callvirt, out addMethod, out args) && addMethod.Name == "Add" && addMethod.HasThis && args.Count >= 2 && args[0].Match(ILCode.Ldloc, out v2) && v == v2) { nextExpr.Code = ILCode.InitCollectionAddMethod; nextExpr.Arguments.RemoveAt(0); collectionInitializer.Arguments.Add(nextExpr); body.RemoveAt(pos + 1); anyAdded = true; } else { break; } } // ensure we added at least one additional arg to the collection initializer: if (anyAdded) { expr.Arguments[0] = collectionInitializer; return(true); } } 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); }
public static void NopMergeILRanges(ILBlockBase block, List <ILNode> newBody, int instrIndexToRemove) { var body = block.Body; ILNode prevNode = null, nextNode = null; ILExpression prev = null, next = null; if (newBody.Count > 0) { prev = (prevNode = newBody[newBody.Count - 1]) as ILExpression; } if (instrIndexToRemove + 1 < body.Count) { next = (nextNode = body[instrIndexToRemove + 1]) as ILExpression; } ILNode node = null; if (prev != null && prev.Prefixes == null) { switch (prev.Code) { case ILCode.Call: case ILCode.CallGetter: case ILCode.Calli: case ILCode.CallSetter: case ILCode.Callvirt: case ILCode.CallvirtGetter: case ILCode.CallvirtSetter: node = prev; break; } } if (next != null && next.Prefixes == null) { if (next.Match(ILCode.Leave)) { node = next; } } if (node != null && node == prevNode) { AddILRangesTryPreviousFirst(body[instrIndexToRemove], prevNode, nextNode, block); } else { AddILRangesTryNextFirst(body[instrIndexToRemove], prevNode, nextNode, block); } }
bool TransformMultidimensionalArrayInitializers(ILBlockBase block, List <ILNode> body, ILExpression expr, int pos) { ILVariable v; ILExpression newarrExpr; IMethod ctor; List <ILExpression> ctorArgs; TypeSpec arySpec; ArraySigBase arrayType; if (expr.Match(ILCode.Stloc, out v, out newarrExpr) && newarrExpr.Match(ILCode.Newobj, out ctor, out ctorArgs) && (arySpec = (ctor.DeclaringType as TypeSpec)) != null && (arrayType = arySpec.TypeSig.RemovePinnedAndModifiers() as ArraySigBase) != null && arrayType.Rank == ctorArgs.Count) { // Clone the type, so we can muck about with the Dimensions var multAry = new ArraySig(arrayType.Next, arrayType.Rank, new uint[arrayType.Rank], new int[arrayType.Rank]); var arrayLengths = new int[multAry.Rank]; for (int i = 0; i < multAry.Rank; i++) { if (!ctorArgs[i].Match(ILCode.Ldc_I4, out arrayLengths[i])) { return(false); } if (arrayLengths[i] <= 0) { return(false); } multAry.Sizes[i] = (uint)(arrayLengths[i] + 1); multAry.LowerBounds[i] = 0; } var totalElements = arrayLengths.Aggregate(1, (t, l) => t * l); ILExpression[] newArr; int initArrayPos; if (ForwardScanInitializeArrayRuntimeHelper(body, pos + 1, v, multAry, totalElements, out newArr, out initArrayPos)) { var newStloc = new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.InitArray, multAry.ToTypeDefOrRef(), newArr)); if (context.CalculateILRanges) { body[pos].AddSelfAndChildrenRecursiveILRanges(newStloc.ILRanges); body[initArrayPos].AddSelfAndChildrenRecursiveILRanges(newStloc.ILRanges); } body[pos] = newStloc; body.RemoveAt(initArrayPos); return(true); } } return(false); }
bool MakeAssignmentExpression(List <ILNode> body, ILExpression expr, int pos) { // exprVar = ... // stloc(v, exprVar) // -> // exprVar = stloc(v, ...)) ILExpression nextExpr = body.ElementAtOrDefault(pos + 1) as ILExpression; ILVariable exprVar; ILExpression initializer; ILVariable v; ILExpression stLocArg; if (expr.Match(ILCode.Stloc, out exprVar, out initializer) && exprVar.IsGenerated && nextExpr.Match(ILCode.Stloc, out v, out stLocArg) && stLocArg.Match(ILCode.Ldloc, 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); } return(false); }
static bool TransformDecimalCtorToConstant(List <ILNode> body, ILExpression expr, int pos) { 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); } } } bool modified = false; foreach (ILExpression arg in expr.Arguments) { modified |= TransformDecimalCtorToConstant(null, arg, -1); } return(modified); }
bool TransformMultidimensionalArrayInitializers(List <ILNode> body, ILExpression expr, int pos) { ILVariable v; ILExpression newarrExpr; IMethod ctor; List <ILExpression> ctorArgs; ArraySig arrayType; if (expr.Match(ILCode.Stloc, out v, out newarrExpr) && newarrExpr.Match(ILCode.Newobj, out ctor, out ctorArgs) && (arrayType = ctor.DeclaringType.TryGetArraySig()) != null && arrayType.Rank == ctorArgs.Count) { // Clone the type, so we can muck about with the Dimensions arrayType = new ArraySig(arrayType.Next, arrayType.Rank); var arrayLengths = new int[arrayType.Rank]; for (int i = 0; i < arrayType.Rank; i++) { if (!ctorArgs[i].Match(ILCode.Ldc_I4, out arrayLengths[i])) { return(false); } if (arrayLengths[i] <= 0) { return(false); } arrayType.LowerBounds.Add(0); arrayType.Sizes.Add((uint)arrayLengths[i]); } var totalElements = arrayLengths.Aggregate(1, (t, l) => t * l); ILExpression[] newArr; int initArrayPos; if (ForwardScanInitializeArrayRuntimeHelper(body, pos + 1, v, arrayType, totalElements, out newArr, out initArrayPos)) { body[pos] = new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.InitArray, arrayType, newArr)); body.RemoveAt(initArrayPos); return(true); } } return(false); }
bool TransformMultidimensionalArrayInitializers(List <ILNode> body, ILExpression expr, int pos) { ILVariable v; ILExpression newarrExpr; MethodReference ctor; List <ILExpression> ctorArgs; ArrayType arrayType; if (expr.Match(ILCode.Stloc, out v, out newarrExpr) && newarrExpr.Match(ILCode.Newobj, out ctor, out ctorArgs) && (arrayType = (ctor.DeclaringType as ArrayType)) != null && arrayType.Rank == ctorArgs.Count) { // Clone the type, so we can muck about with the Dimensions arrayType = new ArrayType(arrayType.ElementType, arrayType.Rank); var arrayLengths = new int[arrayType.Rank]; for (int i = 0; i < arrayType.Rank; i++) { if (!ctorArgs[i].Match(ILCode.Ldc_I4, out arrayLengths[i])) { return(false); } if (arrayLengths[i] <= 0) { return(false); } arrayType.Dimensions[i] = new ArrayDimension(0, arrayLengths[i]); } var totalElements = arrayLengths.Aggregate(1, (t, l) => t * l); ILExpression[] newArr; int initArrayPos; if (ForwardScanInitializeArrayRuntimeHelper(body, pos + 1, v, arrayType, totalElements, out newArr, out initArrayPos)) { var mdArr = Array.CreateInstance(typeof(ILExpression), arrayLengths); body[pos] = new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.InitArray, arrayType, newArr)); body.RemoveAt(initArrayPos); return(true); } } return(false); }
static bool SimplifyLdcI4ConvI8(List <ILNode> body, ILExpression expr, int pos) { 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); } bool modified = false; foreach (ILExpression arg in expr.Arguments) { modified |= SimplifyLdcI4ConvI8(null, arg, -1); } 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); }
bool TransformArrayInitializers(ILBlockBase block, List <ILNode> body, ILExpression expr, int pos) { ILVariable v, v3; ILExpression newarrExpr; ITypeDefOrRef elementType; ILExpression lengthExpr; int arrayLength; if (expr.Match(ILCode.Stloc, out v, out newarrExpr) && newarrExpr.Match(ILCode.Newarr, out elementType, out lengthExpr) && lengthExpr.Match(ILCode.Ldc_I4, out arrayLength) && arrayLength > 0) { ILExpression[] newArr; int initArrayPos; if (ForwardScanInitializeArrayRuntimeHelper(body, pos + 1, v, new SZArraySig(elementType.ToTypeSig()), arrayLength, out newArr, out initArrayPos)) { var arrayType = new ArraySig(elementType.ToTypeSig(), 1, new uint[1], new int[1]); arrayType.Sizes[0] = (uint)(arrayLength + 1); var newStloc = new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.InitArray, arrayType.ToTypeDefOrRef(), newArr)); body[pos].AddSelfAndChildrenRecursiveILRanges(newStloc.ILRanges); body[initArrayPos].AddSelfAndChildrenRecursiveILRanges(newStloc.ILRanges); body[pos] = newStloc; body.RemoveAt(initArrayPos); } // Put in a limit so that we don't consume too much memory if the code allocates a huge array // and populates it extremely sparsly. However, 255 "null" elements in a row actually occur in the Mono C# compiler! const int maxConsecutiveDefaultValueExpressions = 300; List <ILExpression> operands = new List <ILExpression>(); int numberOfInstructionsToRemove = 0; for (int j = pos + 1; j < body.Count; j++) { ILExpression nextExpr = body[j] as ILExpression; int arrayPos; if (nextExpr != null && nextExpr.Code.IsStoreToArray() && nextExpr.Arguments[0].Match(ILCode.Ldloc, out v3) && v == v3 && nextExpr.Arguments[1].Match(ILCode.Ldc_I4, out arrayPos) && arrayPos >= operands.Count && arrayPos <= operands.Count + maxConsecutiveDefaultValueExpressions && !nextExpr.Arguments[2].ContainsReferenceTo(v3)) { while (operands.Count < arrayPos) { operands.Add(new ILExpression(ILCode.DefaultValue, elementType)); } operands.Add(nextExpr.Arguments[2]); numberOfInstructionsToRemove++; } else { break; } } if (operands.Count == arrayLength) { var arrayType = new ArraySig(elementType.ToTypeSig(), 1, new uint[1], new int[1]); arrayType.Sizes[0] = (uint)(arrayLength + 1); expr.Arguments[0] = new ILExpression(ILCode.InitArray, arrayType.ToTypeDefOrRef(), operands); newarrExpr.AddSelfAndChildrenRecursiveILRanges(expr.ILRanges); for (int i = 0; i < numberOfInstructionsToRemove; i++) { body[pos + 1 + i].AddSelfAndChildrenRecursiveILRanges(expr.ILRanges); } body.RemoveRange(pos + 1, numberOfInstructionsToRemove); GetILInlining(method).InlineIfPossible(block, body, ref pos); return(true); } } return(false); }
/// <summary> /// Handles both object and collection initializers. /// </summary> bool TransformObjectInitializers(ILBlockBase block, List <ILNode> body, ILExpression expr, int pos) { if (!context.Settings.ObjectOrCollectionInitializers) { return(false); } Debug.Assert(body[pos] == expr); // should be called for top-level expressions only ILVariable v; ILExpression newObjExpr; ITypeDefOrRef newObjType; bool isValueType; IMethod ctor; List <ILExpression> ctorArgs; if (expr.Match(ILCode.Stloc, out v, out newObjExpr)) { if (newObjExpr.Match(ILCode.Newobj, out ctor, out ctorArgs)) { // v = newObj(ctor, ctorArgs) newObjType = ctor.DeclaringType; isValueType = false; } else if (newObjExpr.Match(ILCode.DefaultValue, out newObjType)) { // v = defaultvalue(type) isValueType = true; } else { return(false); } } else if (expr.Match(ILCode.Call, out ctor, out ctorArgs)) { // call(SomeStruct::.ctor, ldloca(v), remainingArgs) if (ctorArgs.Count > 0 && ctorArgs[0].Match(ILCode.Ldloca, out v)) { isValueType = true; newObjType = ctor.DeclaringType; ctorArgs = new List <ILExpression>(ctorArgs); var old = ctorArgs[0]; ctorArgs.RemoveAt(0); newObjExpr = new ILExpression(ILCode.Newobj, ctor, ctorArgs); old.AddSelfAndChildrenRecursiveILRanges(newObjExpr.ILRanges); } else { return(false); } } else { return(false); } if (DnlibExtensions.IsValueType(newObjType) != isValueType) { return(false); } int originalPos = pos; // don't use object initializer syntax for closures if (Ast.Transforms.DelegateConstruction.IsPotentialClosure(context, newObjType.ResolveWithinSameModule())) { return(false); } ILExpression initializer = ParseObjectInitializer(body, ref pos, v, newObjExpr, IsCollectionType(newObjType.ToTypeSig()), isValueType); if (initializer.Arguments.Count == 1) // only newobj argument, no initializer elements { return(false); } int totalElementCount = pos - originalPos - 1; // totalElementCount: includes elements from nested collections Debug.Assert(totalElementCount >= initializer.Arguments.Count - 1); // Verify that we can inline 'v' into the next instruction: if (pos >= body.Count) { return(false); // reached end of block, but there should be another instruction which consumes the initialized object } var inlining = GetILInlining(method); if (isValueType) { // one ldloc for the use of the initialized object if (inlining.numLdloc.GetOrDefault(v) != 1) { return(false); } // one ldloca for each initializer argument, and also for the ctor call (if it exists) if (inlining.numLdloca.GetOrDefault(v) != totalElementCount + (expr.Code == ILCode.Call ? 1 : 0)) { return(false); } // one stloc for the initial store (if no ctor call was used) if (inlining.numStloc.GetOrDefault(v) != (expr.Code == ILCode.Call ? 0 : 1)) { return(false); } } else { // one ldloc for each initializer argument, and another ldloc for the use of the initialized object if (inlining.numLdloc.GetOrDefault(v) != totalElementCount + 1) { return(false); } if (!(inlining.numStloc.GetOrDefault(v) == 1 && inlining.numLdloca.GetOrDefault(v) == 0)) { return(false); } } ILExpression nextExpr = body[pos] as ILExpression; if (!inlining.CanInlineInto(nextExpr, v, initializer)) { return(false); } if (expr.Code == ILCode.Stloc) { expr.Arguments[0].AddSelfAndChildrenRecursiveILRanges(initializer.ILRanges); expr.Arguments[0] = initializer; } else { Debug.Assert(expr.Code == ILCode.Call); expr.Code = ILCode.Stloc; expr.Operand = v; foreach (var arg in expr.Arguments) { arg.AddSelfAndChildrenRecursiveILRanges(initializer.ILRanges); } expr.Arguments.Clear(); expr.Arguments.Add(initializer); } // remove all the instructions that were pulled into the initializer for (int i = originalPos + 1; i < pos; i++) { body[i].AddSelfAndChildrenRecursiveILRanges(initializer.ILRanges); } body.RemoveRange(originalPos + 1, pos - originalPos - 1); // now that we know that it's an object initializer, change all the first arguments to 'InitializedObject' ChangeFirstArgumentToInitializedObject(initializer); inlining = GetILInlining(method); inlining.InlineIfPossible(block, body, ref originalPos); return(true); }
bool TransformArrayInitializers(List <ILNode> body, ILExpression expr, int pos) { ILVariable v, v3; ILExpression newarrExpr; TypeReference elementType; ILExpression lengthExpr; int arrayLength; if (expr.Match(ILCode.Stloc, out v, out newarrExpr) && newarrExpr.Match(ILCode.Newarr, out elementType, out lengthExpr) && lengthExpr.Match(ILCode.Ldc_I4, out arrayLength) && arrayLength > 0) { ILExpression[] newArr; int initArrayPos; if (ForwardScanInitializeArrayRuntimeHelper(body, pos + 1, v, elementType, arrayLength, out newArr, out initArrayPos)) { var arrayType = new ArrayType(elementType, 1); arrayType.Dimensions[0] = new ArrayDimension(0, arrayLength); body[pos] = new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.InitArray, arrayType, newArr)); body.RemoveAt(initArrayPos); } // Put in a limit so that we don't consume too much memory if the code allocates a huge array // and populates it extremely sparsly. However, 255 "null" elements in a row actually occur in the Mono C# compiler! const int maxConsecutiveDefaultValueExpressions = 300; List <ILExpression> operands = new List <ILExpression>(); int numberOfInstructionsToRemove = 0; for (int j = pos + 1; j < body.Count; j++) { ILExpression nextExpr = body[j] as ILExpression; int arrayPos; if (nextExpr != null && nextExpr.Code.IsStoreToArray() && nextExpr.Arguments[0].Match(ILCode.Ldloc, out v3) && v == v3 && nextExpr.Arguments[1].Match(ILCode.Ldc_I4, out arrayPos) && arrayPos >= operands.Count && arrayPos <= operands.Count + maxConsecutiveDefaultValueExpressions) { while (operands.Count < arrayPos) { operands.Add(new ILExpression(ILCode.DefaultValue, elementType)); } operands.Add(nextExpr.Arguments[2]); numberOfInstructionsToRemove++; } else { break; } } if (operands.Count == arrayLength) { var arrayType = new ArrayType(elementType, 1); arrayType.Dimensions[0] = new ArrayDimension(0, arrayLength); expr.Arguments[0] = new ILExpression(ILCode.InitArray, arrayType, operands); body.RemoveRange(pos + 1, numberOfInstructionsToRemove); new ILInlining(method).InlineIfPossible(body, ref pos); return(true); } } return(false); }
/// <summary> /// Handles both object and collection initializers. /// </summary> bool TransformObjectInitializers(List <ILNode> body, ILExpression expr, int pos) { if (!context.Settings.ObjectOrCollectionInitializers) { return(false); } Debug.Assert(body[pos] == expr); // should be called for top-level expressions only ILVariable v; ILExpression newObjExpr; MethodReference ctor; List <ILExpression> ctorArgs; // v = newObj(ctor, ctorArgs) if (!(expr.Match(ILCode.Stloc, out v, out newObjExpr) && newObjExpr.Match(ILCode.Newobj, out ctor, out ctorArgs))) { return(false); } int originalPos = pos; // don't use object initializer syntax for closures if (Ast.Transforms.DelegateConstruction.IsPotentialClosure(context, ctor.DeclaringType.ResolveWithinSameModule())) { return(false); } ILExpression initializer = ParseObjectInitializer(body, ref pos, v, newObjExpr, IsCollectionType(ctor.DeclaringType)); if (initializer.Arguments.Count == 1) // only newobj argument, no initializer elements { return(false); } int totalElementCount = pos - originalPos - 1; // totalElementCount: includes elements from nested collections Debug.Assert(totalElementCount >= initializer.Arguments.Count - 1); // Verify that we can inline 'v' into the next instruction: if (pos >= body.Count) { return(false); // reached end of block, but there should be another instruction which consumes the initialized object } ILInlining inlining = new ILInlining(method); // one ldloc for each initializer argument, and another ldloc for the use of the initialized object if (inlining.numLdloc.GetOrDefault(v) != totalElementCount + 1) { return(false); } if (!(inlining.numStloc.GetOrDefault(v) == 1 && inlining.numLdloca.GetOrDefault(v) == 0)) { return(false); } ILExpression nextExpr = body[pos] as ILExpression; if (!inlining.CanInlineInto(nextExpr, v, initializer)) { return(false); } expr.Arguments[0] = initializer; // remove all the instructions that were pulled into the initializer body.RemoveRange(originalPos + 1, pos - originalPos - 1); // now that we know that it's an object initializer, change all the first arguments to 'InitializedObject' ChangeFirstArgumentToInitializedObject(initializer); inlining = new ILInlining(method); inlining.InlineIfPossible(body, ref originalPos); return(true); }
bool AdjustInitializerStack(List <ILExpression> initializerStack, List <ILExpression> getters, ILExpression argument, ILVariable v, bool isCollection, bool isValueType) { // Argument is of the form 'getter(getter(...(v)))' // Unpack it into a list of getters: getters.Clear(); while (argument.Code == ILCode.CallvirtGetter || argument.Code == ILCode.CallGetter || argument.Code == ILCode.Ldfld) { getters.Add(argument); if (argument.Arguments.Count != 1) { return(false); } argument = argument.Arguments[0]; } // Ensure that the final argument is 'v' if (isValueType) { ILVariable loadedVar; if (!(argument.Match(ILCode.Ldloca, out loadedVar) && loadedVar == v)) { return(false); } } else { if (!argument.MatchLdloc(v)) { return(false); } } // Now compare the getters with those that are currently active on the initializer stack: int i; for (i = 1; i <= Math.Min(getters.Count, initializerStack.Count - 1); i++) { ILExpression g1 = initializerStack[i].Arguments[0]; // getter stored in initializer ILExpression g2 = getters[getters.Count - i]; // matching getter from argument if (g1.Operand != g2.Operand) { // operands differ, so we abort the comparison break; } } // Remove all initializers from the stack that were not matched with one from the argument: initializerStack.RemoveRange(i, initializerStack.Count - i); // Now create new initializers for the remaining arguments: for (; i <= getters.Count; i++) { ILExpression g = getters[getters.Count - i]; IMemberRef mr = g.Operand as IMemberRef; TypeSig returnType; if (mr == null || mr.IsField) { returnType = TypeAnalysis.GetFieldType((IField)mr); } else { returnType = TypeAnalysis.SubstituteTypeArgs(((IMethod)mr).MethodSig.GetRetType(), method: (IMethod)mr); } ILExpression nestedInitializer = new ILExpression( IsCollectionType(returnType) ? ILCode.InitCollection : ILCode.InitObject, null, g); // add new initializer to its parent: ILExpression parentInitializer = initializerStack[initializerStack.Count - 1]; if (parentInitializer.Code == ILCode.InitCollection) { // can't add children to collection initializer if (parentInitializer.Arguments.Count == 1) { // convert empty collection initializer to object initializer parentInitializer.Code = ILCode.InitObject; } else { return(false); } } parentInitializer.Arguments.Add(nestedInitializer); initializerStack.Add(nestedInitializer); } ILExpression lastInitializer = initializerStack[initializerStack.Count - 1]; if (isCollection) { return(lastInitializer.Code == ILCode.InitCollection); } else { if (lastInitializer.Code == ILCode.InitCollection) { if (lastInitializer.Arguments.Count == 1) { // convert empty collection initializer to object initializer lastInitializer.Code = ILCode.InitObject; return(true); } else { return(false); } } else { return(true); } } }
public static bool TransformArrayInitializers(List <ILNode> body, ILExpression expr, int pos) { ILVariable v, v2, v3; ILExpression newarrExpr; TypeReference arrayType; ILExpression lengthExpr; int arrayLength; if (expr.Match(ILCode.Stloc, out v, out newarrExpr) && newarrExpr.Match(ILCode.Newarr, out arrayType, out lengthExpr) && lengthExpr.Match(ILCode.Ldc_I4, out arrayLength) && arrayLength > 0) { MethodReference methodRef; ILExpression methodArg1; ILExpression methodArg2; FieldDefinition field; if (body.ElementAtOrDefault(pos + 1).Match(ILCode.Call, out methodRef, out methodArg1, out methodArg2) && methodRef.DeclaringType.FullName == "System.Runtime.CompilerServices.RuntimeHelpers" && methodRef.Name == "InitializeArray" && methodArg1.Match(ILCode.Ldloc, out v2) && v == v2 && methodArg2.Match(ILCode.Ldtoken, out field) && field != null && field.InitialValue != null) { ILExpression[] newArr = new ILExpression[arrayLength]; if (DecodeArrayInitializer(TypeAnalysis.GetTypeCode(arrayType), field.InitialValue, newArr)) { body[pos] = new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.InitArray, arrayType, newArr)); body.RemoveAt(pos + 1); return(true); } } const int maxConsecutiveDefaultValueExpressions = 10; List <ILExpression> operands = new List <ILExpression>(); int numberOfInstructionsToRemove = 0; for (int j = pos + 1; j < body.Count; j++) { ILExpression nextExpr = body[j] as ILExpression; int arrayPos; if (nextExpr != null && nextExpr.Code.IsStoreToArray() && nextExpr.Arguments[0].Match(ILCode.Ldloc, out v3) && v == v3 && nextExpr.Arguments[1].Match(ILCode.Ldc_I4, out arrayPos) && arrayPos >= operands.Count && arrayPos <= operands.Count + maxConsecutiveDefaultValueExpressions) { while (operands.Count < arrayPos) { operands.Add(new ILExpression(ILCode.DefaultValue, arrayType)); } operands.Add(nextExpr.Arguments[2]); numberOfInstructionsToRemove++; } else { break; } } if (operands.Count == arrayLength) { expr.Arguments[0] = new ILExpression(ILCode.InitArray, arrayType, operands); body.RemoveRange(pos + 1, numberOfInstructionsToRemove); return(true); } } return(false); }
static bool Step2(List <ILNode> body, ref int pos) { // stloc(CS$0$0001, callvirt(class System.Threading.Tasks.Task`1<bool>::GetAwaiter, awaiterExpr) // brtrue(IL_7C, call(valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool>::get_IsCompleted, ldloca(CS$0$0001))) // await(ldloca(CS$0$0001)) // ... // IL_7C: // arg_8B_0 = call(valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool>::GetResult, ldloca(CS$0$0001)) // initobj(valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool>, ldloca(CS$0$0001)) ILExpression loadAwaiter; ILVariable awaiterVar; if (!body[pos].Match(ILCode.Await, out loadAwaiter)) { return(false); } if (!loadAwaiter.Match(ILCode.Ldloca, out awaiterVar)) { return(false); } ILVariable stackVar; ILExpression stackExpr; while (pos >= 1 && body[pos - 1].Match(ILCode.Stloc, out stackVar, out stackExpr)) { pos--; } // stloc(CS$0$0001, callvirt(class System.Threading.Tasks.Task`1<bool>::GetAwaiter, awaiterExpr) ILExpression getAwaiterCall; if (!(pos >= 2 && body[pos - 2].MatchStloc(awaiterVar, out getAwaiterCall))) { return(false); } MethodReference getAwaiterMethod; ILExpression awaitedExpr; if (!(getAwaiterCall.Match(ILCode.Call, out getAwaiterMethod, out awaitedExpr) || getAwaiterCall.Match(ILCode.Callvirt, out getAwaiterMethod, out awaitedExpr))) { return(false); } if (awaitedExpr.Code == ILCode.AddressOf) { // remove 'AddressOf()' when calling GetAwaiter() on a value type awaitedExpr = awaitedExpr.Arguments[0]; } // brtrue(IL_7C, call(valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool>::get_IsCompleted, ldloca(CS$0$0001))) ILLabel label; ILExpression getIsCompletedCall; if (!(pos >= 1 && body[pos - 1].Match(ILCode.Brtrue, out label, out getIsCompletedCall))) { return(false); } int labelPos = body.IndexOf(label); if (labelPos < pos) { return(false); } for (int i = pos + 1; i < labelPos; i++) { // validate that we aren't deleting any unexpected instructions - // between the await and the label, there should only be the stack, awaiter and state logic ILExpression expr = body[i] as ILExpression; if (expr == null) { return(false); } switch (expr.Code) { case ILCode.Stloc: case ILCode.Initobj: case ILCode.Stfld: case ILCode.Await: // e.g. // stloc(CS$0$0001, ldfld(StateMachine::<>u__$awaitere, ldloc(this))) // initobj(valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool>, ldloca(CS$0$0002_66)) // stfld('<AwaitInLoopCondition>d__d'::<>u__$awaitere, ldloc(this), ldloc(CS$0$0002_66)) // stfld('<AwaitInLoopCondition>d__d'::<>1__state, ldloc(this), ldc.i4(-1)) break; default: return(false); } } if (labelPos + 1 >= body.Count) { return(false); } ILExpression resultAssignment = body[labelPos + 1] as ILExpression; ILVariable resultVar; ILExpression getResultCall; bool isResultAssignment = resultAssignment.Match(ILCode.Stloc, out resultVar, out getResultCall); if (!isResultAssignment) { getResultCall = resultAssignment; } if (!(getResultCall.Operand is MethodReference && ((MethodReference)getResultCall.Operand).Name == "GetResult")) { return(false); } pos -= 2; // also delete 'stloc', 'brtrue' and 'await' body.RemoveRange(pos, labelPos - pos); Debug.Assert(body[pos] == label); pos++; if (isResultAssignment) { Debug.Assert(body[pos] == resultAssignment); resultAssignment.Arguments[0] = new ILExpression(ILCode.Await, null, awaitedExpr); } else { body[pos] = new ILExpression(ILCode.Await, null, awaitedExpr); } // if the awaiter variable is cleared out in the next instruction, remove that instruction if (IsVariableReset(body.ElementAtOrDefault(pos + 1), awaiterVar)) { body.RemoveAt(pos + 1); } return(true); }
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); } if (!(exprInit.Code == ILCode.Ldloc || exprInit.Code == ILCode.Ldsfld || (exprInit.Code == ILCode.CallGetter && exprInit.Arguments.Count == 0))) { return(false); } ILExpression nextExpr = body.ElementAtOrDefault(pos + 1) as ILExpression; if (nextExpr == null) { return(false); } if (exprInit.Code == ILCode.CallGetter) { if (!(nextExpr.Code == ILCode.CallSetter && IsGetterSetterPair(exprInit.Operand, nextExpr.Operand))) { return(false); } } else { if (!(nextExpr.Code == (exprInit.Code == ILCode.Ldloc ? ILCode.Stloc : ILCode.Stsfld) && nextExpr.Operand == exprInit.Operand)) { 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 (exprInit.Code == ILCode.Ldloc) { exprInit.Code = ILCode.Ldloca; } else if (exprInit.Code == ILCode.CallGetter) { exprInit = new ILExpression(ILCode.AddressOf, null, exprInit); } else { exprInit.Code = ILCode.Ldsflda; } expr.Arguments[0] = new ILExpression(incrementCode, incrementAmount, exprInit); body.RemoveAt(pos + 1); // TODO ILRanges return(true); }