From d9cdb29c2d6e05c98412dcd59ff6dce703e2c82d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Srbeck=C3=BD?= Date: Sun, 13 Feb 2011 16:02:48 +0000 Subject: [PATCH 1/3] Refactoring control flow --- .../Ast/AstMetodBodyBuilder.cs | 166 +++------ .../FlowAnalysis/ControlFlowNode.cs | 5 + .../ICSharpCode.Decompiler.csproj | 241 +++++++------ .../ILAst/ControlFlow/Node-Optimize.cs | 188 ---------- .../ILAst/ControlFlow/Node-Structure.cs | 233 ------------- .../ILAst/ControlFlow/Node-Transforms.cs | 60 ---- .../ILAst/ControlFlow/NodeCollection.cs | 130 ------- .../ILAst/ControlFlow/Nodes.cs | 254 -------------- ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs | 4 +- .../ILAst/ILAstOptimizer.cs | 322 ++++++++++++++++++ ICSharpCode.Decompiler/ILAst/ILAstTypes.cs | 108 +++++- 11 files changed, 600 insertions(+), 1111 deletions(-) delete mode 100644 ICSharpCode.Decompiler/ILAst/ControlFlow/Node-Optimize.cs delete mode 100644 ICSharpCode.Decompiler/ILAst/ControlFlow/Node-Structure.cs delete mode 100644 ICSharpCode.Decompiler/ILAst/ControlFlow/Node-Transforms.cs delete mode 100644 ICSharpCode.Decompiler/ILAst/ControlFlow/NodeCollection.cs delete mode 100644 ICSharpCode.Decompiler/ILAst/ControlFlow/Nodes.cs create mode 100644 ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs diff --git a/ICSharpCode.Decompiler/Ast/AstMetodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMetodBodyBuilder.cs index 12cc70149..1f26b9aa1 100644 --- a/ICSharpCode.Decompiler/Ast/AstMetodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMetodBodyBuilder.cs @@ -21,17 +21,7 @@ namespace Decompiler { AstMethodBodyBuilder builder = new AstMethodBodyBuilder(); builder.methodDef = methodDef; - if (Debugger.IsAttached) { - return builder.CreateMethodBody(); - } else { - try { - return builder.CreateMethodBody(); - } catch { - BlockStatement block = new BlockStatement(); - block.AddChild(new Comment("// Exception during decompilation"), BlockStatement.Roles.Comment); - return block; - } - } + return builder.CreateMethodBody(); } static readonly Dictionary typeNameToVariableNameDict = new Dictionary { @@ -57,8 +47,8 @@ namespace Decompiler List body = new ILAstBuilder().Build(methodDef, true); - MethodBodyGraph bodyGraph = new MethodBodyGraph(body); - bodyGraph.Optimize(); + ILAstOptimizer bodyGraph = new ILAstOptimizer(); + bodyGraph.Optimize(body); List intNames = new List(new string[] {"i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t"}); Dictionary typeNames = new Dictionary(); @@ -104,62 +94,42 @@ namespace Decompiler // astBlock.Children.Add(astLocalVar); } - Ast.BlockStatement astBlock = new Ast.BlockStatement(); - astBlock.Statements = TransformNodes(bodyGraph.Childs); + Ast.BlockStatement astBlock = TransformBlock(new ILBlock(body)); CommentStatement.ReplaceAll(astBlock); // convert CommentStatements to Comments return astBlock; } - IEnumerable TransformNodes(IEnumerable nodes) + Ast.BlockStatement TransformBlock(ILBlock block) { - foreach(Node node in nodes) { - foreach(Ast.Statement stmt in TransformNode(node)) { - yield return stmt; + Ast.BlockStatement astBlock = new BlockStatement(); + if (block != null) { + foreach(ILNode node in block.Body) { + astBlock.AddStatements(TransformNode(node)); } } + return astBlock; } - IEnumerable TransformNode(Node node) + IEnumerable TransformNode(ILNode node) { - if (Options.NodeComments) { - yield return new CommentStatement(node.Description); - } - - yield return new Ast.LabelStatement { Label = node.Label }; - - if (node is BasicBlock) { - foreach(ILNode expr in ((BasicBlock)node).Body) { - if (expr is ILLabel) { - yield return new Ast.LabelStatement { Label = ((ILLabel)expr).Name }; + if (node is ILLabel) { + yield return new Ast.LabelStatement { Label = ((ILLabel)node).Name }; + } else if (node is ILExpression) { + object codeExpr = TransformExpression((ILExpression)node); + if (codeExpr != null) { + if (codeExpr is Ast.Expression) { + yield return new Ast.ExpressionStatement { Expression = (Ast.Expression)codeExpr }; + } else if (codeExpr is Ast.Statement) { + yield return (Ast.Statement)codeExpr; } else { - Statement stmt = TransformExpressionToStatement((ILExpression)expr); - if (stmt != null) { - yield return stmt; - } + throw new Exception(); } } - foreach(Statement inode in TransformNodes(node.Childs)) { - yield return inode; - } - Node fallThroughNode = ((BasicBlock)node).FallThroughBasicBlock; - // If there is default branch and it is not the following node - if (fallThroughNode != null) { - yield return new Ast.GotoStatement { GotoType = GotoType.Label, Label = fallThroughNode.Label }; - } - } else if (node is AcyclicGraph) { - foreach(Statement inode in TransformNodes(node.Childs)) { - yield return inode; - } - } else if (node is Loop) { - Ast.BlockStatement blockStatement = new Ast.BlockStatement(); - blockStatement.Statements = TransformNodes(node.Childs); + } else if (node is ILLoop) { yield return new Ast.ForStatement { - EmbeddedStatement = blockStatement + EmbeddedStatement = TransformBlock(((ILLoop)node).ContentBlock) }; - } else if (node is Block) { - foreach(Statement inode in TransformNodes(node.Childs)) { - yield return inode; - } + /* } else if (node is Branch) { yield return new Ast.LabelStatement { Label = ((Branch)node).FirstBasicBlock.Label }; @@ -176,62 +146,37 @@ namespace Decompiler }; yield return ifElseStmt; - } else if (node is ConditionalNode) { - ConditionalNode conditionalNode = (ConditionalNode)node; - yield return new Ast.LabelStatement { Label = conditionalNode.Condition.FirstBasicBlock.Label }; - - Ast.BlockStatement trueBlock = new Ast.BlockStatement(); - // The block entry code - trueBlock.AddStatement(new Ast.GotoStatement(conditionalNode.Condition.TrueSuccessor.Label)); - // Sugested content - trueBlock.AddStatements(TransformNode(conditionalNode.TrueBody)); - - Ast.BlockStatement falseBlock = new Ast.BlockStatement(); - // The block entry code - falseBlock.AddStatement(new Ast.GotoStatement(conditionalNode.Condition.FalseSuccessor.Label)); - // Sugested content - falseBlock.AddStatements(TransformNode(conditionalNode.FalseBody)); + */ + } else if (node is ILCondition) { + ILCondition conditionalNode = (ILCondition)node; + yield return TransformBlock(conditionalNode.ConditionBlock); Ast.IfElseStatement ifElseStmt = new Ast.IfElseStatement { - // Method bodies are swapped - Condition = new Ast.UnaryOperatorExpression( - UnaryOperatorType.Not, - MakeBranchCondition(conditionalNode.Condition) - ), - TrueStatement = falseBlock, - FalseStatement = trueBlock + Condition = new PrimitiveExpression(true), + TrueStatement = TransformBlock(conditionalNode.Block1), + FalseStatement = TransformBlock(conditionalNode.Block2) }; yield return ifElseStmt; - } else if (node is TryCatchNode) { - TryCatchNode tryCachNode = ((TryCatchNode)node); - Ast.BlockStatement tryBlock = new Ast.BlockStatement(); - tryBlock.Statements = TransformNode(tryCachNode.Childs[0]); - Ast.BlockStatement finallyBlock = null; - if (tryCachNode.Childs[1].Childs.Count > 0) { - finallyBlock = new Ast.BlockStatement(); - finallyBlock.Statements = TransformNode(tryCachNode.Childs[1]); - } - List ccs = new List(); - for (int i = 0; i < tryCachNode.Types.Count; i++) { - Ast.BlockStatement catchBlock = new Ast.BlockStatement(); - catchBlock.Statements = TransformNode(tryCachNode.Childs[i + 2]); - Ast.CatchClause cc = new Ast.CatchClause { - Type = AstBuilder.ConvertType(tryCachNode.Types[i]), + } else if (node is ILTryCatchBlock) { + ILTryCatchBlock tryCachNode = ((ILTryCatchBlock)node); + List catchClauses = new List(); + foreach (var catchClause in tryCachNode.CatchBlocks) { + catchClauses.Add(new Ast.CatchClause { + Type = AstBuilder.ConvertType(catchClause.ExceptionType), VariableName = "exception", - Body = catchBlock - }; - ccs.Add(cc); + Body = TransformBlock(catchClause) + }); } yield return new Ast.TryCatchStatement { - TryBlock = tryBlock, CatchClauses = ccs, FinallyBlock = finallyBlock + TryBlock = TransformBlock(tryCachNode.TryBlock), + CatchClauses = catchClauses, + FinallyBlock = TransformBlock(tryCachNode.FinallyBlock) }; + } else if (node is ILBlock) { + yield return TransformBlock((ILBlock)node); } else { - throw new Exception("Bad node type"); - } - - if (Options.NodeComments) { - yield return new CommentStatement(""); + throw new Exception("Unknown node type"); } } @@ -251,19 +196,7 @@ namespace Decompiler return TransformByteCode(methodDef, expr, args); } - Ast.Statement TransformExpressionToStatement(ILExpression expr) - { - object codeExpr = TransformExpression(expr); - if (codeExpr == null) { - return null; - } else if (codeExpr is Ast.Expression) { - return new Ast.ExpressionStatement { Expression = (Ast.Expression)codeExpr }; - } else if (codeExpr is Ast.Statement) { - return (Ast.Statement)codeExpr; - } else { - throw new Exception(); - } - } + /* Ast.Expression MakeBranchCondition(Branch branch) { @@ -327,6 +260,8 @@ namespace Decompiler } } + */ + static object TransformByteCode(MethodDefinition methodDef, ILExpression byteCode, List args) { try { @@ -379,9 +314,10 @@ namespace Decompiler Ast.Expression arg2 = args.Count >= 2 ? args[1] : null; Ast.Expression arg3 = args.Count >= 3 ? args[2] : null; - Ast.Statement branchCommand = null; + BlockStatement branchCommand = null; if (byteCode.Operand is ILLabel) { - branchCommand = new Ast.GotoStatement(((ILLabel)byteCode.Operand).Name); + branchCommand = new BlockStatement(); + branchCommand.AddStatement(new Ast.GotoStatement(((ILLabel)byteCode.Operand).Name)); } switch(opCode.Code) { diff --git a/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowNode.cs b/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowNode.cs index 890b62898..606b43286 100644 --- a/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowNode.cs +++ b/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowNode.cs @@ -158,6 +158,11 @@ namespace ICSharpCode.Decompiler.FlowAnalysis /// public readonly List Outgoing = new List(); + /// + /// Any user data + /// + public object UserData; + internal ControlFlowNode(int blockIndex, int offset, ControlFlowNodeType nodeType) { this.BlockIndex = blockIndex; diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 2ace1aa02..2c5ead47b 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -1,124 +1,119 @@ - - - - {984CC812-9470-4A13-AFF9-CC44068D666C} - Debug - AnyCPU - Library - ICSharpCode.Decompiler - ICSharpCode.Decompiler - v4.0 - Client - Properties - False - False - 4 - false - - - AnyCPU - False - Auto - 4194304 - 4096 - - - bin\Debug\ - true - Full - False - True - DEBUG;TRACE - - - bin\Release\ - False - None - True - False - TRACE - - - - - 3.5 - - - - 3.5 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {D68133BD-1E63-496E-9EDE-4FBDBF77B486} - Mono.Cecil - - - {3B2A5653-EC97-4001-BB9B-D90F1AF2C371} - ICSharpCode.NRefactory - - - - - - - - - - - - - - - - + + + + {984CC812-9470-4A13-AFF9-CC44068D666C} + Debug + AnyCPU + Library + ICSharpCode.Decompiler + ICSharpCode.Decompiler + v4.0 + Client + Properties + False + False + 4 + false + + + AnyCPU + False + Auto + 4194304 + 4096 + + + bin\Debug\ + true + Full + False + True + DEBUG;TRACE + + + bin\Release\ + False + None + True + False + TRACE + + + + + 3.5 + + + + 3.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {D68133BD-1E63-496E-9EDE-4FBDBF77B486} + Mono.Cecil + + + {3B2A5653-EC97-4001-BB9B-D90F1AF2C371} + ICSharpCode.NRefactory + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ICSharpCode.Decompiler/ILAst/ControlFlow/Node-Optimize.cs b/ICSharpCode.Decompiler/ILAst/ControlFlow/Node-Optimize.cs deleted file mode 100644 index c75d430b6..000000000 --- a/ICSharpCode.Decompiler/ILAst/ControlFlow/Node-Optimize.cs +++ /dev/null @@ -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(predecessor, child); - } else { - mergedNode = MergeChilds(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(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(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(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(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(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(); - } - } -} diff --git a/ICSharpCode.Decompiler/ILAst/ControlFlow/Node-Structure.cs b/ICSharpCode.Decompiler/ILAst/ControlFlow/Node-Structure.cs deleted file mode 100644 index e76bf5333..000000000 --- a/ICSharpCode.Decompiler/ILAst/ControlFlow/Node-Structure.cs +++ /dev/null @@ -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; - } - } - - /// All basic blocks within the scope of this node (inclusive) - 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 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 basicBlockPredecessors = new List(); - 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 basicBlockSuccessors = new List(); - 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(); - } - } -} diff --git a/ICSharpCode.Decompiler/ILAst/ControlFlow/Node-Transforms.cs b/ICSharpCode.Decompiler/ILAst/ControlFlow/Node-Transforms.cs deleted file mode 100644 index 02b267935..000000000 --- a/ICSharpCode.Decompiler/ILAst/ControlFlow/Node-Transforms.cs +++ /dev/null @@ -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(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; - } - } - } - } -} diff --git a/ICSharpCode.Decompiler/ILAst/ControlFlow/NodeCollection.cs b/ICSharpCode.Decompiler/ILAst/ControlFlow/NodeCollection.cs deleted file mode 100644 index 5ccc1592c..000000000 --- a/ICSharpCode.Decompiler/ILAst/ControlFlow/NodeCollection.cs +++ /dev/null @@ -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 - { - public static NodeCollection Empty = new NodeCollection(); - - public event EventHandler Added; - public event EventHandler 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 items) - { - foreach(Node item in items) { - this.Add(item); - } - } - - public void RemoveRange(IEnumerable 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 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); - } - } -} diff --git a/ICSharpCode.Decompiler/ILAst/ControlFlow/Nodes.cs b/ICSharpCode.Decompiler/ILAst/ControlFlow/Nodes.cs deleted file mode 100644 index 5478ad583..000000000 --- a/ICSharpCode.Decompiler/ILAst/ControlFlow/Nodes.cs +++ /dev/null @@ -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 body = new List(); - List basicBlockPredecessors = new List(); - BasicBlock fallThroughBasicBlock; - BasicBlock branchBasicBlock; - - public List Body { - get { return body; } - } - - public List BasicBlockPredecessors { - get { return basicBlockPredecessors; } - } - - public BasicBlock FallThroughBasicBlock { - get { return fallThroughBasicBlock; } - set { fallThroughBasicBlock = value; } - } - - public BasicBlock BranchBasicBlock { - get { return branchBasicBlock; } - set { branchBasicBlock = value; } - } - - public IEnumerable 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 labelToBasicBlock = new Dictionary(); - - public MethodBodyGraph(List 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 SplitToBasicBlocks(List ast) - { - if (ast.Count == 0) return new List(); - - List nodes = new List(); - - 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 Types = new List(); - } - - 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); - } - } -} diff --git a/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs b/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs index 9c706e49e..9c9b768f8 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs @@ -312,7 +312,7 @@ namespace Decompiler List nestedEHs = ehs.Where(eh => (tryStart <= eh.TryStart.Offset && eh.TryEnd.Offset < tryEnd) || (tryStart < eh.TryStart.Offset && eh.TryEnd.Offset <= tryEnd)).ToList(); int tryEndIdx; for (tryEndIdx = 0; tryEndIdx < body.Count && body[tryEndIdx].Offset != tryEnd; tryEndIdx++); - tryCatchBlock.TryBlock = ConvertToAst(body.CutRange(0, tryEndIdx), nestedEHs); + tryCatchBlock.TryBlock = new ILBlock(ConvertToAst(body.CutRange(0, tryEndIdx), nestedEHs)); } // Cut all handlers @@ -331,7 +331,7 @@ namespace Decompiler Body = handlerAst }); } else if (eh.HandlerType == ExceptionHandlerType.Finally) { - tryCatchBlock.FinallyBlock = handlerAst; + tryCatchBlock.FinallyBlock = new ILBlock(handlerAst); } else { // TODO } diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs new file mode 100644 index 000000000..dcc8a0a37 --- /dev/null +++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -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 ast) + { + ILLabel entryLabel; + List tryCatchBlocks = ast.OfType().ToList(); + + ControlFlowGraph graph; + + ast = SplitToMovableBlocks(ast, out entryLabel); + + graph = BuildGraph(ast, entryLabel); + graph.ComputeDominance(); + graph.ComputeDominanceFrontier(); + ast = FindLoops(new HashSet(graph.Nodes.Skip(3)), graph.EntryPoint); + + graph = BuildGraph(ast, entryLabel); + graph.ComputeDominance(); + graph.ComputeDominanceFrontier(); + ast = FindConditions(new HashSet(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; + + /// + /// 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. + /// + List SplitToMovableBlocks(List ast, out ILLabel entryLabel) + { + List blocks = new List(); + + 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 nodes, ILLabel entryLabel) + { + int index = 0; + List cfNodes = new List(); + 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 labelToCfNode = new Dictionary(); + Dictionary astNodeToCfNode = new Dictionary(); + 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()) { + 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()) { + IEnumerable 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 FindLoops(HashSet body, ControlFlowNode entryPoint) + { + List result = new List(); + + Queue agenda = new Queue(); + agenda.Enqueue(entryPoint); + while(agenda.Count > 0) { + ControlFlowNode node = agenda.Dequeue(); + + if (body.Contains(node) + && node.DominanceFrontier.Contains(node) + && node != entryPoint) + { + HashSet loopContents = new HashSet(); + 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 body, HashSet 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 FindDominatedNodes(HashSet body, ControlFlowNode head) + { + var exitNodes = head.DominanceFrontier.SelectMany(n => n.Predecessors); + HashSet agenda = new HashSet(exitNodes); + HashSet result = new HashSet(); + + 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 FindConditions(HashSet body, ControlFlowNode entryNode) + { + List result = new List(); + + Queue agenda = new Queue(); + 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 frontiers = new HashSet(); + frontiers.UnionWith(node.Outgoing[0].Target.DominanceFrontier); + frontiers.UnionWith(node.Outgoing[1].Target.DominanceFrontier); + if (!frontiers.Contains(node.Outgoing[0].Target)) { + HashSet 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 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(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(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(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(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(top, right); + scBranch.Operator = ShortCircuitOperator.NotLeftAndRight; + return true; + } + + return false; + } + + */ + } +} diff --git a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs index c30c53499..af8be061f 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs @@ -9,8 +9,36 @@ using Cecil = Mono.Cecil; namespace Decompiler { - public class ILNode + public abstract class ILNode { + public IEnumerable GetChildrenRecursive() where T: ILNode + { + Stack> stack = new Stack>(); + try { + stack.Push(GetChildren().GetEnumerator()); + while (stack.Count > 0) { + while (stack.Peek().MoveNext()) { + ILNode element = stack.Peek().Current; + if (element is T) + yield return (T)element; + IEnumerable children = element.GetChildren(); + if (children != null) { + stack.Push(children.GetEnumerator()); + } + } + stack.Pop().Dispose(); + } + } finally { + while (stack.Count > 0) { + stack.Pop().Dispose(); + } + } + } + + public virtual IEnumerable GetChildren() + { + return null; + } } public class ILLabel: ILNode @@ -23,17 +51,45 @@ namespace Decompiler } } + public class ILBlock: ILNode + { + public List Body; + + public ILBlock(params ILNode[] body) + { + this.Body = new List(body); + } + + public ILBlock(List body) + { + this.Body = body; + } + + public override IEnumerable GetChildren() + { + return this.Body; + } + } + public class ILTryCatchBlock: ILNode { - public class CatchBlock + public class CatchBlock: ILBlock { public TypeReference ExceptionType; - public List Body; } - public List TryBlock; + public ILBlock TryBlock; public List CatchBlocks; - public List FinallyBlock; + public ILBlock FinallyBlock; + + public override IEnumerable GetChildren() + { + yield return this.TryBlock; + foreach (var catchBlock in this.CatchBlocks) { + yield return catchBlock; + } + yield return this.FinallyBlock; + } public override string ToString() { @@ -53,7 +109,7 @@ namespace Decompiler } public class ILExpression: ILNode - { + { public OpCode OpCode { get; set; } public object Operand { get; set; } public List Arguments { get; set; } @@ -65,6 +121,22 @@ namespace Decompiler this.Arguments = new List(args); } + public IEnumerable GetBranchTargets() + { + if (this.Operand is ILLabel) { + return new ILLabel[] { (ILLabel)this.Operand }; + } else if (this.Operand is ILLabel[]) { + return (ILLabel[])this.Operand; + } else { + return null; + } + } + + public override IEnumerable GetChildren() + { + return Arguments; + } + public override string ToString() { StringBuilder sb = new StringBuilder(); @@ -84,4 +156,28 @@ namespace Decompiler return sb.ToString(); } } + + public class ILLoop: ILNode + { + public ILBlock ContentBlock; + + public override IEnumerable GetChildren() + { + yield return ContentBlock; + } + } + + public class ILCondition: ILNode + { + public ILBlock ConditionBlock; + public ILBlock Block1; + public ILBlock Block2; + + public override IEnumerable GetChildren() + { + yield return ConditionBlock; + yield return Block1; + yield return Block2; + } + } } From 4f0c70fce3775a966596dae6a53c61bffe30d345 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Srbeck=C3=BD?= Date: Mon, 14 Feb 2011 00:50:25 +0000 Subject: [PATCH 2/3] Flatten temporary blocks; Simple goto removal; Remove dead labels --- ICSharpCode.Decompiler/Ast/AstBuilder.cs | 2 +- .../Ast/AstMetodBodyBuilder.cs | 5 +- .../Ast/Transforms/RemoveDeadLabels.cs | 56 ------- .../ICSharpCode.Decompiler.csproj | 1 - .../ILAst/ILAstOptimizer.cs | 150 ++++++++++++++---- ICSharpCode.Decompiler/ILAst/ILAstTypes.cs | 41 ++--- 6 files changed, 148 insertions(+), 107 deletions(-) delete mode 100644 ICSharpCode.Decompiler/Ast/Transforms/RemoveDeadLabels.cs diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs index 3b2b71a7c..8310340f6 100644 --- a/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -20,7 +20,7 @@ namespace Decompiler for (int i = 0; i < 4; i++) { if (Options.ReduceAstJumps) { //astCompileUnit.AcceptVisitor(new Transforms.Ast.RemoveGotos(), null); - astCompileUnit.AcceptVisitor(new Transforms.Ast.RemoveDeadLabels(), null); + //astCompileUnit.AcceptVisitor(new Transforms.Ast.RemoveDeadLabels(), null); } if (Options.ReduceAstLoops) { //astCompileUnit.AcceptVisitor(new Transforms.Ast.RestoreLoop(), null); diff --git a/ICSharpCode.Decompiler/Ast/AstMetodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMetodBodyBuilder.cs index 1f26b9aa1..eff57689b 100644 --- a/ICSharpCode.Decompiler/Ast/AstMetodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMetodBodyBuilder.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using Ast = ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.CSharp; using Cecil = Mono.Cecil; @@ -48,7 +49,7 @@ namespace Decompiler List body = new ILAstBuilder().Build(methodDef, true); ILAstOptimizer bodyGraph = new ILAstOptimizer(); - bodyGraph.Optimize(body); + bodyGraph.Optimize(ref body); List intNames = new List(new string[] {"i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t"}); Dictionary typeNames = new Dictionary(); @@ -385,7 +386,7 @@ namespace Decompiler throw new NotImplementedException(); #endregion #region Branching - case Code.Br: return branchCommand; + case Code.Br: return new Ast.GotoStatement(((ILLabel)byteCode.Operand).Name); case Code.Brfalse: return new Ast.IfElseStatement(new Ast.UnaryOperatorExpression(UnaryOperatorType.Not, arg1), branchCommand); case Code.Brtrue: return new Ast.IfElseStatement(arg1, branchCommand); case Code.Beq: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, arg2), branchCommand); diff --git a/ICSharpCode.Decompiler/Ast/Transforms/RemoveDeadLabels.cs b/ICSharpCode.Decompiler/Ast/Transforms/RemoveDeadLabels.cs deleted file mode 100644 index b8f65771a..000000000 --- a/ICSharpCode.Decompiler/Ast/Transforms/RemoveDeadLabels.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System; -using System.Collections.Generic; -using ICSharpCode.NRefactory.CSharp; - -namespace Decompiler.Transforms.Ast -{ - public class RemoveDeadLabels : DepthFirstAstVisitor - { - List usedLabels = new List(); - bool collectingUsedLabels; - - public override object VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration, object data) - { - collectingUsedLabels = true; - base.VisitConstructorDeclaration(constructorDeclaration, data); - collectingUsedLabels = false; - base.VisitConstructorDeclaration(constructorDeclaration, data); - return null; - } - - public override object VisitMethodDeclaration(MethodDeclaration methodDeclaration, object data) - { - collectingUsedLabels = true; - base.VisitMethodDeclaration(methodDeclaration, data); - collectingUsedLabels = false; - base.VisitMethodDeclaration(methodDeclaration, data); - return null; - } - - public override object VisitAccessor(Accessor accessor, object data) - { - collectingUsedLabels = true; - base.VisitAccessor(accessor, data); - collectingUsedLabels = false; - return base.VisitAccessor(accessor, data); - } - - public override object VisitGotoStatement(GotoStatement gotoStatement, object data) - { - if (collectingUsedLabels) { - usedLabels.Add(gotoStatement.Label); - } - return null; - } - - public override object VisitLabelStatement(LabelStatement labelStatement, object data) - { - if (!collectingUsedLabels) { - if (!usedLabels.Contains(labelStatement.Label)) { - labelStatement.Remove(); - } - } - return null; - } - } -} diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 2c5ead47b..eaa5373ac 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -56,7 +56,6 @@ - diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs index dcc8a0a37..485b6b528 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -10,7 +10,21 @@ namespace Decompiler.ControlFlow { public class ILAstOptimizer { - public void Optimize(List ast) + public void Optimize(ref List ast) + { + OptimizeRecursive(ref ast); + + // Provide a container for the algorithms below + ILBlock astBlock = new ILBlock(ast); + + FlattenNestedMovableBlocks(astBlock); + SimpleGotoRemoval(astBlock); + RemoveDeadLabels(astBlock); + + ast = astBlock.Body; + } + + void OptimizeRecursive(ref List ast) { ILLabel entryLabel; List tryCatchBlocks = ast.OfType().ToList(); @@ -31,15 +45,26 @@ namespace Decompiler.ControlFlow // Recursively optimze try-cath blocks foreach(ILTryCatchBlock tryCatchBlock in tryCatchBlocks) { - Optimize(tryCatchBlock.TryBlock.Body); + Optimize(ref tryCatchBlock.TryBlock.Body); foreach(ILTryCatchBlock.CatchBlock catchBlock in tryCatchBlock.CatchBlocks) { - Optimize(catchBlock.Body); + Optimize(ref catchBlock.Body); } - Optimize(tryCatchBlock.FinallyBlock.Body); + Optimize(ref tryCatchBlock.FinallyBlock.Body); } + + // Sort the nodes in the original order + ast = ast.OrderBy(n => n.GetSelfAndChildrenRecursive().First().OriginalOrder).ToList(); + + ast.Insert(0, new ILExpression(OpCodes.Br, entryLabel)); + } + + + class ILMoveAbleBlock: ILBlock + { + public int OriginalOrder; } - int nextLabelIndex = 0; + int nextBlockIndex = 0; /// /// Group input into a set of blocks that can be later arbitraliby schufled. @@ -50,9 +75,9 @@ namespace Decompiler.ControlFlow { List blocks = new List(); - ILBlock block = new ILBlock(); + ILMoveAbleBlock block = new ILMoveAbleBlock() { OriginalOrder = (nextBlockIndex++) }; blocks.Add(block); - entryLabel = new ILLabel() { Name = "Block_" + (nextLabelIndex++) }; + entryLabel = new ILLabel() { Name = "Block_" + block.OriginalOrder }; block.Body.Add(entryLabel); if (ast.Count == 0) @@ -71,14 +96,14 @@ namespace Decompiler.ControlFlow (currNode is ILExpression) && ((ILExpression)currNode).OpCode.IsBranch()) { ILBlock lastBlock = block; - block = new ILBlock(); + block = new ILMoveAbleBlock() { OriginalOrder = (nextBlockIndex++) }; 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)); + ILLabel blockLabel = new ILLabel() { Name = "Block_" + block.OriginalOrder }; + lastBlock.Body.Add(new ILExpression(OpCodes.Br, blockLabel)); block.Body.Add(blockLabel); } } @@ -110,7 +135,7 @@ namespace Decompiler.ControlFlow cfNode.UserData = node; // Find all contained labels - foreach(ILLabel label in node.GetChildrenRecursive()) { + foreach(ILLabel label in node.GetSelfAndChildrenRecursive()) { labelToCfNode[label] = cfNode; } } @@ -126,7 +151,7 @@ namespace Decompiler.ControlFlow ControlFlowNode source = astNodeToCfNode[node]; // Find all branches - foreach(ILExpression child in node.GetChildrenRecursive()) { + foreach(ILExpression child in node.GetSelfAndChildrenRecursive()) { IEnumerable targets = child.GetBranchTargets(); if (targets != null) { foreach(ILLabel target in targets) { @@ -145,7 +170,7 @@ namespace Decompiler.ControlFlow return new ControlFlowGraph(cfNodes.ToArray()); } - static List FindLoops(HashSet body, ControlFlowNode entryPoint) + static List FindLoops(HashSet nodes, ControlFlowNode entryPoint) { List result = new List(); @@ -154,15 +179,15 @@ namespace Decompiler.ControlFlow while(agenda.Count > 0) { ControlFlowNode node = agenda.Dequeue(); - if (body.Contains(node) + if (nodes.Contains(node) && node.DominanceFrontier.Contains(node) && node != entryPoint) { HashSet loopContents = new HashSet(); - FindLoopContents(body, loopContents, node, node); + FindLoopContents(nodes, loopContents, node, node); // Move the content into loop block - body.ExceptWith(loopContents); + nodes.ExceptWith(loopContents); result.Add(new ILLoop() { ContentBlock = new ILBlock(FindLoops(loopContents, node)) }); } @@ -173,23 +198,23 @@ namespace Decompiler.ControlFlow } // Add whatever is left - foreach(var node in body) { + foreach(var node in nodes) { result.Add((ILNode)node.UserData); } return result; } - static void FindLoopContents(HashSet body, HashSet loopContents, ControlFlowNode loopHead, ControlFlowNode addNode) + static void FindLoopContents(HashSet nodes, HashSet loopContents, ControlFlowNode loopHead, ControlFlowNode addNode) { - if (body.Contains(addNode) && loopHead.Dominates(addNode) && loopContents.Add(addNode)) { + if (nodes.Contains(addNode) && loopHead.Dominates(addNode) && loopContents.Add(addNode)) { foreach (var edge in addNode.Incoming) { - FindLoopContents(body, loopContents, loopHead, edge.Source); + FindLoopContents(nodes, loopContents, loopHead, edge.Source); } } } - static HashSet FindDominatedNodes(HashSet body, ControlFlowNode head) + static HashSet FindDominatedNodes(HashSet nodes, ControlFlowNode head) { var exitNodes = head.DominanceFrontier.SelectMany(n => n.Predecessors); HashSet agenda = new HashSet(exitNodes); @@ -199,7 +224,7 @@ namespace Decompiler.ControlFlow ControlFlowNode addNode = agenda.First(); agenda.Remove(addNode); - if (body.Contains(addNode) && head.Dominates(addNode) && result.Add(addNode)) { + if (nodes.Contains(addNode) && head.Dominates(addNode) && result.Add(addNode)) { foreach (var predecessor in addNode.Predecessors) { agenda.Add(predecessor); } @@ -210,7 +235,7 @@ namespace Decompiler.ControlFlow return result; } - static List FindConditions(HashSet body, ControlFlowNode entryNode) + static List FindConditions(HashSet nodes, ControlFlowNode entryNode) { List result = new List(); @@ -219,7 +244,7 @@ namespace Decompiler.ControlFlow while(agenda.Count > 0) { ControlFlowNode node = agenda.Dequeue(); - if (body.Contains(node) && node.Outgoing.Count == 2) { + if (nodes.Contains(node) && node.Outgoing.Count == 2) { ILCondition condition = new ILCondition() { ConditionBlock = new ILBlock((ILNode)node.UserData) }; @@ -227,15 +252,16 @@ namespace Decompiler.ControlFlow frontiers.UnionWith(node.Outgoing[0].Target.DominanceFrontier); frontiers.UnionWith(node.Outgoing[1].Target.DominanceFrontier); if (!frontiers.Contains(node.Outgoing[0].Target)) { - HashSet content1 = FindDominatedNodes(body, node.Outgoing[0].Target); - body.ExceptWith(content1); + HashSet content1 = FindDominatedNodes(nodes, node.Outgoing[0].Target); + nodes.ExceptWith(content1); condition.Block1 = new ILBlock(FindConditions(content1, node.Outgoing[0].Target)); } if (!frontiers.Contains(node.Outgoing[1].Target)) { - HashSet content2 = FindDominatedNodes(body, node.Outgoing[1].Target); - body.ExceptWith(content2); + HashSet content2 = FindDominatedNodes(nodes, node.Outgoing[1].Target); + nodes.ExceptWith(content2); condition.Block2 = new ILBlock(FindConditions(content2, node.Outgoing[1].Target)); } + nodes.Remove(node); result.Add(condition); } @@ -246,7 +272,7 @@ namespace Decompiler.ControlFlow } // Add whatever is left - foreach(var node in body) { + foreach(var node in nodes) { result.Add((ILNode)node.UserData); } @@ -318,5 +344,71 @@ namespace Decompiler.ControlFlow } */ + + /// + /// Flattens all nested movable blocks, except the the top level 'node' argument + /// + void FlattenNestedMovableBlocks(ILNode node) + { + ILBlock block = node as ILBlock; + if (block != null) { + List flatBody = new List(); + foreach (ILNode child in block.Body) { + FlattenNestedMovableBlocks(child); + if (child is ILMoveAbleBlock) { + flatBody.AddRange(((ILMoveAbleBlock)child).Body); + } else { + flatBody.Add(child); + } + } + block.Body = flatBody; + } else if (node is ILExpression) { + // Optimization - no need to check expressions + } else if (node != null) { + // Recursively find all ILBlocks + foreach(ILNode child in node.GetChildren()) { + FlattenNestedMovableBlocks(child); + } + } + } + + void SimpleGotoRemoval(ILBlock ast) + { + var blocks = ast.GetSelfAndChildrenRecursive().ToList(); + foreach(ILBlock block in blocks) { + for (int i = 0; i < block.Body.Count; i++) { + ILExpression expr = block.Body[i] as ILExpression; + // Uncoditional branch + if (expr != null && (expr.OpCode == OpCodes.Br || expr.OpCode == OpCodes.Br_S)) { + // Check that branch is followed by its label (allow multiple labels) + for (int j = i + 1; j < block.Body.Count; j++) { + ILLabel label = block.Body[j] as ILLabel; + if (label == null) + break; // Can not optimize + if (expr.Operand == label) { + block.Body.RemoveAt(i); + break; // Branch removed + } + } + } + } + } + } + + void RemoveDeadLabels(ILBlock ast) + { + HashSet liveLabels = new HashSet(ast.GetSelfAndChildrenRecursive().SelectMany(e => e.GetBranchTargets())); + var blocks = ast.GetSelfAndChildrenRecursive().ToList(); + foreach(ILBlock block in blocks) { + for (int i = 0; i < block.Body.Count;) { + ILLabel label = block.Body[i] as ILLabel; + if (label != null && !liveLabels.Contains(label)) { + block.Body.RemoveAt(i); + } else { + i++; + } + } + } + } } } diff --git a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs index af8be061f..db96898e5 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs @@ -11,19 +11,24 @@ namespace Decompiler { public abstract class ILNode { - public IEnumerable GetChildrenRecursive() where T: ILNode + public IEnumerable GetSelfAndChildrenRecursive() where T: ILNode { + if (this is T) + yield return (T)this; + Stack> stack = new Stack>(); try { stack.Push(GetChildren().GetEnumerator()); while (stack.Count > 0) { while (stack.Peek().MoveNext()) { ILNode element = stack.Peek().Current; - if (element is T) - yield return (T)element; - IEnumerable children = element.GetChildren(); - if (children != null) { - stack.Push(children.GetEnumerator()); + if (element != null) { + if (element is T) + yield return (T)element; + IEnumerable children = element.GetChildren(); + if (children != null) { + stack.Push(children.GetEnumerator()); + } } } stack.Pop().Dispose(); @@ -37,17 +42,7 @@ namespace Decompiler public virtual IEnumerable GetChildren() { - return null; - } - } - - public class ILLabel: ILNode - { - public string Name; - - public override string ToString() - { - return Name + ":"; + yield break; } } @@ -71,6 +66,16 @@ namespace Decompiler } } + public class ILLabel: ILNode + { + public string Name; + + public override string ToString() + { + return Name + ":"; + } + } + public class ILTryCatchBlock: ILNode { public class CatchBlock: ILBlock @@ -128,7 +133,7 @@ namespace Decompiler } else if (this.Operand is ILLabel[]) { return (ILLabel[])this.Operand; } else { - return null; + return new ILLabel[] { }; } } From 9fb7d244ed8b5d06c13850d4f7e041bfe6a9b497 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Srbeck=C3=BD?= Date: Mon, 14 Feb 2011 02:07:05 +0000 Subject: [PATCH 3/3] Find conditions --- .../Ast/AstMetodBodyBuilder.cs | 64 ++++---- .../ILAst/ILAstOptimizer.cs | 140 ++++++++++++------ ICSharpCode.Decompiler/ILAst/ILAstTypes.cs | 14 +- 3 files changed, 125 insertions(+), 93 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/AstMetodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMetodBodyBuilder.cs index eff57689b..61d448e5b 100644 --- a/ICSharpCode.Decompiler/Ast/AstMetodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMetodBodyBuilder.cs @@ -150,15 +150,12 @@ namespace Decompiler */ } else if (node is ILCondition) { ILCondition conditionalNode = (ILCondition)node; - yield return TransformBlock(conditionalNode.ConditionBlock); - - Ast.IfElseStatement ifElseStmt = new Ast.IfElseStatement { - Condition = new PrimitiveExpression(true), - TrueStatement = TransformBlock(conditionalNode.Block1), - FalseStatement = TransformBlock(conditionalNode.Block2) + // Swap bodies + yield return new Ast.IfElseStatement { + Condition = new UnaryOperatorExpression(UnaryOperatorType.Not, MakeBranchCondition(conditionalNode.Condition)), + TrueStatement = TransformBlock(conditionalNode.FalseBlock), + FalseStatement = TransformBlock(conditionalNode.TrueBlock) }; - - yield return ifElseStmt; } else if (node is ILTryCatchBlock) { ILTryCatchBlock tryCachNode = ((ILTryCatchBlock)node); List catchClauses = new List(); @@ -197,35 +194,27 @@ namespace Decompiler return TransformByteCode(methodDef, expr, args); } - /* - - Ast.Expression MakeBranchCondition(Branch branch) - { - return MakeBranchCondition_Internal(branch); - } - - Ast.Expression MakeBranchCondition_Internal(Branch branch) + Ast.Expression MakeBranchCondition(ILExpression expr) { - if (branch is SimpleBranch) { - List args = TransformExpressionArguments((ILExpression)((SimpleBranch)branch).BasicBlock.Body[0]); - Ast.Expression arg1 = args.Count >= 1 ? args[0] : null; - Ast.Expression arg2 = args.Count >= 2 ? args[1] : null; - switch(((ILExpression)((SimpleBranch)branch).BasicBlock.Body[0]).OpCode.Code) { - case Code.Brfalse: return new Ast.UnaryOperatorExpression(UnaryOperatorType.Not, arg1); - case Code.Brtrue: return arg1; - case Code.Beq: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, arg2); - case Code.Bge: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThanOrEqual, arg2); - case Code.Bge_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThanOrEqual, arg2); - case Code.Bgt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); - case Code.Bgt_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); - case Code.Ble: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThanOrEqual, arg2); - case Code.Ble_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThanOrEqual, arg2); - case Code.Blt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); - case Code.Blt_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); - case Code.Bne_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.InEquality, arg2); - case Code.Leave: return new Ast.PrimitiveExpression(true); - default: throw new Exception("Bad opcode"); - } + List args = TransformExpressionArguments(expr); + Ast.Expression arg1 = args.Count >= 1 ? args[0] : null; + Ast.Expression arg2 = args.Count >= 2 ? args[1] : null; + switch(expr.OpCode.Code) { + case Code.Brfalse: return new Ast.UnaryOperatorExpression(UnaryOperatorType.Not, arg1); + case Code.Brtrue: return arg1; + case Code.Beq: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, arg2); + case Code.Bge: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThanOrEqual, arg2); + case Code.Bge_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThanOrEqual, arg2); + case Code.Bgt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); + case Code.Bgt_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); + case Code.Ble: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThanOrEqual, arg2); + case Code.Ble_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThanOrEqual, arg2); + case Code.Blt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); + case Code.Blt_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); + case Code.Bne_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.InEquality, arg2); + default: throw new Exception("Bad opcode"); + } + /* } else if (branch is ShortCircuitBranch) { ShortCircuitBranch scBranch = (ShortCircuitBranch)branch; switch(scBranch.Operator) { @@ -259,10 +248,9 @@ namespace Decompiler } else { throw new Exception("Bad type"); } + */ } - */ - static object TransformByteCode(MethodDefinition methodDef, ILExpression byteCode, List args) { try { diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs index 485b6b528..0ad42e54b 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -10,6 +10,8 @@ namespace Decompiler.ControlFlow { public class ILAstOptimizer { + Dictionary labelToCfNode = new Dictionary(); + public void Optimize(ref List ast) { OptimizeRecursive(ref ast); @@ -17,6 +19,7 @@ namespace Decompiler.ControlFlow // Provide a container for the algorithms below ILBlock astBlock = new ILBlock(ast); + OrderNodes(astBlock); FlattenNestedMovableBlocks(astBlock); SimpleGotoRemoval(astBlock); RemoveDeadLabels(astBlock); @@ -52,14 +55,11 @@ namespace Decompiler.ControlFlow Optimize(ref tryCatchBlock.FinallyBlock.Body); } - // Sort the nodes in the original order - ast = ast.OrderBy(n => n.GetSelfAndChildrenRecursive().First().OriginalOrder).ToList(); - ast.Insert(0, new ILExpression(OpCodes.Br, entryLabel)); } - class ILMoveAbleBlock: ILBlock + class ILMoveableBlock: ILBlock { public int OriginalOrder; } @@ -75,7 +75,7 @@ namespace Decompiler.ControlFlow { List blocks = new List(); - ILMoveAbleBlock block = new ILMoveAbleBlock() { OriginalOrder = (nextBlockIndex++) }; + ILMoveableBlock block = new ILMoveableBlock() { OriginalOrder = (nextBlockIndex++) }; blocks.Add(block); entryLabel = new ILLabel() { Name = "Block_" + block.OriginalOrder }; block.Body.Add(entryLabel); @@ -96,7 +96,7 @@ namespace Decompiler.ControlFlow (currNode is ILExpression) && ((ILExpression)currNode).OpCode.IsBranch()) { ILBlock lastBlock = block; - block = new ILMoveAbleBlock() { OriginalOrder = (nextBlockIndex++) }; + block = new ILMoveableBlock() { OriginalOrder = (nextBlockIndex++) }; blocks.Add(block); // Explicit branch from one block to other @@ -126,7 +126,7 @@ namespace Decompiler.ControlFlow cfNodes.Add(exceptionalExit); // Create graph nodes - Dictionary labelToCfNode = new Dictionary(); + labelToCfNode = new Dictionary(); Dictionary astNodeToCfNode = new Dictionary(); foreach(ILNode node in nodes) { ControlFlowNode cfNode = new ControlFlowNode(index++, -1, ControlFlowNodeType.Normal); @@ -214,28 +214,7 @@ namespace Decompiler.ControlFlow } } - static HashSet FindDominatedNodes(HashSet nodes, ControlFlowNode head) - { - var exitNodes = head.DominanceFrontier.SelectMany(n => n.Predecessors); - HashSet agenda = new HashSet(exitNodes); - HashSet result = new HashSet(); - - while(agenda.Count > 0) { - ControlFlowNode addNode = agenda.First(); - agenda.Remove(addNode); - - if (nodes.Contains(addNode) && head.Dominates(addNode) && result.Add(addNode)) { - foreach (var predecessor in addNode.Predecessors) { - agenda.Add(predecessor); - } - } - } - result.Add(head); - - return result; - } - - static List FindConditions(HashSet nodes, ControlFlowNode entryNode) + List FindConditions(HashSet nodes, ControlFlowNode entryNode) { List result = new List(); @@ -244,25 +223,58 @@ namespace Decompiler.ControlFlow while(agenda.Count > 0) { ControlFlowNode node = agenda.Dequeue(); - if (nodes.Contains(node) && node.Outgoing.Count == 2) { - ILCondition condition = new ILCondition() { - ConditionBlock = new ILBlock((ILNode)node.UserData) - }; - HashSet frontiers = new HashSet(); - frontiers.UnionWith(node.Outgoing[0].Target.DominanceFrontier); - frontiers.UnionWith(node.Outgoing[1].Target.DominanceFrontier); - if (!frontiers.Contains(node.Outgoing[0].Target)) { - HashSet content1 = FindDominatedNodes(nodes, node.Outgoing[0].Target); - nodes.ExceptWith(content1); - condition.Block1 = new ILBlock(FindConditions(content1, node.Outgoing[0].Target)); - } - if (!frontiers.Contains(node.Outgoing[1].Target)) { - HashSet content2 = FindDominatedNodes(nodes, node.Outgoing[1].Target); - nodes.ExceptWith(content2); - condition.Block2 = new ILBlock(FindConditions(content2, node.Outgoing[1].Target)); + ILMoveableBlock block = node.UserData as ILMoveableBlock; + + // Find a block that represents a simple condition + if (nodes.Contains(node) && block != null && block.Body.Count == 3) { + + ILLabel label = block.Body[0] as ILLabel; + ILExpression condBranch = block.Body[1] as ILExpression; + ILExpression statBranch = block.Body[2] as ILExpression; + + if (label != null && + condBranch != null && condBranch.Operand is ILLabel && condBranch.Arguments.Count > 0 && + statBranch != null && statBranch.Operand is ILLabel && statBranch.Arguments.Count == 0) + { + ControlFlowNode condTarget; + ControlFlowNode statTarget; + if (labelToCfNode.TryGetValue((ILLabel)condBranch.Operand, out condTarget) && + labelToCfNode.TryGetValue((ILLabel)statBranch.Operand, out statTarget)) + { + ILCondition condition = new ILCondition() { + Condition = condBranch, + TrueTarget = (ILLabel)condBranch.Operand, + FalseTarget = (ILLabel)statBranch.Operand + }; + + // TODO: Use the labels to ensre correctness + // TODO: Ensure that the labels are considered live in dead label removal + + // Replace the two branches with a conditional structure + block.Body.Remove(condBranch); + block.Body.Remove(statBranch); + block.Body.Add(condition); + result.Add(block); + + // Pull in the conditional code + HashSet frontiers = new HashSet(); + frontiers.UnionWith(condTarget.DominanceFrontier); + frontiers.UnionWith(statTarget.DominanceFrontier); + + if (!frontiers.Contains(condTarget)) { + HashSet content = FindDominatedNodes(nodes, condTarget); + nodes.ExceptWith(content); + condition.TrueBlock = new ILBlock(FindConditions(content, condTarget)); + } + if (!frontiers.Contains(statTarget)) { + HashSet content = FindDominatedNodes(nodes, statTarget); + nodes.ExceptWith(content); + condition.FalseBlock = new ILBlock(FindConditions(content, statTarget)); + } + + nodes.Remove(node); + } } - nodes.Remove(node); - result.Add(condition); } // Using the dominator tree should ensure we find the the widest loop first @@ -279,6 +291,27 @@ namespace Decompiler.ControlFlow return result; } + static HashSet FindDominatedNodes(HashSet nodes, ControlFlowNode head) + { + var exitNodes = head.DominanceFrontier.SelectMany(n => n.Predecessors); + HashSet agenda = new HashSet(exitNodes); + HashSet result = new HashSet(); + + while(agenda.Count > 0) { + ControlFlowNode addNode = agenda.First(); + agenda.Remove(addNode); + + if (nodes.Contains(addNode) && head.Dominates(addNode) && result.Add(addNode)) { + foreach (var predecessor in addNode.Predecessors) { + agenda.Add(predecessor); + } + } + } + result.Add(head); + + return result; + } + /* public enum ShortCircuitOperator @@ -345,6 +378,15 @@ namespace Decompiler.ControlFlow */ + void OrderNodes(ILBlock ast) + { + var blocks = ast.GetSelfAndChildrenRecursive().ToList(); + ILMoveableBlock first = new ILMoveableBlock() { OriginalOrder = -1 }; + foreach(ILBlock block in blocks) { + block.Body = block.Body.OrderBy(n => (n.GetSelfAndChildrenRecursive().FirstOrDefault() ?? first).OriginalOrder).ToList(); + } + } + /// /// Flattens all nested movable blocks, except the the top level 'node' argument /// @@ -355,8 +397,8 @@ namespace Decompiler.ControlFlow List flatBody = new List(); foreach (ILNode child in block.Body) { FlattenNestedMovableBlocks(child); - if (child is ILMoveAbleBlock) { - flatBody.AddRange(((ILMoveAbleBlock)child).Body); + if (child is ILMoveableBlock) { + flatBody.AddRange(((ILMoveableBlock)child).Body); } else { flatBody.Add(child); } diff --git a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs index db96898e5..584390299 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs @@ -174,15 +174,17 @@ namespace Decompiler public class ILCondition: ILNode { - public ILBlock ConditionBlock; - public ILBlock Block1; - public ILBlock Block2; + public ILExpression Condition; + public ILBlock TrueBlock; // Branch was taken + public ILLabel TrueTarget; // Entry label + public ILBlock FalseBlock; // Fall-though + public ILLabel FalseTarget; // Entry label public override IEnumerable GetChildren() { - yield return ConditionBlock; - yield return Block1; - yield return Block2; + yield return Condition; + yield return TrueBlock; + yield return FalseBlock; } } }