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); }
/// <summary> /// Reduce the nesting of conditions. /// It should be done on flat data that already had most gotos removed /// </summary> void ReduceIfNesting(ILNode node) { ILBlock block = node as ILBlock; if (block != null) { for (int i = 0; i < block.Body.Count; i++) { ILCondition cond = block.Body[i] as ILCondition; if (cond != null) { bool trueExits = cond.TrueBlock.Body.LastOrDefault().IsUnconditionalControlFlow(); bool falseExits = cond.FalseBlock.Body.LastOrDefault().IsUnconditionalControlFlow(); if (trueExits) { // Move the false block after the condition block.Body.InsertRange(i + 1, cond.FalseBlock.GetChildren()); cond.FalseBlock = new ILBlock(); } else if (falseExits) { // Move the true block after the condition block.Body.InsertRange(i + 1, cond.TrueBlock.GetChildren()); cond.TrueBlock = new ILBlock(); } // Eliminate empty true block if (!cond.TrueBlock.GetChildren().Any() && cond.FalseBlock.GetChildren().Any()) { // Swap bodies ILBlock tmp = cond.TrueBlock; cond.TrueBlock = cond.FalseBlock; cond.FalseBlock = tmp; cond.Condition = new ILExpression(ILCode.LogicNot, null, cond.Condition); } } } } // We are changing the number of blocks so we use plain old recursion to get all blocks foreach (ILNode child in node.GetChildren()) { if (child != null && !(child is ILExpression)) { ReduceIfNesting(child); } } }
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])) { initValue = new ILExpression(ILCode.Stloc, pinnedVar, arrayLoadingExpr); nextPos = i + 1; return(true); } } } } } initValue = null; nextPos = -1; return(false); }
/// <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> 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); }
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) && assignedExpr.Arguments[1].Match(ILCode.Call, 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); }
void CachedDelegateInitialization(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: true); 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); } }