diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index 474eb6242..e7bc4ed69 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -76,6 +76,8 @@ namespace Decompiler { Ast.BlockStatement astBlock = new BlockStatement(); if (block != null) { + if (block.EntryGoto != null) + astBlock.AddStatement((Statement)TransformExpression(block.EntryGoto)); foreach(ILNode node in block.Body) { astBlock.AddStatements(TransformNode(node)); } @@ -104,24 +106,6 @@ namespace Decompiler yield return new Ast.ForStatement { EmbeddedStatement = TransformBlock(((ILLoop)node).ContentBlock) }; - /* - } else if (node is Branch) { - yield return new Ast.LabelStatement { Label = ((Branch)node).FirstBasicBlock.Label }; - - Ast.BlockStatement trueBlock = new Ast.BlockStatement(); - trueBlock.AddStatement(new Ast.GotoStatement(((Branch)node).TrueSuccessor.Label)); - - Ast.BlockStatement falseBlock = new Ast.BlockStatement(); - falseBlock.AddStatement(new Ast.GotoStatement(((Branch)node).FalseSuccessor.Label)); - - Ast.IfElseStatement ifElseStmt = new Ast.IfElseStatement { - Condition = MakeBranchCondition((Branch)node), - TrueStatement = trueBlock, - FalseStatement = falseBlock - }; - - yield return ifElseStmt; - */ } else if (node is ILCondition) { ILCondition conditionalNode = (ILCondition)node; if (conditionalNode.FalseBlock.Body.Any()) { @@ -148,6 +132,8 @@ namespace Decompiler switchStmt.SwitchSections.Add(section); } yield return switchStmt; + if (ilSwitch.DefaultGoto != null) + yield return (Statement)TransformExpression(ilSwitch.DefaultGoto); } else if (node is ILTryCatchBlock) { ILTryCatchBlock tryCatchNode = ((ILTryCatchBlock)node); var tryCatchStmt = new Ast.TryCatchStatement(); @@ -190,6 +176,23 @@ namespace Decompiler Ast.Expression MakeBranchCondition(ILExpression expr) { + switch(expr.Code) { + case ILCode.LogicNot: + return new Ast.UnaryOperatorExpression(UnaryOperatorType.Not, MakeBranchCondition(expr.Arguments[0])); + case ILCode.LogicAnd: + return new Ast.BinaryOperatorExpression( + MakeBranchCondition(expr.Arguments[0]), + BinaryOperatorType.ConditionalAnd, + MakeBranchCondition(expr.Arguments[1]) + ); + case ILCode.LogicOr: + return new Ast.BinaryOperatorExpression( + MakeBranchCondition(expr.Arguments[0]), + BinaryOperatorType.ConditionalOr, + MakeBranchCondition(expr.Arguments[1]) + ); + } + List args = TransformExpressionArguments(expr); Ast.Expression arg1 = args.Count >= 1 ? args[0] : null; Ast.Expression arg2 = args.Count >= 2 ? args[1] : null; @@ -229,43 +232,9 @@ namespace Decompiler 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) { - case ShortCircuitOperator.LeftAndRight: - return new BinaryOperatorExpression( - MakeBranchCondition(scBranch.Left), - BinaryOperatorType.ConditionalAnd, - MakeBranchCondition(scBranch.Right) - ); - case ShortCircuitOperator.LeftOrRight: - return new BinaryOperatorExpression( - MakeBranchCondition(scBranch.Left), - BinaryOperatorType.ConditionalOr, - MakeBranchCondition(scBranch.Right) - ); - case ShortCircuitOperator.NotLeftAndRight: - return new BinaryOperatorExpression( - new UnaryOperatorExpression(UnaryOperatorType.Not, MakeBranchCondition(scBranch.Left)), - BinaryOperatorType.ConditionalAnd, - MakeBranchCondition(scBranch.Right) - ); - case ShortCircuitOperator.NotLeftOrRight: - return new BinaryOperatorExpression( - new UnaryOperatorExpression(UnaryOperatorType.Not, MakeBranchCondition(scBranch.Left)), - BinaryOperatorType.ConditionalOr, - MakeBranchCondition(scBranch.Right) - ); - default: - throw new Exception("Bad operator"); - } - } else { - throw new Exception("Bad type"); + default: + throw new Exception("Bad opcode"); } - */ } AstNode TransformByteCode(ILExpression byteCode, List args) diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs index abfb0826e..4e95f6ffd 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -22,48 +22,47 @@ namespace Decompiler.ControlFlow public class ILAstOptimizer { Dictionary labelToCfNode = new Dictionary(); + Dictionary labelRefCount; public void Optimize(DecompilerContext context, ILBlock method, ILAstOptimizationStep abortBeforeStep = ILAstOptimizationStep.None) { if (abortBeforeStep == ILAstOptimizationStep.SplitToMovableBlocks) return; foreach(ILBlock block in method.GetSelfAndChildrenRecursive().ToList()) { - SplitToMovableBlocks(block); + SplitToBasicBlocks(block); } if (abortBeforeStep == ILAstOptimizationStep.FindLoops) return; - foreach(ILBlock block in method.GetSelfAndChildrenRecursive().Where(b => !(b is ILMoveableBlock)).ToList()) { + foreach(ILBlock block in method.GetSelfAndChildrenRecursive().ToList()) { ControlFlowGraph graph; - graph = BuildGraph(block.Body, block.EntryPoint); + graph = BuildGraph(block.Body, (ILLabel)block.EntryGoto.Operand); graph.ComputeDominance(); graph.ComputeDominanceFrontier(); block.Body = FindLoops(new HashSet(graph.Nodes.Skip(3)), graph.EntryPoint, true); } if (abortBeforeStep == ILAstOptimizationStep.FindConditions) return; - foreach(ILBlock block in method.GetSelfAndChildrenRecursive().Where(b => !(b is ILMoveableBlock)).ToList()) { + UpdateLabelRefCounts(method); + foreach(ILBlock block in method.GetSelfAndChildrenRecursive().ToList()) { ControlFlowGraph graph; - graph = BuildGraph(block.Body, block.EntryPoint); + graph = BuildGraph(block.Body, (ILLabel)block.EntryGoto.Operand); graph.ComputeDominance(); graph.ComputeDominanceFrontier(); block.Body = FindConditions(new HashSet(graph.Nodes.Skip(3)), graph.EntryPoint); } - // OrderNodes(method); if (abortBeforeStep == ILAstOptimizationStep.FlattenNestedMovableBlocks) return; - FlattenNestedMovableBlocks(method); + FlattenBasicBlocks(method); + if (abortBeforeStep == ILAstOptimizationStep.SimpleGotoRemoval) return; SimpleGotoRemoval(method); + if (abortBeforeStep == ILAstOptimizationStep.RemoveDeadLabels) return; RemoveDeadLabels(method); + if (abortBeforeStep == ILAstOptimizationStep.TypeInference) return; TypeAnalysis.Run(context, method); } - class ILMoveableBlock: ILBlock - { - public int OriginalOrder; - } - int nextBlockIndex = 0; /// @@ -71,26 +70,29 @@ namespace Decompiler.ControlFlow /// The method adds necessary branches to make control flow between blocks /// explicit and thus order independent. /// - void SplitToMovableBlocks(ILBlock block) + void SplitToBasicBlocks(ILBlock block) { // Remve no-ops // TODO: Assign the no-op range to someting block.Body = block.Body.Where(n => !(n is ILExpression && ((ILExpression)n).Code == ILCode.Nop)).ToList(); - List moveableBlocks = new List(); + List basicBlocks = new List(); - ILMoveableBlock moveableBlock = new ILMoveableBlock() { OriginalOrder = (nextBlockIndex++) }; - moveableBlocks.Add(moveableBlock); - block.EntryPoint = new ILLabel() { Name = "Block_" + moveableBlock.OriginalOrder }; - moveableBlock.Body.Add(block.EntryPoint); + ILBasicBlock basicBlock = new ILBasicBlock() { + EntryLabel = new ILLabel() { Name = "Block_" + (nextBlockIndex++) } + }; + basicBlocks.Add(basicBlock); + block.EntryGoto = new ILExpression(ILCode.Br, basicBlock.EntryLabel); if (block.Body.Count > 0) { - moveableBlock.Body.Add(block.Body[0]); + basicBlock.Body.Add(block.Body[0]); for (int i = 1; i < block.Body.Count; i++) { ILNode lastNode = block.Body[i - 1]; ILNode currNode = block.Body[i]; + bool added = false; + // Insert split if ((currNode is ILLabel && !(lastNode is ILLabel)) || lastNode is ILTryCatchBlock || @@ -98,24 +100,30 @@ namespace Decompiler.ControlFlow (lastNode is ILExpression) && ((ILExpression)lastNode).IsBranch() || (currNode is ILExpression) && ((ILExpression)currNode).IsBranch()) { - ILBlock lastBlock = moveableBlock; - moveableBlock = new ILMoveableBlock() { OriginalOrder = (nextBlockIndex++) }; - moveableBlocks.Add(moveableBlock); + ILBasicBlock lastBlock = basicBlock; + basicBlock = new ILBasicBlock(); + basicBlocks.Add(basicBlock); + if (currNode is ILLabel) { + // Reuse the first label + basicBlock.EntryLabel = (ILLabel)currNode; + added = true; + } else { + basicBlock.EntryLabel = new ILLabel() { Name = "Block_" + (nextBlockIndex++) }; + } // Explicit branch from one block to other // (unless the last expression was unconditional branch) if (!(lastNode is ILExpression) || ((ILExpression)lastNode).Code.CanFallThough()) { - ILLabel blockLabel = new ILLabel() { Name = "Block_" + moveableBlock.OriginalOrder }; - lastBlock.Body.Add(new ILExpression(ILCode.Br, blockLabel)); - moveableBlock.Body.Add(blockLabel); + lastBlock.FallthoughGoto = new ILExpression(ILCode.Br, basicBlock.EntryLabel); } } - moveableBlock.Body.Add(currNode); + if (!added) + basicBlock.Body.Add(currNode); } } - block.Body = moveableBlocks; + block.Body = basicBlocks; return; } @@ -175,7 +183,7 @@ namespace Decompiler.ControlFlow return new ControlFlowGraph(cfNodes.ToArray()); } - List FindLoops(HashSet nodes, ControlFlowNode entryPoint, bool excludeEntryPoint) + List FindLoops(HashSet scope, ControlFlowNode entryPoint, bool excludeEntryPoint) { List result = new List(); @@ -184,18 +192,22 @@ namespace Decompiler.ControlFlow while(agenda.Count > 0) { ControlFlowNode node = agenda.Dequeue(); - if (nodes.Contains(node) + if (scope.Contains(node) && node.DominanceFrontier.Contains(node) && (node != entryPoint || !excludeEntryPoint)) { HashSet loopContents = new HashSet(); - FindLoopContents(nodes, loopContents, node, node); + FindLoopContents(scope, loopContents, node, node); // Move the content into loop block - nodes.ExceptWith(loopContents); + scope.ExceptWith(loopContents); ILLabel entryLabel = new ILLabel() { Name = "Loop_" + (nextBlockIndex++) }; - ((ILBlock)node.UserData).Body.Insert(0, entryLabel); - result.Add(new ILLoop() { ContentBlock = new ILBlock(FindLoops(loopContents, node, true)) { EntryPoint = entryLabel } }); + ((ILBasicBlock)node.UserData).Body.Insert(0, entryLabel); + result.Add(new ILLoop() { + ContentBlock = new ILBlock(FindLoops(loopContents, node, true)) { + EntryGoto = new ILExpression(ILCode.Br, entryLabel) + } + }); } // Using the dominator tree should ensure we find the the widest loop first @@ -205,23 +217,34 @@ namespace Decompiler.ControlFlow } // Add whatever is left - foreach(var node in nodes) { + foreach(var node in scope) { result.Add((ILNode)node.UserData); } return result; } - static void FindLoopContents(HashSet nodes, HashSet loopContents, ControlFlowNode loopHead, ControlFlowNode addNode) + static void FindLoopContents(HashSet scope, HashSet loopContents, ControlFlowNode loopHead, ControlFlowNode addNode) { - if (nodes.Contains(addNode) && loopHead.Dominates(addNode) && loopContents.Add(addNode)) { + if (scope.Contains(addNode) && loopHead.Dominates(addNode) && loopContents.Add(addNode)) { foreach (var edge in addNode.Incoming) { - FindLoopContents(nodes, loopContents, loopHead, edge.Source); + FindLoopContents(scope, loopContents, loopHead, edge.Source); } } } - List FindConditions(HashSet nodes, ControlFlowNode entryNode) + void UpdateLabelRefCounts(ILBlock method) + { + labelRefCount = new Dictionary(); + foreach(ILLabel label in method.GetSelfAndChildrenRecursive()) { + labelRefCount[label] = 0; + } + foreach(ILLabel target in method.GetSelfAndChildrenRecursive().SelectMany(e => e.GetBranchTargets())) { + labelRefCount[target]++; + } + } + + List FindConditions(HashSet scope, ControlFlowNode entryNode) { List result = new List(); @@ -236,41 +259,32 @@ namespace Decompiler.ControlFlow agenda.Remove(node); // Find a block that represents a simple condition - if (nodes.Contains(node)) { + if (scope.Contains(node)) { - ILMoveableBlock block = node.UserData as ILMoveableBlock; + ILBasicBlock block = node.UserData as ILBasicBlock; - if (block != null && block.Body.Count == 3) { + if (block != null && block.Body.Count == 1) { - ILLabel label = block.Body[0] as ILLabel; - ILExpression condBranch = block.Body[1] as ILExpression; - ILExpression statBranch = block.Body[2] as ILExpression; + ILExpression condBranch = block.Body[0] as ILExpression; // Switch - if (label != null && - condBranch != null && condBranch.Operand is ILLabel[] && condBranch.Arguments.Count > 0 && - statBranch != null && statBranch.Operand is ILLabel && statBranch.Arguments.Count == 0) - { - ILSwitch ilSwitch = new ILSwitch() { Condition = condBranch }; + if (condBranch != null && condBranch.Operand is ILLabel[] && condBranch.Arguments.Count > 0) { - // Replace the two branches with a conditional structure - this preserves the node label - block.Body.Remove(condBranch); - block.Body.Remove(statBranch); - block.Body.Add(ilSwitch); + ILSwitch ilSwitch = new ILSwitch() { + Condition = condBranch, + DefaultGoto = block.FallthoughGoto + }; - ControlFlowNode statTarget = null; - labelToCfNode.TryGetValue((ILLabel)statBranch.Operand, out statTarget); + ControlFlowNode fallTarget = null; + labelToCfNode.TryGetValue((ILLabel)block.FallthoughGoto.Operand, out fallTarget); - // Pull in the conditional code HashSet frontiers = new HashSet(); - - if (statTarget != null) - frontiers.UnionWith(statTarget.DominanceFrontier); + if (fallTarget != null) + frontiers.UnionWith(fallTarget.DominanceFrontier); foreach(ILLabel condLabel in (ILLabel[])condBranch.Operand) { ControlFlowNode condTarget = null; labelToCfNode.TryGetValue(condLabel, out condTarget); - if (condTarget != null) frontiers.UnionWith(condTarget.DominanceFrontier); } @@ -279,10 +293,12 @@ namespace Decompiler.ControlFlow ControlFlowNode condTarget = null; labelToCfNode.TryGetValue(condLabel, out condTarget); - ILBlock caseBlock = new ILBlock() { EntryPoint = condLabel }; + ILBlock caseBlock = new ILBlock() { + EntryGoto = new ILExpression(ILCode.Br, condLabel) + }; if (condTarget != null && !frontiers.Contains(condTarget)) { - HashSet content = FindDominatedNodes(nodes, condTarget); - nodes.ExceptWith(content); + HashSet content = FindDominatedNodes(scope, condTarget); + scope.ExceptWith(content); caseBlock.Body.AddRange(FindConditions(content, condTarget)); } ilSwitch.CaseBlocks.Add(caseBlock); @@ -291,61 +307,61 @@ namespace Decompiler.ControlFlow // The labels will not be used - kill them condBranch.Operand = null; - result.Add(block); - nodes.Remove(node); + result.Add(new ILBasicBlock() { + EntryLabel = block.EntryLabel, // Keep the entry label + Body = { ilSwitch } + }); + scope.Remove(node); } // Two-way branch - if (label != null && - condBranch != null && condBranch.Operand is ILLabel && condBranch.Arguments.Count > 0 && - statBranch != null && statBranch.Operand is ILLabel && statBranch.Arguments.Count == 0) - { - ControlFlowNode statTarget = null; - labelToCfNode.TryGetValue((ILLabel)statBranch.Operand, out statTarget); - ControlFlowNode condTarget = null; - labelToCfNode.TryGetValue((ILLabel)condBranch.Operand, out condTarget); + ILCondition ilCond; + HashSet matchedNodes; + ILLabel condEntryLabel; + if (TryMatchCondition(scope, new ControlFlowNode[] {}, node, out ilCond, out matchedNodes, out condEntryLabel)) { - ILCondition condition = new ILCondition() { - Condition = condBranch, - TrueBlock = new ILBlock() { EntryPoint = (ILLabel)condBranch.Operand }, - FalseBlock = new ILBlock() { EntryPoint = (ILLabel)statBranch.Operand } - }; + // The branch labels will not be used - kill them + foreach(ILExpression expr in ilCond.Condition.GetSelfAndChildrenRecursive()) { + if (expr.GetBranchTargets().Any()) { + expr.Operand = null; + } + } - // Replace the two branches with a conditional structure - this preserves the node label - block.Body.Remove(condBranch); - block.Body.Remove(statBranch); - block.Body.Add(condition); + ControlFlowNode trueTarget = null; + labelToCfNode.TryGetValue((ILLabel)ilCond.TrueBlock.EntryGoto.Operand, out trueTarget); + ControlFlowNode falseTarget = null; + labelToCfNode.TryGetValue((ILLabel)ilCond.FalseBlock.EntryGoto.Operand, out falseTarget); // Pull in the conditional code HashSet frontiers = new HashSet(); - if (statTarget != null) - frontiers.UnionWith(statTarget.DominanceFrontier); - if (condTarget != null) - frontiers.UnionWith(condTarget.DominanceFrontier); + if (trueTarget != null) + frontiers.UnionWith(trueTarget.DominanceFrontier); + if (falseTarget != null) + frontiers.UnionWith(falseTarget.DominanceFrontier); - if (condTarget != null && !frontiers.Contains(condTarget)) { - HashSet content = FindDominatedNodes(nodes, condTarget); - nodes.ExceptWith(content); - condition.TrueBlock.Body.AddRange(FindConditions(content, condTarget)); + if (trueTarget != null && !frontiers.Contains(trueTarget)) { + HashSet content = FindDominatedNodes(scope, trueTarget); + scope.ExceptWith(content); + ilCond.TrueBlock.Body.AddRange(FindConditions(content, trueTarget)); } - if (statTarget != null && !frontiers.Contains(statTarget)) { - HashSet content = FindDominatedNodes(nodes, statTarget); - nodes.ExceptWith(content); - condition.FalseBlock.Body.AddRange(FindConditions(content, statTarget)); + if (falseTarget != null && !frontiers.Contains(falseTarget)) { + HashSet content = FindDominatedNodes(scope, falseTarget); + scope.ExceptWith(content); + ilCond.FalseBlock.Body.AddRange(FindConditions(content, falseTarget)); } - // The label will not be used - kill it - condBranch.Operand = null; - - result.Add(block); - nodes.Remove(node); + result.Add(new ILBasicBlock() { + EntryLabel = condEntryLabel, // Keep the entry label + Body = { ilCond } + }); + scope.ExceptWith(matchedNodes); } } // Add the node now so that we have good ordering - if (nodes.Contains(node)) { + if (scope.Contains(node)) { result.Add((ILNode)node.UserData); - nodes.Remove(node); + scope.Remove(node); } } @@ -356,14 +372,115 @@ namespace Decompiler.ControlFlow } // Add whatever is left - foreach(var node in nodes) { + foreach(var node in scope) { result.Add((ILNode)node.UserData); } return result; } - static HashSet FindDominatedNodes(HashSet nodes, ControlFlowNode head) + bool TryMatchCondition(HashSet scope, IEnumerable scopeExcept, ControlFlowNode head, out ILCondition condition, out HashSet matchedNodes, out ILLabel entryLabel) + { + condition = null; + matchedNodes = null; + entryLabel = null; + if (!scope.Contains(head) || scopeExcept.Contains(head)) + return false; + + ILBasicBlock basicBlock = head.UserData as ILBasicBlock; + + if (basicBlock == null || basicBlock.Body.Count != 1) + return false; + + ILExpression condBranch = basicBlock.Body[0] as ILExpression; + + if (condBranch != null && condBranch.Operand is ILLabel && condBranch.Arguments.Count > 0) { + + // We have found a two-way condition + condition = new ILCondition() { + Condition = condBranch, + TrueBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, condBranch.Operand) }, + FalseBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, basicBlock.FallthoughGoto.Operand) } + }; + // We are done with the node so "remove" it from scope + scopeExcept = scopeExcept.Union(new[] {head}); + matchedNodes = new HashSet() { head }; + entryLabel = basicBlock.EntryLabel; + + // Optimize short-circut expressions + while(true) { + + // Consider condition.TrueBlock + { + ILLabel nextLabel = (ILLabel)condition.TrueBlock.EntryGoto.Operand; + ControlFlowNode nextTarget; + labelToCfNode.TryGetValue(nextLabel, out nextTarget); + ILCondition nextCond; + HashSet nextMatchedNodes; + ILLabel nextEnteryLabel; + if (nextTarget != null && + TryMatchCondition(scope, scopeExcept, nextTarget, out nextCond, out nextMatchedNodes, out nextEnteryLabel) && + labelRefCount[nextEnteryLabel] == 1) + { + if (condition.FalseBlock.EntryGoto.Operand == nextCond.FalseBlock.EntryGoto.Operand) { + condition.Condition = new ILExpression(ILCode.LogicAnd, null, condition.Condition, nextCond.Condition); + condition.TrueBlock = nextCond.TrueBlock; + condition.FalseBlock = nextCond.FalseBlock; + scopeExcept = scopeExcept.Union(nextMatchedNodes); + matchedNodes.UnionWith(nextMatchedNodes); + continue; + } + + if (condition.FalseBlock.EntryGoto.Operand == nextCond.TrueBlock.EntryGoto.Operand) { + condition.Condition = new ILExpression(ILCode.LogicOr, null, new ILExpression(ILCode.LogicNot, null, condition.Condition), nextCond.Condition); + condition.TrueBlock = nextCond.TrueBlock; + condition.FalseBlock = nextCond.FalseBlock; + scopeExcept = scopeExcept.Union(nextMatchedNodes); + matchedNodes.UnionWith(nextMatchedNodes); + continue; + } + } + } + + // Consider condition.FalseBlock + { + ILLabel nextLabel = (ILLabel)condition.FalseBlock.EntryGoto.Operand; + ControlFlowNode nextTarget; + labelToCfNode.TryGetValue(nextLabel, out nextTarget); + ILCondition nextCond; + HashSet nextMatchedNodes; + ILLabel nextEnteryLabel; + if (nextTarget != null && + TryMatchCondition(scope, scopeExcept, nextTarget, out nextCond, out nextMatchedNodes, out nextEnteryLabel) && + labelRefCount[nextEnteryLabel] == 1) + { + if (condition.TrueBlock.EntryGoto.Operand == nextCond.FalseBlock.EntryGoto.Operand) { + condition.Condition = new ILExpression(ILCode.LogicAnd, null, new ILExpression(ILCode.LogicNot, null, condition.Condition), nextCond.Condition); + condition.TrueBlock = nextCond.TrueBlock; + condition.FalseBlock = nextCond.FalseBlock; + scopeExcept = scopeExcept.Union(nextMatchedNodes); + matchedNodes.UnionWith(nextMatchedNodes); + continue; + } + + if (condition.TrueBlock.EntryGoto.Operand == nextCond.TrueBlock.EntryGoto.Operand) { + condition.Condition = new ILExpression(ILCode.LogicOr, null, condition.Condition, nextCond.Condition); + condition.TrueBlock = nextCond.TrueBlock; + condition.FalseBlock = nextCond.FalseBlock; + scopeExcept = scopeExcept.Union(nextMatchedNodes); + matchedNodes.UnionWith(nextMatchedNodes); + continue; + } + } + } + break; + } + return true; + } + return false; + } + + static HashSet FindDominatedNodes(HashSet scope, ControlFlowNode head) { var exitNodes = head.DominanceFrontier.SelectMany(n => n.Predecessors); HashSet agenda = new HashSet(exitNodes); @@ -373,121 +490,42 @@ namespace Decompiler.ControlFlow ControlFlowNode addNode = agenda.First(); agenda.Remove(addNode); - if (nodes.Contains(addNode) && head.Dominates(addNode) && result.Add(addNode)) { + if (scope.Contains(addNode) && head.Dominates(addNode) && result.Add(addNode)) { foreach (var predecessor in addNode.Predecessors) { agenda.Add(predecessor); } } } - if (nodes.Contains(head)) + if (scope.Contains(head)) result.Add(head); 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; - } - - */ - - void OrderNodes(ILBlock ast) - { - // Order movable nodes - var blocks = ast.GetSelfAndChildrenRecursive().Where(b => !(b is ILMoveableBlock)).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 + /// Flattens all nested basic blocks, except the the top level 'node' argument /// - void FlattenNestedMovableBlocks(ILNode node) + void FlattenBasicBlocks(ILNode node) { ILBlock block = node as ILBlock; if (block != null) { List flatBody = new List(); - if (block.EntryPoint != null) { - flatBody.Add(new ILExpression(ILCode.Br, block.EntryPoint)); - block.EntryPoint = null; - } - foreach (ILNode child in block.Body) { - FlattenNestedMovableBlocks(child); - if (child is ILMoveableBlock) { - flatBody.AddRange(((ILMoveableBlock)child).Body); + foreach (ILNode child in block.GetChildren()) { + FlattenBasicBlocks(child); + if (child is ILBasicBlock) { + flatBody.AddRange(child.GetChildren()); } else { flatBody.Add(child); } } + block.EntryGoto = null; 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); + FlattenBasicBlocks(child); } } } diff --git a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs index a7c8e297b..21e7e10b4 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs @@ -3,13 +3,13 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; - using Decompiler.ControlFlow; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.NRefactory.Utils; using Mono.Cecil; using Mono.Cecil.Cil; +using Mono.CSharp; using Cecil = Mono.Cecil; namespace Decompiler @@ -38,8 +38,7 @@ namespace Decompiler public class ILBlock: ILNode { - // TODO: This should really be a goto, not a label - public ILLabel EntryPoint; + public ILExpression EntryGoto; public List Body; @@ -55,8 +54,8 @@ namespace Decompiler public override IEnumerable GetChildren() { - if (EntryPoint != null) - yield return EntryPoint; + if (this.EntryGoto != null) + yield return this.EntryGoto; foreach(ILNode child in this.Body) { yield return child; } @@ -64,9 +63,33 @@ namespace Decompiler public override void WriteTo(ITextOutput output) { - if (EntryPoint != null) - EntryPoint.WriteTo(output); - foreach(ILNode child in this.Body) { + foreach(ILNode child in this.GetChildren()) { + child.WriteTo(output); + output.WriteLine(); + } + } + } + + public class ILBasicBlock: ILNode + { + public ILLabel EntryLabel; + public List Body = new List(); + public ILExpression FallthoughGoto; + + public override IEnumerable GetChildren() + { + if (this.EntryLabel != null) + yield return this.EntryLabel; + foreach (ILNode child in this.Body) { + yield return child; + } + if (this.FallthoughGoto != null) + yield return this.FallthoughGoto; + } + + public override void WriteTo(ITextOutput output) + { + foreach(ILNode child in this.GetChildren()) { child.WriteTo(output); output.WriteLine(); } @@ -119,7 +142,8 @@ namespace Decompiler public override IEnumerable GetChildren() { - yield return this.TryBlock; + if (this.TryBlock != null) + yield return this.TryBlock; foreach (var catchBlock in this.CatchBlocks) { yield return catchBlock; } @@ -188,6 +212,11 @@ namespace Decompiler this.ILRanges = new List(1); } + public override IEnumerable GetChildren() + { + return Arguments; + } + public bool IsBranch() { return this.Operand is ILLabel || this.Operand is ILLabel[]; @@ -225,11 +254,6 @@ namespace Decompiler return ranges; } - public override IEnumerable GetChildren() - { - return Arguments; - } - public override void WriteTo(ITextOutput output) { if (Operand is ILVariable && ((ILVariable)Operand).IsGenerated) { @@ -283,7 +307,8 @@ namespace Decompiler public override IEnumerable GetChildren() { - yield return ContentBlock; + if (this.ContentBlock != null) + yield return ContentBlock; } public override void WriteTo(ITextOutput output) @@ -304,10 +329,12 @@ namespace Decompiler public override IEnumerable GetChildren() { - yield return Condition; - yield return TrueBlock; - if (FalseBlock != null) - yield return FalseBlock; + if (this.Condition != null) + yield return this.Condition; + if (this.TrueBlock != null) + yield return this.TrueBlock; + if (this.FalseBlock != null) + yield return this.FalseBlock; } public override void WriteTo(ITextOutput output) @@ -333,13 +360,17 @@ namespace Decompiler { public ILExpression Condition; public List CaseBlocks = new List(); + public ILExpression DefaultGoto; public override IEnumerable GetChildren() { - yield return Condition; + if (this.Condition != null) + yield return this.Condition; foreach (ILBlock caseBlock in this.CaseBlocks) { yield return caseBlock; } + if (this.DefaultGoto != null) + yield return this.DefaultGoto; } public override void WriteTo(ITextOutput output) diff --git a/ICSharpCode.Decompiler/ILAst/ILCodes.cs b/ICSharpCode.Decompiler/ILAst/ILCodes.cs index 7cb0abec5..2f4de5351 100644 --- a/ICSharpCode.Decompiler/ILAst/ILCodes.cs +++ b/ICSharpCode.Decompiler/ILAst/ILCodes.cs @@ -255,6 +255,9 @@ namespace Decompiler // Virtual codes - defined for convenience Ldexception, // Operand holds the CatchType for catch handler, null for filter + LogicNot, + LogicAnd, + LogicOr } public static class ILCodeUtil diff --git a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs index d81eddee9..3549c13bd 100644 --- a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs +++ b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs @@ -103,6 +103,20 @@ namespace Decompiler TypeReference DoInferTypeForExpression(ILExpression expr, TypeReference expectedType, bool forceInferChildren = false) { + switch (expr.Code) { + case ILCode.LogicNot: + if (forceInferChildren) { + InferTypeForExpression(expr.Arguments.Single(), typeSystem.Boolean); + } + return typeSystem.Boolean; + case ILCode.LogicAnd: + case ILCode.LogicOr: + if (forceInferChildren) { + InferTypeForExpression(expr.Arguments[0], typeSystem.Boolean); + InferTypeForExpression(expr.Arguments[0], typeSystem.Boolean); + } + return typeSystem.Boolean; + } switch ((Code)expr.Code) { #region Variable load/store case Code.Stloc: