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 = null; ILLabel trueLabel; ILLabel falseLabel; // It has to be just brtrue - any preceding code would introduce goto error.Assert(!basicBlock.MatchLastAndBr(GMCode.Bt, out trueLabel, out condExpr, out falseLabel), "Unrolled loop"); if (basicBlock.MatchLastAndBr(GMCode.Bt, out trueLabel, out condExpr, out falseLabel) || basicBlock.MatchLastAndBr(GMCode.Pushenv, out falseLabel, out condExpr, out trueLabel) || // built it the same way from the dissasembler, this needs inverted basicBlock.MatchLastAndBr(GMCode.Repeat, out trueLabel, out condExpr, out falseLabel)) // repeate loop is built like a while { GMCode loopType = (basicBlock.Body.ElementAt(basicBlock.Body.Count - 2) as ILExpression).Code; 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); bool mustNegate = false; if (loopContents.Contains(falseTarget) || falseTarget == node) { // Negate the condition mustNegate = true; // condExpr = new ILExpression(GMCode.Not, null, condExpr); ILLabel tmp = trueLabel; trueLabel = falseLabel; falseLabel = tmp; } // HACK 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); } //Debug.Assert(false); Debug.Assert(condExpr != null); switch (loopType) { case GMCode.Pushenv: basicBlock.Body.RemoveTail(GMCode.Pushenv, GMCode.B); basicBlock.Body.Add(new ILWithStatement() { Condition = condExpr, // we never negate Body = new ILBlock() { EntryGoto = new ILExpression(GMCode.B, trueLabel), Body = FindLoops(loopContents, node, false) } }); break; case GMCode.Bt: basicBlock.Body.RemoveTail(GMCode.Bt, GMCode.B); basicBlock.Body.Add(new ILWhileLoop() { Condition = mustNegate ? condExpr.NegateCondition() : condExpr, Body = new ILBlock() { EntryGoto = new ILExpression(GMCode.B, trueLabel), Body = FindLoops(loopContents, node, false) } }); break; case GMCode.Repeat: basicBlock.Body.RemoveTail(GMCode.Repeat, GMCode.B); basicBlock.Body.Add(new ILRepeat() { Condition = condExpr, // we never negate Body = new ILBlock() { EntryGoto = new ILExpression(GMCode.B, trueLabel), Body = FindLoops(loopContents, node, false) } }); break; } basicBlock.Body.Add(new ILExpression(GMCode.B, falseLabel)); result.Add(basicBlock); scope.ExceptWith(loopContents); } } // Fallback method: while(true) if (scope.Contains(node)) { Debug.Assert(false); result.Add(new ILBasicBlock() { Body = new List <ILNode>() { ILLabel.Generate("Loop"), new ILWhileLoop() { Condition = new ILExpression(GMCode.Constant, new ILValue(true)), Body = new ILBlock() { EntryGoto = new ILExpression(GMCode.B, (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); }