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 IList <ILExpression> cases; ILLabel[] caseLabels; ILExpression switchCondition; ILLabel fallLabel; // IList<ILExpression> cases; out IList<ILExpression> arg, out ILLabel fallLabel) // matches a switch statment, not sure how the hell I am going to do this if (block.MatchLastAndBr(GMCode.Switch, out caseLabels, out cases, out fallLabel)) { // Debug.Assert(fallLabel == endBlock); // this should be true switchCondition = cases[0]; // thats the switch arg cases.RemoveAt(0); // remove the switch condition // Replace the switch code with ILSwitch ILSwitch ilSwitch = new ILSwitch() { Condition = switchCondition }; block.Body.RemoveTail(GMCode.Switch, GMCode.B); block.Body.Add(ilSwitch); block.Body.Add(new ILExpression(GMCode.B, fallLabel)); result.Add(block); // Remove the item so that it is not picked up as content scope.RemoveOrThrow(node); // 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 <ILExpression>(), EntryGoto = new ILExpression(GMCode.B, 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); foreach (var con in FindConditions(content, condTarget)) { caseBlock.Body.Add(con); } // 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(GMCode.LoopOrSwitchBreak, null) } }); } } if (cases[i].Code != GMCode.DefaultCase) { caseBlock.Values.Add(cases[i].Arguments[0]); } else { caseBlock.Values.Add(new ILExpression(GMCode.DefaultCase, null)); } } // 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(GMCode.B, fallLabel) }; ilSwitch.CaseBlocks.Add(caseBlock); block.Body.RemoveTail(GMCode.B); scope.ExceptWith(content); foreach (var con in FindConditions(content, fallTarget)) { caseBlock.Body.Add(con); } // 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(GMCode.LoopOrSwitchBreak, null) } }); } } } // Debug.Assert((block.Body.First() as ILLabel).Name != "L1938"); // Two-way branch ILLabel trueLabel; ILLabel falseLabel; IList <ILExpression> condExprs; if (block.MatchLastAndBr(GMCode.Bf, out falseLabel, out condExprs, out trueLabel) && condExprs[0].Code != GMCode.Pop) // its resolved { ILExpression condExpr = condExprs[0]; IList <ILNode> body = block.Body; // this is a simple condition, skip anything short curiket for now // Match a condition patern // Convert the brtrue to ILCondition ILCondition ilCond = new ILCondition() { Condition = condExpr, TrueBlock = new ILBlock() { EntryGoto = new ILExpression(GMCode.B, trueLabel) }, FalseBlock = new ILBlock() { EntryGoto = new ILExpression(GMCode.B, falseLabel) } }; block.Body.RemoveTail(GMCode.Bf, GMCode.B); 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); foreach (var con in FindConditions(content, trueTarget)) { ilCond.TrueBlock.Body.Add(con); } } if (falseTarget != null && HasSingleEdgeEnteringBlock(falseTarget)) { HashSet <ControlFlowNode> content = FindDominatedNodes(scope, falseTarget); scope.ExceptWith(content); foreach (var con in FindConditions(content, falseTarget)) { ilCond.FalseBlock.Body.Add(con); } } } } // 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); }
/// <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 == GMCode.B) { 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(GMCode.BadOp)) { current = n as ILTryCatchBlock; break; } } } return(null); } } else if (expr.Code == GMCode.BadOp) { return(Exit(expr, visitedNodes)); } else if (expr.Code == GMCode.LoopOrSwitchBreak) { ILNode breakBlock = GetParents(expr).First(n => n is ILWhileLoop || n is ILSwitch || n is ILWithStatement); return(Exit(breakBlock, new HashSet <ILNode>() { expr })); } else if (expr.Code == GMCode.LoopContinue) { ILNode continueBlock = GetParents(expr).First(n => n is ILWhileLoop || n is ILWithStatement); 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); } ILWithStatement with = node as ILWithStatement; if (with != null) { if (with.Enviroment != null) { return(with.Enviroment); } else { return(Enter(with.Body, visitedNodes)); } } 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()); }