mirror of https://github.com/icsharpcode/ILSpy.git
11 changed files with 600 additions and 1111 deletions
@ -1,188 +0,0 @@
@@ -1,188 +0,0 @@
|
||||
using System; |
||||
using System.Collections; |
||||
using System.Collections.Generic; |
||||
using System.Diagnostics; |
||||
|
||||
namespace Decompiler.ControlFlow |
||||
{ |
||||
public abstract partial class Node |
||||
{ |
||||
public void Optimize() |
||||
{ |
||||
if (Options.ReduceLoops) { |
||||
OptimizeLoops(); |
||||
} |
||||
if (Options.ReduceConditonals) { |
||||
OptimizeShortCircuits(); |
||||
OptimizeConditions(); |
||||
} |
||||
} |
||||
|
||||
public void OptimizeLoops() |
||||
{ |
||||
Reset: |
||||
foreach(Node child in this.Childs) { |
||||
if (child.Predecessors.Count == 1) { |
||||
Node predecessor = child.Predecessors[0]; |
||||
Node mergedNode; |
||||
if (child.Successors.Contains(predecessor)) { |
||||
mergedNode = MergeChilds<Loop>(predecessor, child); |
||||
} else { |
||||
mergedNode = MergeChilds<AcyclicGraph>(predecessor, child); |
||||
} |
||||
mergedNode.FalttenAcyclicChilds(); |
||||
goto Reset; |
||||
} |
||||
} |
||||
// If the result is single acyclic node, eliminate it
|
||||
if (this.Childs.Count == 1 && this.HeadChild is AcyclicGraph) { |
||||
Node headChild = this.HeadChild; |
||||
this.Childs.Remove(this.HeadChild); |
||||
headChild.Childs.MoveTo(this); |
||||
} |
||||
} |
||||
|
||||
NodeCollection GetReachableNodes() |
||||
{ |
||||
NodeCollection reachableNodes = new NodeCollection(); |
||||
reachableNodes.Add(this); |
||||
for(int i = 0; i < reachableNodes.Count; i++) { |
||||
foreach(Node alsoReachable in reachableNodes[i].Successors) { |
||||
// Do not go though the head child
|
||||
if (alsoReachable != this.Parent.HeadChild) { |
||||
reachableNodes.Add(alsoReachable); |
||||
} |
||||
} |
||||
} |
||||
return reachableNodes; |
||||
} |
||||
|
||||
public void OptimizeShortCircuits() |
||||
{ |
||||
foreach(Node child in this.Childs) { |
||||
child.OptimizeShortCircuits(); |
||||
} |
||||
|
||||
Reset: |
||||
foreach(Node child in this.Childs) { |
||||
if (TryOptimizeShortCircuit(child)) { |
||||
goto Reset; |
||||
} |
||||
} |
||||
} |
||||
|
||||
public static bool TryOptimizeShortCircuit(Node head) |
||||
{ |
||||
if ((head is BasicBlock) && |
||||
(head as BasicBlock).BranchBasicBlock != null && |
||||
(head as BasicBlock).FallThroughBasicBlock != null) { |
||||
head.Parent.MergeChilds<SimpleBranch>(head); |
||||
return true; |
||||
} |
||||
|
||||
Branch top = head as Branch; |
||||
if (top == null) return false; |
||||
|
||||
Branch left = head.FloatUpToNeighbours(top.TrueSuccessor) as Branch; |
||||
Branch right = head.FloatUpToNeighbours(top.FalseSuccessor) as Branch; |
||||
|
||||
// A & B
|
||||
if (left != null && |
||||
left.Predecessors.Count == 1 && |
||||
left.FalseSuccessor == top.FalseSuccessor) { |
||||
ShortCircuitBranch scBranch = top.Parent.MergeChilds<ShortCircuitBranch>(top, left); |
||||
scBranch.Operator = ShortCircuitOperator.LeftAndRight; |
||||
return true; |
||||
} |
||||
|
||||
// ~A | B
|
||||
if (left != null && |
||||
left.Predecessors.Count == 1 && |
||||
left.TrueSuccessor == top.FalseSuccessor) { |
||||
ShortCircuitBranch scBranch = top.Parent.MergeChilds<ShortCircuitBranch>(top, left); |
||||
scBranch.Operator = ShortCircuitOperator.NotLeftOrRight; |
||||
return true; |
||||
} |
||||
|
||||
// A | B
|
||||
if (right != null && |
||||
right.Predecessors.Count == 1 && |
||||
right.TrueSuccessor == top.TrueSuccessor) { |
||||
ShortCircuitBranch scBranch = top.Parent.MergeChilds<ShortCircuitBranch>(top, right); |
||||
scBranch.Operator = ShortCircuitOperator.LeftOrRight; |
||||
return true; |
||||
} |
||||
|
||||
// ~A & B
|
||||
if (right != null && |
||||
right.Predecessors.Count == 1 && |
||||
right.FalseSuccessor == top.TrueSuccessor) { |
||||
ShortCircuitBranch scBranch = top.Parent.MergeChilds<ShortCircuitBranch>(top, right); |
||||
scBranch.Operator = ShortCircuitOperator.NotLeftAndRight; |
||||
return true; |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
|
||||
public void OptimizeConditions() |
||||
{ |
||||
foreach(Node child in this.Childs) { |
||||
child.OptimizeConditions(); |
||||
} |
||||
|
||||
Node conditionNode = this.HeadChild; |
||||
while(conditionNode != null) { |
||||
// Keep looking for some conditional block
|
||||
if (conditionNode is Branch) { |
||||
// Found start of conditional
|
||||
OptimizeIf((Branch)conditionNode); |
||||
// Restart
|
||||
conditionNode = this.HeadChild; |
||||
continue; |
||||
} else if (conditionNode.Successors.Count > 0) { |
||||
// Keep looking down
|
||||
conditionNode = conditionNode.Successors[0]; |
||||
if (conditionNode == this.HeadChild) { |
||||
return; |
||||
} |
||||
continue; // Next
|
||||
} else { |
||||
return; // End of block
|
||||
} |
||||
} |
||||
} |
||||
|
||||
public static void OptimizeIf(Branch condition) |
||||
{ |
||||
Node trueStart = condition.FloatUpToNeighbours(condition.TrueSuccessor); |
||||
Node falseStart = condition.FloatUpToNeighbours(condition.FalseSuccessor); |
||||
|
||||
NodeCollection trueReachable = trueStart != null ? trueStart.GetReachableNodes() : NodeCollection.Empty; |
||||
NodeCollection falseReachable = falseStart != null ? falseStart.GetReachableNodes() : NodeCollection.Empty; |
||||
NodeCollection commonReachable = NodeCollection.Intersect(trueReachable, falseReachable); |
||||
|
||||
NodeCollection trueNodes = trueReachable.Clone(); |
||||
trueNodes.RemoveRange(commonReachable); |
||||
NodeCollection falseNodes = falseReachable.Clone(); |
||||
falseNodes.RemoveRange(commonReachable); |
||||
|
||||
// Replace the basic block with condition node
|
||||
Node conditionParent = condition.Parent; |
||||
int conditionIndex = condition.Index; |
||||
ConditionalNode conditionalNode = new ConditionalNode(condition); |
||||
conditionalNode.MoveTo(conditionParent, conditionIndex); |
||||
|
||||
// If there are no common nodes, let the 'true' block be the default
|
||||
if (commonReachable.Count > 0) { |
||||
trueNodes.MoveTo(conditionalNode.TrueBody); |
||||
} |
||||
|
||||
falseNodes.MoveTo(conditionalNode.FalseBody); |
||||
|
||||
// Optimize the created subtrees
|
||||
conditionalNode.TrueBody.OptimizeConditions(); |
||||
conditionalNode.FalseBody.OptimizeConditions(); |
||||
} |
||||
} |
||||
} |
@ -1,233 +0,0 @@
@@ -1,233 +0,0 @@
|
||||
using System; |
||||
using System.Collections; |
||||
using System.Collections.Generic; |
||||
|
||||
namespace Decompiler.ControlFlow |
||||
{ |
||||
public abstract partial class Node |
||||
{ |
||||
public static int NextNodeID = 1; |
||||
|
||||
int id; |
||||
string label; |
||||
Node parent; |
||||
NodeCollection childs = new NodeCollection(); |
||||
|
||||
// Structural and linking cache
|
||||
NodeCollection basicBlocks_cache = null; |
||||
NodeCollection predecessors_cache = null; |
||||
NodeCollection successors_cache = null; |
||||
|
||||
public int ID { |
||||
get { return id; } |
||||
} |
||||
|
||||
public string Label { |
||||
get { return label; } |
||||
} |
||||
|
||||
public Node Parent { |
||||
get { return parent; } |
||||
} |
||||
|
||||
public Node HeadChild { |
||||
get { |
||||
if (this.Childs.Count > 0) { |
||||
return this.Childs[0]; |
||||
} else { |
||||
return null; |
||||
} |
||||
} |
||||
} |
||||
|
||||
public NodeCollection Childs { |
||||
get { |
||||
return childs; |
||||
} |
||||
} |
||||
|
||||
/// <summary> All basic blocks within the scope of this node (inclusive) </summary>
|
||||
public NodeCollection BasicBlocks { |
||||
get { |
||||
if (basicBlocks_cache == null) { |
||||
NodeCollection basicBlocks = new NodeCollection(); |
||||
|
||||
if (this is BasicBlock) { |
||||
basicBlocks.Add(this); |
||||
} |
||||
foreach(Node child in this.Childs) { |
||||
basicBlocks.AddRange(child.BasicBlocks); |
||||
} |
||||
|
||||
basicBlocks_cache = basicBlocks; |
||||
} |
||||
return basicBlocks_cache; |
||||
} |
||||
} |
||||
|
||||
NodeCollection FloatUpToNeighbours(IEnumerable<BasicBlock> basicBlocks) |
||||
{ |
||||
NodeCollection neighbours = new NodeCollection(); |
||||
if (this.Parent != null) { |
||||
foreach(BasicBlock basicBlock in basicBlocks) { |
||||
Node targetNode = FloatUpToNeighbours(basicBlock); |
||||
// The target is outside the scope of the parent node
|
||||
if (targetNode == null) continue; |
||||
// This child is a loop
|
||||
if (targetNode == this) continue; |
||||
// We found a target in our scope
|
||||
neighbours.Add(targetNode); |
||||
} |
||||
} |
||||
return neighbours; |
||||
} |
||||
|
||||
Node FloatUpToNeighbours(BasicBlock basicBlock) |
||||
{ |
||||
// Find neighbour coresponding to the basickBlock
|
||||
Node targetNode = basicBlock; |
||||
while(targetNode != null && targetNode.Parent != this.Parent) { |
||||
targetNode = targetNode.Parent; |
||||
} |
||||
return targetNode; |
||||
} |
||||
|
||||
public NodeCollection Predecessors { |
||||
get { |
||||
if (predecessors_cache == null) { |
||||
List<BasicBlock> basicBlockPredecessors = new List<BasicBlock>(); |
||||
foreach(BasicBlock basicBlock in this.BasicBlocks) { |
||||
foreach(BasicBlock basicBlockPredecessor in basicBlock.BasicBlockPredecessors) { |
||||
basicBlockPredecessors.Add(basicBlockPredecessor); |
||||
} |
||||
} |
||||
|
||||
predecessors_cache = FloatUpToNeighbours(basicBlockPredecessors); |
||||
} |
||||
return predecessors_cache; |
||||
} |
||||
} |
||||
|
||||
public NodeCollection Successors { |
||||
get { |
||||
if (successors_cache == null) { |
||||
List<BasicBlock> basicBlockSuccessors = new List<BasicBlock>(); |
||||
foreach(BasicBlock basicBlock in this.BasicBlocks) { |
||||
foreach(BasicBlock basicBlockSuccessor in basicBlock.BasicBlockSuccessors) { |
||||
basicBlockSuccessors.Add(basicBlockSuccessor); |
||||
} |
||||
} |
||||
|
||||
successors_cache = FloatUpToNeighbours(basicBlockSuccessors); |
||||
} |
||||
return successors_cache; |
||||
} |
||||
} |
||||
|
||||
int Index { |
||||
get { |
||||
if (this.Parent == null) throw new Exception("Does not have a parent"); |
||||
return this.Parent.Childs.IndexOf(this); |
||||
} |
||||
} |
||||
|
||||
public Node NextNode { |
||||
get { |
||||
int index = this.Index + 1; |
||||
if (0 <= index && index < this.Parent.Childs.Count) { |
||||
return this.Parent.Childs[index]; |
||||
} else { |
||||
return null; |
||||
} |
||||
} |
||||
} |
||||
|
||||
public string Description { |
||||
get { |
||||
return ToString(); |
||||
} |
||||
} |
||||
|
||||
protected Node() |
||||
{ |
||||
this.id = NextNodeID++; |
||||
this.label = this.GetType().Name + "_" + ID; |
||||
this.Childs.Added += delegate(object sender, NodeEventArgs e) { |
||||
if (e.Node.Parent != null) { |
||||
throw new Exception("Node is already assigned to other parent"); |
||||
} |
||||
e.Node.parent = this; |
||||
NotifyChildsChanged(); |
||||
}; |
||||
this.Childs.Removed += delegate(object sender, NodeEventArgs e) { |
||||
e.Node.parent = null; |
||||
NotifyChildsChanged(); |
||||
}; |
||||
} |
||||
|
||||
void NotifyChildsChanged() |
||||
{ |
||||
this.basicBlocks_cache = null; |
||||
foreach(Node child in this.Childs) { |
||||
child.predecessors_cache = null; |
||||
child.successors_cache = null; |
||||
} |
||||
if (this.Parent != null) { |
||||
this.Parent.NotifyChildsChanged(); |
||||
} |
||||
} |
||||
|
||||
public override string ToString() |
||||
{ |
||||
System.Text.StringBuilder sb = new System.Text.StringBuilder(); |
||||
sb.Append(this.GetType().Name); |
||||
sb.Append(" "); |
||||
sb.Append(ID); |
||||
sb.Append(" "); |
||||
|
||||
sb.Append("("); |
||||
|
||||
if (this.Predecessors.Count > 0) { |
||||
sb.Append("Predecessors:"); |
||||
bool isFirst = true; |
||||
foreach(Node predecessor in this.Predecessors) { |
||||
if (isFirst) { |
||||
isFirst = false; |
||||
} else { |
||||
sb.Append(","); |
||||
} |
||||
sb.Append(predecessor.ID); |
||||
} |
||||
sb.Append(" "); |
||||
} |
||||
|
||||
if (this.Successors.Count > 0) { |
||||
sb.Append("Successors:"); |
||||
bool isFirst = true; |
||||
foreach(Node successor in this.Successors) { |
||||
if (isFirst) { |
||||
isFirst = false; |
||||
} else { |
||||
sb.Append(","); |
||||
} |
||||
sb.Append(successor.ID); |
||||
} |
||||
sb.Append(" "); |
||||
} |
||||
|
||||
if (this.Parent != null) { |
||||
sb.Append("Parent:"); |
||||
sb.Append(this.Parent.ID); |
||||
sb.Append(" "); |
||||
} |
||||
|
||||
if (sb[sb.Length - 1] == '(') { |
||||
sb.Length -= 1; |
||||
} else if (sb[sb.Length - 1] == ' ') { |
||||
sb.Length -= 1; |
||||
sb.Append(")"); |
||||
} |
||||
return sb.ToString(); |
||||
} |
||||
} |
||||
} |
@ -1,60 +0,0 @@
@@ -1,60 +0,0 @@
|
||||
using System; |
||||
using System.Collections; |
||||
using System.Collections.Generic; |
||||
|
||||
namespace Decompiler.ControlFlow |
||||
{ |
||||
public abstract partial class Node |
||||
{ |
||||
public void Remove() |
||||
{ |
||||
if (this.Parent != null) { |
||||
this.Parent.Childs.Remove(this); |
||||
} |
||||
} |
||||
|
||||
public void MoveTo(Node newNode) |
||||
{ |
||||
MoveTo(newNode, newNode.Childs.Count); |
||||
} |
||||
|
||||
public void MoveTo(Node newNode, int index) |
||||
{ |
||||
this.Remove(); |
||||
newNode.Childs.Insert(index, this); |
||||
} |
||||
|
||||
T MergeChilds<T>(params Node[] nodes) where T: Node, new() |
||||
{ |
||||
foreach(Node node in nodes) { |
||||
if (node == null) throw new ArgumentNullException("nodes"); |
||||
if (node.Parent != this) throw new ArgumentException("The node is not my child"); |
||||
} |
||||
if (nodes.Length == 0) throw new ArgumentException("At least one node must be specified"); |
||||
|
||||
T mergedNode = new T(); |
||||
|
||||
// Add the merged node
|
||||
int headIndex = this.Childs.IndexOf(nodes[0]); |
||||
this.Childs.Insert(headIndex, mergedNode); |
||||
|
||||
foreach(Node node in nodes) { |
||||
node.MoveTo(mergedNode); |
||||
} |
||||
|
||||
return mergedNode; |
||||
} |
||||
|
||||
public void FalttenAcyclicChilds() |
||||
{ |
||||
Reset: |
||||
foreach(Node child in this.Childs) { |
||||
if (child is AcyclicGraph) { |
||||
child.Childs.MoveTo(this, child.Index); |
||||
child.Remove(); |
||||
goto Reset; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
@ -1,130 +0,0 @@
@@ -1,130 +0,0 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
|
||||
namespace Decompiler.ControlFlow |
||||
{ |
||||
public class NodeEventArgs: EventArgs |
||||
{ |
||||
Node node; |
||||
|
||||
public Node Node { |
||||
get { return node; } |
||||
} |
||||
|
||||
public NodeEventArgs(Node node) |
||||
{ |
||||
this.node = node; |
||||
} |
||||
} |
||||
|
||||
public class NodeCollection: System.Collections.ObjectModel.Collection<Node> |
||||
{ |
||||
public static NodeCollection Empty = new NodeCollection(); |
||||
|
||||
public event EventHandler<NodeEventArgs> Added; |
||||
public event EventHandler<NodeEventArgs> Removed; |
||||
|
||||
protected virtual void OnAdded(Node node) |
||||
{ |
||||
if (Added != null) { |
||||
Added(this, new NodeEventArgs(node)); |
||||
} |
||||
} |
||||
|
||||
protected virtual void OnRemoved(Node node) |
||||
{ |
||||
if (Removed != null) { |
||||
Removed(this, new NodeEventArgs(node)); |
||||
} |
||||
} |
||||
|
||||
protected override void ClearItems() |
||||
{ |
||||
while(this.Count > 0) { |
||||
this.RemoveAt(this.Count - 1); |
||||
} |
||||
} |
||||
|
||||
protected override void InsertItem(int index, Node item) |
||||
{ |
||||
if (!this.Contains(item)) { |
||||
base.InsertItem(index, item); |
||||
} |
||||
OnAdded(item); |
||||
} |
||||
|
||||
protected override void RemoveItem(int index) |
||||
{ |
||||
Node node = this[index]; |
||||
base.RemoveItem(index); |
||||
OnRemoved(node); |
||||
} |
||||
|
||||
protected override void SetItem(int index, Node item) |
||||
{ |
||||
this.RemoveAt(index); |
||||
this.Insert(index, item); |
||||
} |
||||
|
||||
|
||||
public void AddRange(IEnumerable<Node> items) |
||||
{ |
||||
foreach(Node item in items) { |
||||
this.Add(item); |
||||
} |
||||
} |
||||
|
||||
public void RemoveRange(IEnumerable<Node> items) |
||||
{ |
||||
foreach(Node item in items) { |
||||
this.Remove(item); |
||||
} |
||||
} |
||||
|
||||
public void MoveTo(Node newNode) |
||||
{ |
||||
foreach(Node child in this.Clone()) { |
||||
child.MoveTo(newNode); |
||||
} |
||||
} |
||||
|
||||
public void MoveTo(Node newNode, int index) |
||||
{ |
||||
foreach(Node child in this.Clone()) { |
||||
child.MoveTo(newNode, index); |
||||
index++; |
||||
} |
||||
} |
||||
|
||||
public NodeCollection() |
||||
{ |
||||
|
||||
} |
||||
|
||||
public NodeCollection(IEnumerable<Node> items) |
||||
{ |
||||
this.AddRange(items); |
||||
} |
||||
|
||||
public NodeCollection Clone() |
||||
{ |
||||
return new NodeCollection(this); |
||||
} |
||||
|
||||
public static NodeCollection Intersect(NodeCollection collectionA, NodeCollection collectionB) |
||||
{ |
||||
NodeCollection result = new NodeCollection(); |
||||
foreach(Node a in collectionA) { |
||||
if (collectionB.Contains(a)) { |
||||
result.Add(a); |
||||
} |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
public override string ToString() |
||||
{ |
||||
return string.Format("{0} Count = {1}", typeof(NodeCollection).Name, this.Count); |
||||
} |
||||
} |
||||
} |
@ -1,254 +0,0 @@
@@ -1,254 +0,0 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
using Mono.Cecil; |
||||
using Mono.Cecil.Cil; |
||||
using Decompiler.Rocks; |
||||
|
||||
namespace Decompiler.ControlFlow |
||||
{ |
||||
public class BasicBlock: Node |
||||
{ |
||||
List<ILNode> body = new List<ILNode>(); |
||||
List<BasicBlock> basicBlockPredecessors = new List<BasicBlock>(); |
||||
BasicBlock fallThroughBasicBlock; |
||||
BasicBlock branchBasicBlock; |
||||
|
||||
public List<ILNode> Body { |
||||
get { return body; } |
||||
} |
||||
|
||||
public List<BasicBlock> BasicBlockPredecessors { |
||||
get { return basicBlockPredecessors; } |
||||
} |
||||
|
||||
public BasicBlock FallThroughBasicBlock { |
||||
get { return fallThroughBasicBlock; } |
||||
set { fallThroughBasicBlock = value; } |
||||
} |
||||
|
||||
public BasicBlock BranchBasicBlock { |
||||
get { return branchBasicBlock; } |
||||
set { branchBasicBlock = value; } |
||||
} |
||||
|
||||
public IEnumerable<BasicBlock> BasicBlockSuccessors { |
||||
get { |
||||
if (this.FallThroughBasicBlock != null) { |
||||
yield return this.FallThroughBasicBlock; |
||||
} |
||||
if (this.BranchBasicBlock != null) { |
||||
yield return this.BranchBasicBlock; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
public enum ShortCircuitOperator { |
||||
LeftAndRight, |
||||
LeftOrRight, |
||||
NotLeftAndRight, |
||||
NotLeftOrRight, |
||||
} |
||||
|
||||
public abstract class Branch: Node |
||||
{ |
||||
public abstract BasicBlock FirstBasicBlock { get; } |
||||
public abstract BasicBlock TrueSuccessor { get; } |
||||
public abstract BasicBlock FalseSuccessor { get; } |
||||
} |
||||
|
||||
public class SimpleBranch: Branch |
||||
{ |
||||
public override BasicBlock FirstBasicBlock { |
||||
get { |
||||
return this.BasicBlock; |
||||
} |
||||
} |
||||
|
||||
public BasicBlock BasicBlock { |
||||
get { return (BasicBlock)this.Childs[0]; } |
||||
} |
||||
|
||||
public override BasicBlock TrueSuccessor { |
||||
get { return this.BasicBlock.BranchBasicBlock; } |
||||
} |
||||
|
||||
public override BasicBlock FalseSuccessor { |
||||
get { return this.BasicBlock.FallThroughBasicBlock; } |
||||
} |
||||
} |
||||
|
||||
public class ShortCircuitBranch: Branch |
||||
{ |
||||
ShortCircuitOperator @operator; |
||||
|
||||
public override BasicBlock FirstBasicBlock { |
||||
get { |
||||
return this.Left.FirstBasicBlock; |
||||
} |
||||
} |
||||
|
||||
public Branch Left { |
||||
get { return (Branch)this.Childs[0];; } |
||||
} |
||||
|
||||
public Branch Right { |
||||
get { return (Branch)this.Childs[1]; } |
||||
} |
||||
|
||||
public ShortCircuitOperator Operator { |
||||
get { return @operator; } |
||||
set { @operator = value; } |
||||
} |
||||
|
||||
public override BasicBlock TrueSuccessor { |
||||
get { return this.Right.TrueSuccessor; } |
||||
} |
||||
|
||||
public override BasicBlock FalseSuccessor { |
||||
get { return this.Right.FalseSuccessor; } |
||||
} |
||||
} |
||||
|
||||
public class MethodBodyGraph: Node |
||||
{ |
||||
BasicBlock methodEntry; |
||||
|
||||
public BasicBlock MethodEntry { |
||||
get { return methodEntry; } |
||||
} |
||||
|
||||
Dictionary<ILLabel, BasicBlock> labelToBasicBlock = new Dictionary<ILLabel, BasicBlock>(); |
||||
|
||||
public MethodBodyGraph(List<ILNode> ast) |
||||
{ |
||||
if (ast.Count == 0) throw new ArgumentException("Count == 0", "ast"); |
||||
this.Childs.AddRange(SplitToBasicBlocks(ast)); |
||||
|
||||
// Add branch links to BasicBlocks
|
||||
foreach(BasicBlock basicBlock in this.BasicBlocks) { |
||||
foreach(ILNode node in basicBlock.Body) { |
||||
if (node is ILExpression) { |
||||
ILExpression expr = (ILExpression)node; |
||||
if (expr.Operand is ILLabel) { |
||||
BasicBlock target = labelToBasicBlock[(ILLabel)expr.Operand]; |
||||
basicBlock.BranchBasicBlock = target; |
||||
target.BasicBlockPredecessors.Add(basicBlock); |
||||
} |
||||
// TODO: Switch
|
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
public List<Node> SplitToBasicBlocks(List<ILNode> ast) |
||||
{ |
||||
if (ast.Count == 0) return new List<Node>(); |
||||
|
||||
List<Node> nodes = new List<Node>(); |
||||
|
||||
BasicBlock basicBlock = null; |
||||
|
||||
for(int i = 0; i < ast.Count; i++) { |
||||
if (i == 0 || |
||||
ast[i] is ILLabel || |
||||
ast[i - 1] is ILTryCatchBlock || |
||||
ast[i] is ILTryCatchBlock || |
||||
(ast[i - 1] is ILExpression) && ((ILExpression)ast[i - 1]).OpCode.IsBranch() || |
||||
(ast[i] is ILExpression) && ((ILExpression)ast[i]).OpCode.IsBranch()) |
||||
{ |
||||
BasicBlock oldBB = basicBlock; |
||||
basicBlock = new BasicBlock(); |
||||
if (methodEntry == null) methodEntry = basicBlock; |
||||
nodes.Add(basicBlock); |
||||
// Links
|
||||
if (oldBB != null && ast[i - 1] is ILExpression && ((ILExpression)ast[i - 1]).OpCode.CanFallThough()) { |
||||
oldBB.FallThroughBasicBlock = basicBlock; |
||||
basicBlock.BasicBlockPredecessors.Add(oldBB); |
||||
} |
||||
} |
||||
if (ast[i] is ILTryCatchBlock) { |
||||
basicBlock.Childs.Add(ConvertTryCatch((ILTryCatchBlock)ast[i])); |
||||
} else { |
||||
basicBlock.Body.Add(ast[i]); |
||||
} |
||||
if (ast[i] is ILLabel) { |
||||
labelToBasicBlock[(ILLabel)ast[i]] = basicBlock; |
||||
} |
||||
} |
||||
|
||||
return nodes; |
||||
} |
||||
|
||||
public TryCatchNode ConvertTryCatch(ILTryCatchBlock ilTryCatch) |
||||
{ |
||||
TryCatchNode tryCatch = new TryCatchNode(); |
||||
|
||||
Block tryBlock = new Block(); |
||||
tryBlock.Childs.AddRange(SplitToBasicBlocks(ilTryCatch.TryBlock)); |
||||
tryBlock.MoveTo(tryCatch); |
||||
|
||||
Block finallyBlock = new Block(); |
||||
if (ilTryCatch.FinallyBlock != null) { |
||||
finallyBlock.Childs.AddRange(SplitToBasicBlocks(ilTryCatch.FinallyBlock)); |
||||
} |
||||
finallyBlock.MoveTo(tryCatch); |
||||
|
||||
foreach(ILTryCatchBlock.CatchBlock cb in ilTryCatch.CatchBlocks) { |
||||
tryCatch.Types.Add(cb.ExceptionType); |
||||
Block catchBlock = new Block(); |
||||
catchBlock.Childs.AddRange(SplitToBasicBlocks(cb.Body)); |
||||
catchBlock.MoveTo(tryCatch); |
||||
} |
||||
|
||||
return tryCatch; |
||||
} |
||||
|
||||
} |
||||
|
||||
public class TryCatchNode: Node |
||||
{ |
||||
public List<TypeReference> Types = new List<TypeReference>(); |
||||
} |
||||
|
||||
public class AcyclicGraph: Node |
||||
{ |
||||
} |
||||
|
||||
public class Loop: Node |
||||
{ |
||||
} |
||||
|
||||
public class Block: Node |
||||
{ |
||||
} |
||||
|
||||
public class ConditionalNode: Node |
||||
{ |
||||
Branch condition; |
||||
Block trueBody = new Block(); |
||||
Block falseBody = new Block(); |
||||
|
||||
public Branch Condition { |
||||
get { return condition; } |
||||
} |
||||
|
||||
public Block TrueBody { |
||||
get { return trueBody; } |
||||
} |
||||
|
||||
public Block FalseBody { |
||||
get { return falseBody; } |
||||
} |
||||
|
||||
public ConditionalNode(Branch condition) |
||||
{ |
||||
this.condition = condition; |
||||
|
||||
condition.MoveTo(this); |
||||
trueBody.MoveTo(this); |
||||
falseBody.MoveTo(this); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,322 @@
@@ -0,0 +1,322 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
using ICSharpCode.Decompiler.FlowAnalysis; |
||||
using Mono.Cecil; |
||||
using Mono.Cecil.Cil; |
||||
using Decompiler.Rocks; |
||||
|
||||
namespace Decompiler.ControlFlow |
||||
{ |
||||
public class ILAstOptimizer |
||||
{ |
||||
public void Optimize(List<ILNode> ast) |
||||
{ |
||||
ILLabel entryLabel; |
||||
List<ILTryCatchBlock> tryCatchBlocks = ast.OfType<ILTryCatchBlock>().ToList(); |
||||
|
||||
ControlFlowGraph graph; |
||||
|
||||
ast = SplitToMovableBlocks(ast, out entryLabel); |
||||
|
||||
graph = BuildGraph(ast, entryLabel); |
||||
graph.ComputeDominance(); |
||||
graph.ComputeDominanceFrontier(); |
||||
ast = FindLoops(new HashSet<ControlFlowNode>(graph.Nodes.Skip(3)), graph.EntryPoint); |
||||
|
||||
graph = BuildGraph(ast, entryLabel); |
||||
graph.ComputeDominance(); |
||||
graph.ComputeDominanceFrontier(); |
||||
ast = FindConditions(new HashSet<ControlFlowNode>(graph.Nodes.Skip(3)), graph.EntryPoint); |
||||
|
||||
// Recursively optimze try-cath blocks
|
||||
foreach(ILTryCatchBlock tryCatchBlock in tryCatchBlocks) { |
||||
Optimize(tryCatchBlock.TryBlock.Body); |
||||
foreach(ILTryCatchBlock.CatchBlock catchBlock in tryCatchBlock.CatchBlocks) { |
||||
Optimize(catchBlock.Body); |
||||
} |
||||
Optimize(tryCatchBlock.FinallyBlock.Body); |
||||
} |
||||
} |
||||
|
||||
int nextLabelIndex = 0; |
||||
|
||||
/// <summary>
|
||||
/// Group input into a set of blocks that can be later arbitraliby schufled.
|
||||
/// The method adds necessary branches to make control flow between blocks
|
||||
/// explicit and thus order independent.
|
||||
/// </summary>
|
||||
List<ILNode> SplitToMovableBlocks(List<ILNode> ast, out ILLabel entryLabel) |
||||
{ |
||||
List<ILNode> blocks = new List<ILNode>(); |
||||
|
||||
ILBlock block = new ILBlock(); |
||||
blocks.Add(block); |
||||
entryLabel = new ILLabel() { Name = "Block_" + (nextLabelIndex++) }; |
||||
block.Body.Add(entryLabel); |
||||
|
||||
if (ast.Count == 0) |
||||
return blocks; |
||||
block.Body.Add(ast[0]); |
||||
|
||||
for (int i = 1; i < ast.Count; i++) { |
||||
ILNode lastNode = ast[i - 1]; |
||||
ILNode currNode = ast[i]; |
||||
|
||||
// Insert split
|
||||
if ((currNode is ILLabel && !(lastNode is ILLabel)) || |
||||
lastNode is ILTryCatchBlock || |
||||
currNode is ILTryCatchBlock || |
||||
(lastNode is ILExpression) && ((ILExpression)lastNode).OpCode.IsBranch() || |
||||
(currNode is ILExpression) && ((ILExpression)currNode).OpCode.IsBranch()) |
||||
{ |
||||
ILBlock lastBlock = block; |
||||
block = new ILBlock(); |
||||
blocks.Add(block); |
||||
|
||||
// Explicit branch from one block to other
|
||||
// (unless the last expression was unconditional branch)
|
||||
if (!(lastNode is ILExpression) || ((ILExpression)lastNode).OpCode.CanFallThough()) { |
||||
ILLabel blockLabel = new ILLabel() { Name = "Block_" + (nextLabelIndex++) }; |
||||
lastBlock.Body.Add(new ILExpression(OpCodes.Br_S, blockLabel)); |
||||
block.Body.Add(blockLabel); |
||||
} |
||||
} |
||||
|
||||
block.Body.Add(currNode); |
||||
} |
||||
|
||||
return blocks; |
||||
} |
||||
|
||||
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
|
||||
Dictionary<ILLabel, ControlFlowNode> labelToCfNode = new Dictionary<ILLabel, ControlFlowNode>(); |
||||
Dictionary<ILNode, ControlFlowNode> astNodeToCfNode = new Dictionary<ILNode, ControlFlowNode>(); |
||||
foreach(ILNode 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.GetChildrenRecursive<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(ILNode node in nodes) { |
||||
ControlFlowNode source = astNodeToCfNode[node]; |
||||
|
||||
// Find all branches
|
||||
foreach(ILExpression child in node.GetChildrenRecursive<ILExpression>()) { |
||||
IEnumerable<ILLabel> targets = child.GetBranchTargets(); |
||||
if (targets != null) { |
||||
foreach(ILLabel target in targets) { |
||||
ControlFlowNode destination; |
||||
// Labels which are out of out scope will not be int the collection
|
||||
if (labelToCfNode.TryGetValue(target, out destination) && destination != source) { |
||||
ControlFlowEdge edge = new ControlFlowEdge(source, destination, JumpType.Normal); |
||||
source.Outgoing.Add(edge); |
||||
destination.Incoming.Add(edge); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
return new ControlFlowGraph(cfNodes.ToArray()); |
||||
} |
||||
|
||||
static List<ILNode> FindLoops(HashSet<ControlFlowNode> body, ControlFlowNode entryPoint) |
||||
{ |
||||
List<ILNode> result = new List<ILNode>(); |
||||
|
||||
Queue<ControlFlowNode> agenda = new Queue<ControlFlowNode>(); |
||||
agenda.Enqueue(entryPoint); |
||||
while(agenda.Count > 0) { |
||||
ControlFlowNode node = agenda.Dequeue(); |
||||
|
||||
if (body.Contains(node) |
||||
&& node.DominanceFrontier.Contains(node) |
||||
&& node != entryPoint) |
||||
{ |
||||
HashSet<ControlFlowNode> loopContents = new HashSet<ControlFlowNode>(); |
||||
FindLoopContents(body, loopContents, node, node); |
||||
|
||||
// Move the content into loop block
|
||||
body.ExceptWith(loopContents); |
||||
result.Add(new ILLoop() { ContentBlock = new ILBlock(FindLoops(loopContents, node)) }); |
||||
} |
||||
|
||||
// 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 body) { |
||||
result.Add((ILNode)node.UserData); |
||||
} |
||||
|
||||
return result; |
||||
} |
||||
|
||||
static void FindLoopContents(HashSet<ControlFlowNode> body, HashSet<ControlFlowNode> loopContents, ControlFlowNode loopHead, ControlFlowNode addNode) |
||||
{ |
||||
if (body.Contains(addNode) && loopHead.Dominates(addNode) && loopContents.Add(addNode)) { |
||||
foreach (var edge in addNode.Incoming) { |
||||
FindLoopContents(body, loopContents, loopHead, edge.Source); |
||||
} |
||||
} |
||||
} |
||||
|
||||
static HashSet<ControlFlowNode> FindDominatedNodes(HashSet<ControlFlowNode> body, ControlFlowNode head) |
||||
{ |
||||
var exitNodes = head.DominanceFrontier.SelectMany(n => n.Predecessors); |
||||
HashSet<ControlFlowNode> agenda = new HashSet<ControlFlowNode>(exitNodes); |
||||
HashSet<ControlFlowNode> result = new HashSet<ControlFlowNode>(); |
||||
|
||||
while(agenda.Count > 0) { |
||||
ControlFlowNode addNode = agenda.First(); |
||||
agenda.Remove(addNode); |
||||
|
||||
if (body.Contains(addNode) && head.Dominates(addNode) && result.Add(addNode)) { |
||||
foreach (var predecessor in addNode.Predecessors) { |
||||
agenda.Add(predecessor); |
||||
} |
||||
} |
||||
} |
||||
result.Add(head); |
||||
|
||||
return result; |
||||
} |
||||
|
||||
static List<ILNode> FindConditions(HashSet<ControlFlowNode> body, ControlFlowNode entryNode) |
||||
{ |
||||
List<ILNode> result = new List<ILNode>(); |
||||
|
||||
Queue<ControlFlowNode> agenda = new Queue<ControlFlowNode>(); |
||||
agenda.Enqueue(entryNode); |
||||
while(agenda.Count > 0) { |
||||
ControlFlowNode node = agenda.Dequeue(); |
||||
|
||||
if (body.Contains(node) && node.Outgoing.Count == 2) { |
||||
ILCondition condition = new ILCondition() { |
||||
ConditionBlock = new ILBlock((ILNode)node.UserData) |
||||
}; |
||||
HashSet<ControlFlowNode> frontiers = new HashSet<ControlFlowNode>(); |
||||
frontiers.UnionWith(node.Outgoing[0].Target.DominanceFrontier); |
||||
frontiers.UnionWith(node.Outgoing[1].Target.DominanceFrontier); |
||||
if (!frontiers.Contains(node.Outgoing[0].Target)) { |
||||
HashSet<ControlFlowNode> content1 = FindDominatedNodes(body, node.Outgoing[0].Target); |
||||
body.ExceptWith(content1); |
||||
condition.Block1 = new ILBlock(FindConditions(content1, node.Outgoing[0].Target)); |
||||
} |
||||
if (!frontiers.Contains(node.Outgoing[1].Target)) { |
||||
HashSet<ControlFlowNode> content2 = FindDominatedNodes(body, node.Outgoing[1].Target); |
||||
body.ExceptWith(content2); |
||||
condition.Block2 = new ILBlock(FindConditions(content2, node.Outgoing[1].Target)); |
||||
} |
||||
result.Add(condition); |
||||
} |
||||
|
||||
// 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 body) { |
||||
result.Add((ILNode)node.UserData); |
||||
} |
||||
|
||||
return result; |
||||
} |
||||
|
||||
/* |
||||
|
||||
public enum ShortCircuitOperator |
||||
{ |
||||
LeftAndRight, |
||||
LeftOrRight, |
||||
NotLeftAndRight, |
||||
NotLeftOrRight, |
||||
} |
||||
|
||||
static bool TryOptimizeShortCircuit(Node head) |
||||
{ |
||||
if ((head is BasicBlock) && |
||||
(head as BasicBlock).BranchBasicBlock != null && |
||||
(head as BasicBlock).FallThroughBasicBlock != null) { |
||||
head.Parent.MergeChilds<SimpleBranch>(head); |
||||
return true; |
||||
} |
||||
|
||||
Branch top = head as Branch; |
||||
if (top == null) return false; |
||||
|
||||
Branch left = head.FloatUpToNeighbours(top.TrueSuccessor) as Branch; |
||||
Branch right = head.FloatUpToNeighbours(top.FalseSuccessor) as Branch; |
||||
|
||||
// A & B
|
||||
if (left != null && |
||||
left.Predecessors.Count == 1 && |
||||
left.FalseSuccessor == top.FalseSuccessor) { |
||||
ShortCircuitBranch scBranch = top.Parent.MergeChilds<ShortCircuitBranch>(top, left); |
||||
scBranch.Operator = ShortCircuitOperator.LeftAndRight; |
||||
return true; |
||||
} |
||||
|
||||
// ~A | B
|
||||
if (left != null && |
||||
left.Predecessors.Count == 1 && |
||||
left.TrueSuccessor == top.FalseSuccessor) { |
||||
ShortCircuitBranch scBranch = top.Parent.MergeChilds<ShortCircuitBranch>(top, left); |
||||
scBranch.Operator = ShortCircuitOperator.NotLeftOrRight; |
||||
return true; |
||||
} |
||||
|
||||
// A | B
|
||||
if (right != null && |
||||
right.Predecessors.Count == 1 && |
||||
right.TrueSuccessor == top.TrueSuccessor) { |
||||
ShortCircuitBranch scBranch = top.Parent.MergeChilds<ShortCircuitBranch>(top, right); |
||||
scBranch.Operator = ShortCircuitOperator.LeftOrRight; |
||||
return true; |
||||
} |
||||
|
||||
// ~A & B
|
||||
if (right != null && |
||||
right.Predecessors.Count == 1 && |
||||
right.FalseSuccessor == top.TrueSuccessor) { |
||||
ShortCircuitBranch scBranch = top.Parent.MergeChilds<ShortCircuitBranch>(top, right); |
||||
scBranch.Operator = ShortCircuitOperator.NotLeftAndRight; |
||||
return true; |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
|
||||
*/ |
||||
} |
||||
} |
Loading…
Reference in new issue