public bool SimplifyCustomShortCircuit(List <ILNode> body, ILBasicBlock head, int pos) { Debug.Assert(body.Contains(head)); // --- looking for the following pattern --- // stloc(targetVar, leftVar) // brtrue(exitLabel, call(op_False, leftVar) // br(followingBlock) // // FollowingBlock: // stloc(targetVar, call(op_BitwiseAnd, leftVar, rightExpression)) // br(exitLabel) // --- if (head.Body.Count < 3) { return(false); } // looking for: // stloc(targetVar, leftVar) ILVariable targetVar; ILExpression targetVarInitExpr; if (!head.Body[head.Body.Count - 3].Match(ILCode.Stloc, out targetVar, out targetVarInitExpr)) { return(false); } ILVariable leftVar; if (!targetVarInitExpr.Match(ILCode.Ldloc, out leftVar)) { return(false); } // looking for: // brtrue(exitLabel, call(op_False, leftVar) // br(followingBlock) ILExpression callExpr; ILLabel exitLabel; ILLabel followingBlock; if (!head.MatchLastAndBr(ILCode.Brtrue, out exitLabel, out callExpr, out followingBlock)) { return(false); } if (labelGlobalRefCount[followingBlock] > 1) { return(false); } IMethod opFalse; ILExpression opFalseArg; if (!callExpr.Match(ILCode.Call, out opFalse, out opFalseArg)) { return(false); } // ignore operators other than op_False and op_True if (opFalse.Name != "op_False" && opFalse.Name != "op_True") { return(false); } if (!opFalseArg.MatchLdloc(leftVar)) { return(false); } ILBasicBlock followingBasicBlock = labelToBasicBlock[followingBlock]; // FollowingBlock: // stloc(targetVar, call(op_BitwiseAnd, leftVar, rightExpression)) // br(exitLabel) ILVariable _targetVar; ILExpression opBitwiseCallExpr; ILLabel _exitLabel; if (!followingBasicBlock.MatchSingleAndBr(ILCode.Stloc, out _targetVar, out opBitwiseCallExpr, out _exitLabel)) { return(false); } if (_targetVar != targetVar || exitLabel != _exitLabel) { return(false); } IMethod opBitwise; ILExpression leftVarExpression; ILExpression rightExpression; if (!opBitwiseCallExpr.Match(ILCode.Call, out opBitwise, out leftVarExpression, out rightExpression)) { return(false); } if (!leftVarExpression.MatchLdloc(leftVar)) { return(false); } // ignore operators other than op_BitwiseAnd and op_BitwiseOr if (opBitwise.Name != "op_BitwiseAnd" && opBitwise.Name != "op_BitwiseOr") { return(false); } // insert: // stloc(targetVar, LogicAnd(C::op_BitwiseAnd, leftVar, rightExpression) // br(exitLabel) ILCode op = opBitwise.Name == "op_BitwiseAnd" ? ILCode.LogicAnd : ILCode.LogicOr; if (op == ILCode.LogicAnd && opFalse.Name != "op_False") { return(false); } if (op == ILCode.LogicOr && opFalse.Name != "op_True") { return(false); } ILExpression shortCircuitExpr = MakeLeftAssociativeShortCircuit(op, opFalseArg, rightExpression); shortCircuitExpr.Operand = opBitwise; Debug.Fail("Preserve ILRanges"); //TODO: Could never trigger this code path. Tested many asms. var tail = head.Body.RemoveTail(ILCode.Stloc, ILCode.Brtrue, ILCode.Br); //TODO: Keep tail's ILRanges //TODO: Keep ILRanges of other things that are removed by this method head.Body.Add(new ILExpression(ILCode.Stloc, targetVar, shortCircuitExpr)); head.Body.Add(new ILExpression(ILCode.Br, exitLabel)); body.Remove(followingBasicBlock); return(true); }
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); }