summaryrefslogtreecommitdiff
path: root/ICSharpCode.Decompiler/ILAst/LoopsAndConditions.cs
diff options
context:
space:
mode:
Diffstat (limited to 'ICSharpCode.Decompiler/ILAst/LoopsAndConditions.cs')
-rw-r--r--ICSharpCode.Decompiler/ILAst/LoopsAndConditions.cs443
1 files changed, 0 insertions, 443 deletions
diff --git a/ICSharpCode.Decompiler/ILAst/LoopsAndConditions.cs b/ICSharpCode.Decompiler/ILAst/LoopsAndConditions.cs
deleted file mode 100644
index 32095b03..00000000
--- a/ICSharpCode.Decompiler/ILAst/LoopsAndConditions.cs
+++ /dev/null
@@ -1,443 +0,0 @@
-// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy of this
-// software and associated documentation files (the "Software"), to deal in the Software
-// without restriction, including without limitation the rights to use, copy, modify, merge,
-// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
-// to whom the Software is furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all copies or
-// substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
-// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
-// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
-// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
-// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-// DEALINGS IN THE SOFTWARE.
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-using ICSharpCode.Decompiler.FlowAnalysis;
-
-namespace ICSharpCode.Decompiler.ILAst
-{
- /// <summary>
- /// Description of LoopsAndConditions.
- /// </summary>
- public class LoopsAndConditions
- {
- Dictionary<ILLabel, ControlFlowNode> labelToCfNode = new Dictionary<ILLabel, ControlFlowNode>();
-
- readonly DecompilerContext context;
-
- uint nextLabelIndex = 0;
-
- public LoopsAndConditions(DecompilerContext context)
- {
- this.context = context;
- }
-
- public void FindLoops(ILBlock block)
- {
- if (block.Body.Count > 0) {
- ControlFlowGraph graph;
- graph = BuildGraph(block.Body, (ILLabel)block.EntryGoto.Operand);
- graph.ComputeDominance(context.CancellationToken);
- graph.ComputeDominanceFrontier();
- block.Body = FindLoops(new HashSet<ControlFlowNode>(graph.Nodes.Skip(3)), graph.EntryPoint, false);
- }
- }
-
- public void FindConditions(ILBlock block)
- {
- if (block.Body.Count > 0) {
- ControlFlowGraph graph;
- graph = BuildGraph(block.Body, (ILLabel)block.EntryGoto.Operand);
- graph.ComputeDominance(context.CancellationToken);
- graph.ComputeDominanceFrontier();
- block.Body = FindConditions(new HashSet<ControlFlowNode>(graph.Nodes.Skip(3)), graph.EntryPoint);
- }
- }
-
- ControlFlowGraph BuildGraph(List<ILNode> nodes, ILLabel entryLabel)
- {
- int index = 0;
- List<ControlFlowNode> cfNodes = new List<ControlFlowNode>();
- ControlFlowNode entryPoint = new ControlFlowNode(index++, 0, ControlFlowNodeType.EntryPoint);
- cfNodes.Add(entryPoint);
- ControlFlowNode regularExit = new ControlFlowNode(index++, -1, ControlFlowNodeType.RegularExit);
- cfNodes.Add(regularExit);
- ControlFlowNode exceptionalExit = new ControlFlowNode(index++, -1, ControlFlowNodeType.ExceptionalExit);
- cfNodes.Add(exceptionalExit);
-
- // Create graph nodes
- labelToCfNode = new Dictionary<ILLabel, ControlFlowNode>();
- Dictionary<ILNode, ControlFlowNode> astNodeToCfNode = new Dictionary<ILNode, ControlFlowNode>();
- foreach(ILBasicBlock node in nodes) {
- ControlFlowNode cfNode = new ControlFlowNode(index++, -1, ControlFlowNodeType.Normal);
- cfNodes.Add(cfNode);
- astNodeToCfNode[node] = cfNode;
- cfNode.UserData = node;
-
- // Find all contained labels
- foreach(ILLabel label in node.GetSelfAndChildrenRecursive<ILLabel>()) {
- labelToCfNode[label] = cfNode;
- }
- }
-
- // Entry endge
- ControlFlowNode entryNode = labelToCfNode[entryLabel];
- ControlFlowEdge entryEdge = new ControlFlowEdge(entryPoint, entryNode, JumpType.Normal);
- entryPoint.Outgoing.Add(entryEdge);
- entryNode.Incoming.Add(entryEdge);
-
- // Create edges
- foreach(ILBasicBlock node in nodes) {
- ControlFlowNode source = astNodeToCfNode[node];
-
- // Find all branches
- foreach(ILLabel target in node.GetSelfAndChildrenRecursive<ILExpression>(e => e.IsBranch()).SelectMany(e => e.GetBranchTargets())) {
- ControlFlowNode destination;
- // Labels which are out of out scope will not be in the collection
- // Insert self edge only if we are sure we are a loop
- if (labelToCfNode.TryGetValue(target, out destination) && (destination != source || target == node.Body.FirstOrDefault())) {
- ControlFlowEdge edge = new ControlFlowEdge(source, destination, JumpType.Normal);
- source.Outgoing.Add(edge);
- destination.Incoming.Add(edge);
- }
- }
- }
-
- return new ControlFlowGraph(cfNodes.ToArray());
- }
-
- 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;
- }
-
- 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;
- }
-
- static bool HasSingleEdgeEnteringBlock(ControlFlowNode node)
- {
- return node.Incoming.Count(edge => !node.Dominates(edge.Source)) == 1;
- }
-
- static HashSet<ControlFlowNode> FindDominatedNodes(HashSet<ControlFlowNode> scope, ControlFlowNode head)
- {
- HashSet<ControlFlowNode> agenda = new HashSet<ControlFlowNode>();
- HashSet<ControlFlowNode> result = new HashSet<ControlFlowNode>();
- agenda.Add(head);
-
- while(agenda.Count > 0) {
- ControlFlowNode addNode = agenda.First();
- agenda.Remove(addNode);
-
- if (scope.Contains(addNode) && head.Dominates(addNode) && result.Add(addNode)) {
- foreach (var successor in addNode.Successors) {
- agenda.Add(successor);
- }
- }
- }
-
- return result;
- }
-
- static HashSet<ControlFlowNode> FindLoopContent(HashSet<ControlFlowNode> scope, ControlFlowNode head)
- {
- var viaBackEdges = head.Predecessors.Where(p => head.Dominates(p));
- HashSet<ControlFlowNode> agenda = new HashSet<ControlFlowNode>(viaBackEdges);
- HashSet<ControlFlowNode> result = new HashSet<ControlFlowNode>();
-
- while(agenda.Count > 0) {
- ControlFlowNode addNode = agenda.First();
- agenda.Remove(addNode);
-
- if (scope.Contains(addNode) && head.Dominates(addNode) && result.Add(addNode)) {
- foreach (var predecessor in addNode.Predecessors) {
- agenda.Add(predecessor);
- }
- }
- }
- if (scope.Contains(head))
- result.Add(head);
-
- return result;
- }
- }
-}