private 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); }
private static bool MatchStFld(ILNode stfld, ILVariable stateMachineVar, out FieldDefinition field, out ILExpression expr) { field = null; FieldReference fieldRef; ILExpression ldloca; if (!stfld.Match(ILCode.Stfld, out fieldRef, out ldloca, out expr)) { return(false); } field = fieldRef.ResolveWithinSameModule(); return(field != null && ldloca.MatchLdloca(stateMachineVar)); }
private void HandleAwait(List <ILNode> newBody, out ILVariable awaiterVar, out FieldDefinition awaiterField, out int targetStateID) { // Handle the instructions prior to the exit out of the method to detect what is being awaited. // (analyses the last instructions in newBody and removes the analyzed instructions from newBody) if (doFinallyBodies != null) { // stloc(<>t__doFinallyBodies, ldc.i4(0)) ILExpression dfbInitExpr; if (!newBody.LastOrDefault().MatchStloc(doFinallyBodies, out dfbInitExpr)) { throw new SymbolicAnalysisFailedException(); } int val; if (!(dfbInitExpr.Match(ILCode.Ldc_I4, out val) && val == 0)) { throw new SymbolicAnalysisFailedException(); } newBody.RemoveAt(newBody.Count - 1); // remove doFinallyBodies assignment } // call(AsyncTaskMethodBuilder::AwaitUnsafeOnCompleted, ldflda(StateMachine::<>t__builder, ldloc(this)), ldloca(CS$0$0001), ldloc(this)) ILExpression callAwaitUnsafeOnCompleted = newBody.LastOrDefault() as ILExpression; newBody.RemoveAt(newBody.Count - 1); // remove AwaitUnsafeOnCompleted call if (callAwaitUnsafeOnCompleted == null || callAwaitUnsafeOnCompleted.Code != ILCode.Call) { throw new SymbolicAnalysisFailedException(); } string methodName = ((MethodReference)callAwaitUnsafeOnCompleted.Operand).Name; if (methodName != "AwaitUnsafeOnCompleted" && methodName != "AwaitOnCompleted") { throw new SymbolicAnalysisFailedException(); } if (callAwaitUnsafeOnCompleted.Arguments.Count != 3) { throw new SymbolicAnalysisFailedException(); } if (!callAwaitUnsafeOnCompleted.Arguments[1].Match(ILCode.Ldloca, out awaiterVar)) { throw new SymbolicAnalysisFailedException(); } // stfld(StateMachine::<>u__$awaiter6, ldloc(this), ldloc(CS$0$0001)) FieldReference awaiterFieldRef; ILExpression loadThis, loadAwaiterVar; if (!newBody.LastOrDefault().Match(ILCode.Stfld, out awaiterFieldRef, out loadThis, out loadAwaiterVar)) { throw new SymbolicAnalysisFailedException(); } newBody.RemoveAt(newBody.Count - 1); // remove awaiter field assignment awaiterField = awaiterFieldRef.ResolveWithinSameModule(); if (!(awaiterField != null && loadThis.MatchThis() && loadAwaiterVar.MatchLdloc(awaiterVar))) { throw new SymbolicAnalysisFailedException(); } // stfld(StateMachine::<>1__state, ldloc(this), ldc.i4(0)) if (MatchStateAssignment(newBody.LastOrDefault(), out targetStateID)) { newBody.RemoveAt(newBody.Count - 1); // remove awaiter field assignment } else if (MatchRoslynStateAssignment(newBody, newBody.Count - 3, out targetStateID)) { newBody.RemoveRange(newBody.Count - 3, 3); // remove awaiter field assignment } }
private List <ILNode> FindConditions(HashSet <ControlFlowNode> scope, ControlFlowNode entryNode) { List <ILNode> result = new List <ILNode>(); // Do not modify entry data scope = new HashSet <ControlFlowNode>(scope); Stack <ControlFlowNode> agenda = new Stack <ControlFlowNode>(); agenda.Push(entryNode); while (agenda.Count > 0) { ControlFlowNode node = agenda.Pop(); // Find a block that represents a simple condition if (scope.Contains(node)) { ILBasicBlock block = (ILBasicBlock)node.UserData; { // Switch ILLabel[] caseLabels; ILExpression switchArg; ILLabel fallLabel; if (block.MatchLastAndBr(ILCode.Switch, out caseLabels, out switchArg, out fallLabel)) { // Replace the switch code with ILSwitch ILSwitch ilSwitch = new ILSwitch() { Condition = switchArg }; block.Body.RemoveTail(ILCode.Switch, ILCode.Br); block.Body.Add(ilSwitch); block.Body.Add(new ILExpression(ILCode.Br, fallLabel)); result.Add(block); // Remove the item so that it is not picked up as content scope.RemoveOrThrow(node); // Find the switch offset int addValue = 0; List <ILExpression> subArgs; if (ilSwitch.Condition.Match(ILCode.Sub, out subArgs) && subArgs[1].Match(ILCode.Ldc_I4, out addValue)) { ilSwitch.Condition = subArgs[0]; } // Pull in code of cases ControlFlowNode fallTarget = null; labelToCfNode.TryGetValue(fallLabel, out fallTarget); HashSet <ControlFlowNode> frontiers = new HashSet <ControlFlowNode>(); if (fallTarget != null) { frontiers.UnionWith(fallTarget.DominanceFrontier.Except(new[] { fallTarget })); } foreach (ILLabel condLabel in caseLabels) { ControlFlowNode condTarget = null; labelToCfNode.TryGetValue(condLabel, out condTarget); if (condTarget != null) { frontiers.UnionWith(condTarget.DominanceFrontier.Except(new[] { condTarget })); } } for (int i = 0; i < caseLabels.Length; i++) { ILLabel condLabel = caseLabels[i]; // Find or create new case block ILSwitch.CaseBlock caseBlock = ilSwitch.CaseBlocks.FirstOrDefault(b => b.EntryGoto.Operand == condLabel); if (caseBlock == null) { caseBlock = new ILSwitch.CaseBlock() { Values = new List <int>(), EntryGoto = new ILExpression(ILCode.Br, condLabel) }; ilSwitch.CaseBlocks.Add(caseBlock); ControlFlowNode condTarget = null; labelToCfNode.TryGetValue(condLabel, out condTarget); if (condTarget != null && !frontiers.Contains(condTarget)) { HashSet <ControlFlowNode> content = FindDominatedNodes(scope, condTarget); scope.ExceptWith(content); caseBlock.Body.AddRange(FindConditions(content, condTarget)); // Add explicit break which should not be used by default, but the goto removal might decide to use it caseBlock.Body.Add(new ILBasicBlock() { Body = { new ILLabel() { Name = "SwitchBreak_" + (nextLabelIndex++) }, new ILExpression(ILCode.LoopOrSwitchBreak, null) } }); } } caseBlock.Values.Add(i + addValue); } // Heuristis to determine if we want to use fallthough as default case if (fallTarget != null && !frontiers.Contains(fallTarget)) { HashSet <ControlFlowNode> content = FindDominatedNodes(scope, fallTarget); if (content.Any()) { var caseBlock = new ILSwitch.CaseBlock() { EntryGoto = new ILExpression(ILCode.Br, fallLabel) }; ilSwitch.CaseBlocks.Add(caseBlock); block.Body.RemoveTail(ILCode.Br); scope.ExceptWith(content); caseBlock.Body.AddRange(FindConditions(content, fallTarget)); // Add explicit break which should not be used by default, but the goto removal might decide to use it caseBlock.Body.Add(new ILBasicBlock() { Body = { new ILLabel() { Name = "SwitchBreak_" + (nextLabelIndex++) }, new ILExpression(ILCode.LoopOrSwitchBreak, null) } }); } } } // Two-way branch ILExpression condExpr; ILLabel trueLabel; ILLabel falseLabel; if (block.MatchLastAndBr(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) { // Swap bodies since that seems to be the usual C# order ILLabel temp = trueLabel; trueLabel = falseLabel; falseLabel = temp; condExpr = new ILExpression(ILCode.LogicNot, null, condExpr); // Convert the brtrue to ILCondition ILCondition ilCond = new ILCondition() { Condition = condExpr, TrueBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, trueLabel) }, FalseBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, falseLabel) } }; block.Body.RemoveTail(ILCode.Brtrue, ILCode.Br); block.Body.Add(ilCond); result.Add(block); // Remove the item immediately so that it is not picked up as content scope.RemoveOrThrow(node); ControlFlowNode trueTarget = null; labelToCfNode.TryGetValue(trueLabel, out trueTarget); ControlFlowNode falseTarget = null; labelToCfNode.TryGetValue(falseLabel, out falseTarget); // Pull in the conditional code if (trueTarget != null && HasSingleEdgeEnteringBlock(trueTarget)) { HashSet <ControlFlowNode> content = FindDominatedNodes(scope, trueTarget); scope.ExceptWith(content); ilCond.TrueBlock.Body.AddRange(FindConditions(content, trueTarget)); } if (falseTarget != null && HasSingleEdgeEnteringBlock(falseTarget)) { HashSet <ControlFlowNode> content = FindDominatedNodes(scope, falseTarget); scope.ExceptWith(content); ilCond.FalseBlock.Body.AddRange(FindConditions(content, falseTarget)); } } } // Add the node now so that we have good ordering if (scope.Contains(node)) { result.Add((ILNode)node.UserData); scope.Remove(node); } } // depth-first traversal of dominator tree for (int i = node.DominatorTreeChildren.Count - 1; i >= 0; i--) { agenda.Push(node.DominatorTreeChildren[i]); } } // Add whatever is left foreach (var node in scope) { result.Add((ILNode)node.UserData); } return(result); }
List <ILNode> ConvertToAst(List <ByteCode> body) { List <ILNode> ast = new List <ILNode>(); // Convert stack-based IL code to ILAst tree foreach (ByteCode byteCode in body) { ILRange ilRange = new ILRange() { From = byteCode.Offset, To = byteCode.EndOffset }; if (byteCode.StackBefore == null) { // Unreachable code continue; } ILExpression expr = new ILExpression(byteCode.Code, byteCode.Operand); expr.ILRanges.Add(ilRange); expr.Prefixes = byteCode.Prefixes; // Label for this instruction if (byteCode.Label != null) { ast.Add(byteCode.Label); } // Reference arguments using temporary variables int popCount = byteCode.PopCount ?? byteCode.StackBefore.Count; for (int i = byteCode.StackBefore.Count - popCount; i < byteCode.StackBefore.Count; i++) { StackSlot slot = byteCode.StackBefore[i]; expr.Arguments.Add(new ILExpression(ILCode.Ldloc, slot.LoadFrom)); } // Store the result to temporary variable(s) if needed if (byteCode.StoreTo == null || byteCode.StoreTo.Count == 0) { ast.Add(expr); } else if (byteCode.StoreTo.Count == 1) { ast.Add(new ILExpression(ILCode.Stloc, byteCode.StoreTo[0], expr)); } else { ILVariable tmpVar = new ILVariable() { Name = "expr_" + byteCode.Offset.ToString("X2"), IsGenerated = true }; ast.Add(new ILExpression(ILCode.Stloc, tmpVar, expr)); foreach (ILVariable storeTo in byteCode.StoreTo.AsEnumerable().Reverse()) { ast.Add(new ILExpression(ILCode.Stloc, storeTo, new ILExpression(ILCode.Ldloc, tmpVar))); } } } return(ast); }
public override bool Match(PatternMatcher pm, ILExpression e) { return(e.Code == ILCode.Ldc_I4 && e.InferredType.GetElementType() == ElementType.Boolean && object.Equals(e.Operand, value)); }
/// <summary> /// Finds the position to inline to. /// </summary> /// <returns>true = found; false = cannot continue search; null = not found</returns> private bool?FindLoadInNext(ILExpression expr, ILVariable v, ILExpression expressionBeingMoved, out ILExpression parent, out int pos) { parent = null; pos = 0; if (expr == null) { return(false); } for (int i = 0; i < expr.Arguments.Count; i++) { // Stop when seeing an opcode that does not guarantee that its operands will be evaluated. // Inlining in that case might result in the inlined expresion not being evaluted. if (i == 1 && (expr.Code == ILCode.LogicAnd || expr.Code == ILCode.LogicOr || expr.Code == ILCode.TernaryOp || expr.Code == ILCode.NullCoalescing)) { return(false); } ILExpression arg = expr.Arguments[i]; if ((arg.Code == ILCode.Ldloc || arg.Code == ILCode.Ldloca) && arg.Operand == v) { parent = expr; pos = i; return(true); } bool?r = FindLoadInNext(arg, v, expressionBeingMoved, out parent, out pos); if (r != null) { return(r); } } if (IsSafeForInlineOver(expr, expressionBeingMoved)) { return(null); // continue searching } else { return(false); // abort, inlining not possible } }
public static bool Match <T>(this ILNode node, ILCode code, out T operand, out ILExpression arg1, out ILExpression arg2) { List <ILExpression> args; if (node.Match(code, out operand, out args) && args.Count == 2) { arg1 = args[0]; arg2 = args[1]; return(true); } arg1 = null; arg2 = null; return(false); }
public static bool MatchSingle <T>(this ILBasicBlock bb, ILCode code, out T operand, out ILExpression arg) { if (bb.Body.Count == 2 && bb.Body[0] is ILLabel && bb.Body[1].Match(code, out operand, out arg)) { return(true); } operand = default(T); arg = null; return(false); }
/// <summary> /// Parses an object initializer. /// </summary> /// <param name="body">ILAst block</param> /// <param name="pos"> /// Input: position of the instruction assigning to 'v'. /// Output: first position after the object initializer /// </param> /// <param name="v">The variable that holds the object being initialized</param> /// <param name="newObjExpr">The newobj instruction</param> /// <returns>InitObject instruction</returns> ILExpression ParseObjectInitializer(List <ILNode> body, ref int pos, ILVariable v, ILExpression newObjExpr, bool isCollection, bool isValueType) { // Take care not to modify any existing ILExpressions in here. // We just construct new ones around the old ones, any modifications must wait until the whole // object/collection initializer was analyzed. ILExpression objectInitializer = new ILExpression(isCollection ? ILCode.InitCollection : ILCode.InitObject, null, newObjExpr); List <ILExpression> initializerStack = new List <ILExpression>(); initializerStack.Add(objectInitializer); while (++pos < body.Count) { ILExpression nextExpr = body[pos] as ILExpression; if (IsSetterInObjectInitializer(nextExpr)) { if (!AdjustInitializerStack(initializerStack, nextExpr.Arguments[0], v, false, isValueType)) { CleanupInitializerStackAfterFailedAdjustment(initializerStack); break; } initializerStack[initializerStack.Count - 1].Arguments.Add(nextExpr); } else if (IsAddMethodCall(nextExpr)) { if (!AdjustInitializerStack(initializerStack, nextExpr.Arguments[0], v, true, isValueType)) { CleanupInitializerStackAfterFailedAdjustment(initializerStack); break; } initializerStack[initializerStack.Count - 1].Arguments.Add(nextExpr); } else { // can't match any more initializers: end of object initializer break; } } return(objectInitializer); }
static bool AdjustInitializerStack(List <ILExpression> initializerStack, ILExpression argument, ILVariable v, bool isCollection, bool isValueType) { // Argument is of the form 'getter(getter(...(v)))' // Unpack it into a list of getters: List <ILExpression> getters = new List <ILExpression>(); 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]; MemberReference mr = (MemberReference)g.Operand; TypeReference returnType; if (mr is FieldReference) { returnType = TypeAnalysis.GetFieldType((FieldReference)mr); } else { returnType = TypeAnalysis.SubstituteTypeArgs(((MethodReference)mr).ReturnType, 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); } } }
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; TypeReference newObjType; bool isValueType; MethodReference 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); ctorArgs.RemoveAt(0); newObjExpr = new ILExpression(ILCode.Newobj, ctor, ctorArgs); } else { return(false); } } else { return(false); } if (newObjType.IsValueType != 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), 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 } ILInlining inlining = new ILInlining(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] = initializer; } else { Debug.Assert(expr.Code == ILCode.Call); expr.Code = ILCode.Stloc; expr.Operand = v; expr.Arguments.Clear(); expr.Arguments.Add(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); }
static bool DecodeArrayInitializer(TypeCode elementType, byte[] initialValue, ILExpression[] output) { switch (elementType) { case TypeCode.Boolean: case TypeCode.Byte: if (initialValue.Length == output.Length) { for (int j = 0; j < output.Length; j++) { output[j] = new ILExpression(ILCode.Ldc_I4, (int)initialValue[j]); } return(true); } return(false); case TypeCode.SByte: if (initialValue.Length == output.Length) { for (int j = 0; j < output.Length; j++) { output[j] = new ILExpression(ILCode.Ldc_I4, (int)unchecked ((sbyte)initialValue[j])); } return(true); } return(false); case TypeCode.Int16: if (initialValue.Length == output.Length * 2) { for (int j = 0; j < output.Length; j++) { output[j] = new ILExpression(ILCode.Ldc_I4, (int)BitConverter.ToInt16(initialValue, j * 2)); } return(true); } return(false); case TypeCode.Char: case TypeCode.UInt16: if (initialValue.Length == output.Length * 2) { for (int j = 0; j < output.Length; j++) { output[j] = new ILExpression(ILCode.Ldc_I4, (int)BitConverter.ToUInt16(initialValue, j * 2)); } return(true); } return(false); case TypeCode.Int32: case TypeCode.UInt32: if (initialValue.Length == output.Length * 4) { for (int j = 0; j < output.Length; j++) { output[j] = new ILExpression(ILCode.Ldc_I4, BitConverter.ToInt32(initialValue, j * 4)); } return(true); } return(false); case TypeCode.Int64: case TypeCode.UInt64: if (initialValue.Length == output.Length * 8) { for (int j = 0; j < output.Length; j++) { output[j] = new ILExpression(ILCode.Ldc_I8, BitConverter.ToInt64(initialValue, j * 8)); } return(true); } return(false); case TypeCode.Single: if (initialValue.Length == output.Length * 4) { for (int j = 0; j < output.Length; j++) { output[j] = new ILExpression(ILCode.Ldc_R4, BitConverter.ToSingle(initialValue, j * 4)); } return(true); } return(false); case TypeCode.Double: if (initialValue.Length == output.Length * 8) { for (int j = 0; j < output.Length; j++) { output[j] = new ILExpression(ILCode.Ldc_R8, BitConverter.ToDouble(initialValue, j * 8)); } return(true); } return(false); default: return(false); } }
public override bool Match(PatternMatcher pm, ILExpression e) { return(e.Code == this.code && base.Match(pm, e)); }
public static bool MatchSingleAndBr <T>(this ILBasicBlock bb, ILCode code, out T operand, out ILExpression arg, out ILLabel brLabel) { if (bb.Body.Count == 3 && bb.Body[0] is ILLabel && bb.Body[1].Match(code, out operand, out arg) && bb.Body[2].Match(ILCode.Br, out brLabel)) { return(true); } operand = default(T); arg = null; brLabel = null; return(false); }
public override bool Match(PatternMatcher pm, ILExpression e) { switch (e.Code) { case ILCode.Ceq: if (type != OperatorType.Equality) { return(false); } break; case ILCode.Cne: if (type != OperatorType.InEquality) { return(false); } break; case ILCode.Cgt: case ILCode.Cgt_Un: case ILCode.Cge: case ILCode.Cge_Un: case ILCode.Clt: case ILCode.Clt_Un: case ILCode.Cle: case ILCode.Cle_Un: if (type != OperatorType.Comparison) { return(false); } break; 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: case ILCode.Not: case ILCode.Neg: case ILCode.LogicNot: if (type != OperatorType.Other) { return(false); } break; case ILCode.Call: var m = e.Operand as IMethod; if (m == null || m.MethodSig == null || m.MethodSig.HasThis || m.MethodSig.Params.Count == 0 || e.Arguments.Count > 2 || !IsCustomOperator(m.Name)) { return(false); } break; default: return(false); } if (pm.Operator != null) { throw new InvalidOperationException(); } pm.Operator = e; var a0 = e.Arguments[0]; if (!simple) { return(VariableAGetValueOrDefault.Match(pm, a0) && VariableBGetValueOrDefault.Match(pm, e.Arguments[1])); } if (e.Arguments.Count == 1) { return(VariableAGetValueOrDefault.Match(pm, a0)); } if (VariableAGetValueOrDefault.Match(pm, a0)) { pm.SimpleOperand = e.Arguments[1]; pm.SimpleLeftOperand = false; return(true); } if (VariableAGetValueOrDefault.Match(pm, e.Arguments[1])) { pm.SimpleOperand = a0; pm.SimpleLeftOperand = true; return(true); } return(false); }
public static bool MatchLastAndBr <T>(this ILBasicBlock bb, ILCode code, out T operand, out ILExpression arg, out ILLabel brLabel) { if (bb.Body.ElementAtOrDefault(bb.Body.Count - 2).Match(code, out operand, out arg) && bb.Body.LastOrDefault().Match(ILCode.Br, out brLabel)) { return(true); } operand = default(T); arg = null; brLabel = null; return(false); }
/// <summary> /// Is this a temporary variable generated by the C# compiler for instance method calls on value type values /// </summary> /// <param name="next">The next top-level expression</param> /// <param name="parent">The direct parent of the load within 'next'</param> /// <param name="pos">Index of the load within 'parent'</param> /// <param name="v">The variable being inlined.</param> /// <param name="inlinedExpression">The expression being inlined</param> private bool IsGeneratedValueTypeTemporary(ILExpression next, ILExpression parent, int pos, ILVariable v, ILExpression inlinedExpression) { if (pos == 0 && v.Type != null && v.Type.IsValueType) { // Inlining a value type variable is allowed only if the resulting code will maintain the semantics // that the method is operating on a copy. // Thus, we have to disallow inlining of other locals, fields, array elements, dereferenced pointers switch (inlinedExpression.Code) { case ILCode.Ldloc: case ILCode.Stloc: case ILCode.CompoundAssignment: case ILCode.Ldelem_Any: case ILCode.Ldelem_I: case ILCode.Ldelem_I1: case ILCode.Ldelem_I2: case ILCode.Ldelem_I4: case ILCode.Ldelem_I8: case ILCode.Ldelem_R4: case ILCode.Ldelem_R8: case ILCode.Ldelem_Ref: case ILCode.Ldelem_U1: case ILCode.Ldelem_U2: case ILCode.Ldelem_U4: case ILCode.Ldobj: case ILCode.Ldind_Ref: return(false); case ILCode.Ldfld: case ILCode.Stfld: case ILCode.Ldsfld: case ILCode.Stsfld: // allow inlining field access only if it's a readonly field FieldDefinition f = ((FieldReference)inlinedExpression.Operand).Resolve(); if (!(f != null && f.IsInitOnly)) { return(false); } break; case ILCode.Call: case ILCode.CallGetter: // inlining runs both before and after IntroducePropertyAccessInstructions, // so we have to handle both 'call' and 'callgetter' MethodReference mr = (MethodReference)inlinedExpression.Operand; // ensure that it's not an multi-dimensional array getter if (mr.DeclaringType is ArrayType) { return(false); } goto case ILCode.Callvirt; case ILCode.Callvirt: case ILCode.CallvirtGetter: // don't inline foreach loop variables: mr = (MethodReference)inlinedExpression.Operand; if (mr.Name == "get_Current" && mr.HasThis) { return(false); } break; case ILCode.Castclass: case ILCode.Unbox_Any: // These are valid, but might occur as part of a foreach loop variable. ILExpression arg = inlinedExpression.Arguments[0]; if (arg.Code == ILCode.CallGetter || arg.Code == ILCode.CallvirtGetter || arg.Code == ILCode.Call || arg.Code == ILCode.Callvirt) { mr = (MethodReference)arg.Operand; if (mr.Name == "get_Current" && mr.HasThis) { return(false); // looks like a foreach loop variable, so don't inline it } } break; } // inline the compiler-generated variable that are used when accessing a member on a value type: switch (parent.Code) { case ILCode.Call: case ILCode.CallGetter: case ILCode.CallSetter: case ILCode.Callvirt: case ILCode.CallvirtGetter: case ILCode.CallvirtSetter: MethodReference mr = (MethodReference)parent.Operand; return(mr.HasThis); case ILCode.Stfld: case ILCode.Ldfld: case ILCode.Ldflda: case ILCode.Await: return(true); } } return(false); }
public static bool MatchStloc(this ILNode node, ILVariable expectedVar, out ILExpression expr) { ILVariable v; return(node.Match(ILCode.Stloc, out v, out expr) && v == expectedVar); }
private List <ILNode> FindLoops(HashSet <ControlFlowNode> scope, ControlFlowNode entryPoint, bool excludeEntryPoint) { List <ILNode> result = new List <ILNode>(); // Do not modify entry data scope = new HashSet <ControlFlowNode>(scope); Queue <ControlFlowNode> agenda = new Queue <ControlFlowNode>(); agenda.Enqueue(entryPoint); while (agenda.Count > 0) { ControlFlowNode node = agenda.Dequeue(); // If the node is a loop header if (scope.Contains(node) && node.DominanceFrontier.Contains(node) && (node != entryPoint || !excludeEntryPoint)) { HashSet <ControlFlowNode> loopContents = FindLoopContent(scope, node); // If the first expression is a loop condition ILBasicBlock basicBlock = (ILBasicBlock)node.UserData; ILExpression condExpr; ILLabel trueLabel; ILLabel falseLabel; // It has to be just brtrue - any preceding code would introduce goto if (basicBlock.MatchSingleAndBr(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) { ControlFlowNode trueTarget; labelToCfNode.TryGetValue(trueLabel, out trueTarget); ControlFlowNode falseTarget; labelToCfNode.TryGetValue(falseLabel, out falseTarget); // If one point inside the loop and the other outside if ((!loopContents.Contains(trueTarget) && loopContents.Contains(falseTarget)) || (loopContents.Contains(trueTarget) && !loopContents.Contains(falseTarget))) { loopContents.RemoveOrThrow(node); scope.RemoveOrThrow(node); // If false means enter the loop if (loopContents.Contains(falseTarget) || falseTarget == node) { // Negate the condition condExpr = new ILExpression(ILCode.LogicNot, null, condExpr); ILLabel tmp = trueLabel; trueLabel = falseLabel; falseLabel = tmp; } ControlFlowNode postLoopTarget; labelToCfNode.TryGetValue(falseLabel, out postLoopTarget); if (postLoopTarget != null) { // Pull more nodes into the loop HashSet <ControlFlowNode> postLoopContents = FindDominatedNodes(scope, postLoopTarget); var pullIn = scope.Except(postLoopContents).Where(n => node.Dominates(n)); loopContents.UnionWith(pullIn); } // Use loop to implement the brtrue basicBlock.Body.RemoveTail(ILCode.Brtrue, ILCode.Br); basicBlock.Body.Add(new ILWhileLoop() { Condition = condExpr, BodyBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, trueLabel), Body = FindLoops(loopContents, node, false) } }); basicBlock.Body.Add(new ILExpression(ILCode.Br, falseLabel)); result.Add(basicBlock); scope.ExceptWith(loopContents); } } // Fallback method: while(true) if (scope.Contains(node)) { result.Add(new ILBasicBlock() { Body = new List <ILNode>() { new ILLabel() { Name = "Loop_" + (nextLabelIndex++) }, new ILWhileLoop() { BodyBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, (ILLabel)basicBlock.Body.First()), Body = FindLoops(loopContents, node, true) } }, }, }); scope.ExceptWith(loopContents); } } // Using the dominator tree should ensure we find the the widest loop first foreach (var child in node.DominatorTreeChildren) { agenda.Enqueue(child); } } // Add whatever is left foreach (var node in scope) { result.Add((ILNode)node.UserData); } scope.Clear(); return(result); }
public static bool Match(this ILNode node, ILCode code) { ILExpression expr = node as ILExpression; return(expr != null && expr.Prefixes == null && expr.Code == code); }
/// <summary> /// Get the first expression to be excecuted if the instruction pointer is at the start of the given node. /// Try blocks may not be entered in any way. If possible, the try block is returned as the node to be executed. /// </summary> ILNode Enter(ILNode node, HashSet <ILNode> visitedNodes) { if (node == null) { throw new ArgumentNullException(); } if (!visitedNodes.Add(node)) { return(null); // Infinite loop } ILLabel label = node as ILLabel; if (label != null) { return(Exit(label, visitedNodes)); } ILExpression expr = node as ILExpression; if (expr != null) { if (expr.Code == ILCode.Br || expr.Code == ILCode.Leave) { ILLabel target = (ILLabel)expr.Operand; // Early exit - same try-block if (GetParents(expr).OfType <ILTryCatchBlock>().FirstOrDefault() == GetParents(target).OfType <ILTryCatchBlock>().FirstOrDefault()) { return(Enter(target, visitedNodes)); } // Make sure we are not entering any try-block var srcTryBlocks = GetParents(expr).OfType <ILTryCatchBlock>().Reverse().ToList(); var dstTryBlocks = GetParents(target).OfType <ILTryCatchBlock>().Reverse().ToList(); // Skip blocks that we are already in int i = 0; while (i < srcTryBlocks.Count && i < dstTryBlocks.Count && srcTryBlocks[i] == dstTryBlocks[i]) { i++; } if (i == dstTryBlocks.Count) { return(Enter(target, visitedNodes)); } else { ILTryCatchBlock dstTryBlock = dstTryBlocks[i]; // Check that the goto points to the start ILTryCatchBlock current = dstTryBlock; while (current != null) { foreach (ILNode n in current.TryBlock.Body) { if (n is ILLabel) { if (n == target) { return(dstTryBlock); } } else if (!n.Match(ILCode.Nop)) { current = n as ILTryCatchBlock; break; } } } return(null); } } else if (expr.Code == ILCode.Nop) { return(Exit(expr, visitedNodes)); } else if (expr.Code == ILCode.LoopOrSwitchBreak) { ILNode breakBlock = GetParents(expr).First(n => n is ILWhileLoop || n is ILSwitch); return(Exit(breakBlock, new HashSet <ILNode>() { expr })); } else if (expr.Code == ILCode.LoopContinue) { ILNode continueBlock = GetParents(expr).First(n => n is ILWhileLoop); return(Enter(continueBlock, new HashSet <ILNode>() { expr })); } else { return(expr); } } ILBlock block = node as ILBlock; if (block != null) { if (block.EntryGoto != null) { return(Enter(block.EntryGoto, visitedNodes)); } else if (block.Body.Count > 0) { return(Enter(block.Body[0], visitedNodes)); } else { return(Exit(block, visitedNodes)); } } ILCondition cond = node as ILCondition; if (cond != null) { return(cond.Condition); } ILWhileLoop loop = node as ILWhileLoop; if (loop != null) { if (loop.Condition != null) { return(loop.Condition); } else { return(Enter(loop.BodyBlock, visitedNodes)); } } ILTryCatchBlock tryCatch = node as ILTryCatchBlock; if (tryCatch != null) { return(tryCatch); } ILSwitch ilSwitch = node as ILSwitch; if (ilSwitch != null) { return(ilSwitch.Condition); } throw new NotSupportedException(node.GetType().ToString()); }
List <ILNode> ConvertToAst(List <ByteCode> body, HashSet <ExceptionHandler> ehs) { List <ILNode> ast = new List <ILNode>(); while (ehs.Any()) { ILTryCatchBlock tryCatchBlock = new ILTryCatchBlock(); // Find the first and widest scope int tryStart = ehs.Min(eh => eh.TryStart.Offset); int tryEnd = ehs.Where(eh => eh.TryStart.Offset == tryStart).Max(eh => eh.TryEnd.Offset); var handlers = ehs.Where(eh => eh.TryStart.Offset == tryStart && eh.TryEnd.Offset == tryEnd).ToList(); // Cut all instructions up to the try block { int tryStartIdx; for (tryStartIdx = 0; body[tryStartIdx].Offset != tryStart; tryStartIdx++) { ; } ast.AddRange(ConvertToAst(body.CutRange(0, tryStartIdx))); } // Cut the try block { HashSet <ExceptionHandler> nestedEHs = new HashSet <ExceptionHandler>(ehs.Where(eh => (tryStart <= eh.TryStart.Offset && eh.TryEnd.Offset < tryEnd) || (tryStart < eh.TryStart.Offset && eh.TryEnd.Offset <= tryEnd))); ehs.ExceptWith(nestedEHs); int tryEndIdx; for (tryEndIdx = 0; tryEndIdx < body.Count && body[tryEndIdx].Offset != tryEnd; tryEndIdx++) { ; } tryCatchBlock.TryBlock = new ILBlock(ConvertToAst(body.CutRange(0, tryEndIdx), nestedEHs)); } // Cut all handlers tryCatchBlock.CatchBlocks = new List <ILTryCatchBlock.CatchBlock>(); foreach (ExceptionHandler eh in handlers) { int startIndex; for (startIndex = 0; body[startIndex].Offset != eh.HandlerStart.Offset; startIndex++) { ; } int endInclusiveIndex; if (eh.HandlerEnd == null) { endInclusiveIndex = body.Count - 1; } // Note that the end(exclusive) instruction may not necessarly be in our body else { for (endInclusiveIndex = 0; body[endInclusiveIndex].Next.Offset != eh.HandlerEnd.Offset; endInclusiveIndex++) { ; } } int count = 1 + endInclusiveIndex - startIndex; HashSet <ExceptionHandler> nestedEHs = new HashSet <ExceptionHandler>(ehs.Where(e => (eh.HandlerStart.Offset <= e.TryStart.Offset && e.TryEnd.Offset < eh.HandlerEnd.Offset) || (eh.HandlerStart.Offset < e.TryStart.Offset && e.TryEnd.Offset <= eh.HandlerEnd.Offset))); ehs.ExceptWith(nestedEHs); List <ILNode> handlerAst = ConvertToAst(body.CutRange(startIndex, count), nestedEHs); if (eh.HandlerType == ExceptionHandlerType.Catch) { ILTryCatchBlock.CatchBlock catchBlock = new ILTryCatchBlock.CatchBlock() { ExceptionType = eh.CatchType, Body = handlerAst }; // Handle the automatically pushed exception on the stack ByteCode ldexception = ldexceptions[eh]; if (ldexception.StoreTo.Count == 0) { throw new Exception("Exception should be consumed by something"); } else if (ldexception.StoreTo.Count == 1) { ILExpression first = catchBlock.Body[0] as ILExpression; if (first != null && first.Code == ILCode.Pop && first.Arguments[0].Code == ILCode.Ldloc && first.Arguments[0].Operand == ldexception.StoreTo[0]) { // The exception is just poped - optimize it all away; catchBlock.ExceptionVariable = null; catchBlock.Body.RemoveAt(0); } else { catchBlock.ExceptionVariable = ldexception.StoreTo[0]; } } else { ILVariable exTemp = new ILVariable() { Name = "ex_" + eh.HandlerStart.Offset.ToString("X2"), IsGenerated = true }; catchBlock.ExceptionVariable = exTemp; foreach (ILVariable storeTo in ldexception.StoreTo) { catchBlock.Body.Insert(0, new ILExpression(ILCode.Stloc, storeTo, new ILExpression(ILCode.Ldloc, exTemp))); } } tryCatchBlock.CatchBlocks.Add(catchBlock); } else if (eh.HandlerType == ExceptionHandlerType.Finally) { tryCatchBlock.FinallyBlock = new ILBlock(handlerAst); } else if (eh.HandlerType == ExceptionHandlerType.Fault) { tryCatchBlock.FaultBlock = new ILBlock(handlerAst); } else { // TODO: ExceptionHandlerType.Filter } } ehs.ExceptWith(handlers); ast.Add(tryCatchBlock); } // Add whatever is left ast.AddRange(ConvertToAst(body)); return(ast); }