From 1174435aa61b2061be3e1c69676d63ad8398183c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Srbeck=C3=BD?= Date: Sun, 27 Feb 2011 07:21:01 +0000 Subject: [PATCH 1/5] Preparing duplication of return statement --- .../ILAst/ILAstOptimizer.cs | 108 ++++++++++++++++-- 1 file changed, 101 insertions(+), 7 deletions(-) diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs index 6fa5b760e..23971feb8 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -25,6 +25,8 @@ namespace Decompiler.ControlFlow public class ILAstOptimizer { + int nextLabelIndex = 0; + Dictionary labelToCfNode = new Dictionary(); public void Optimize(DecompilerContext context, ILBlock method, ILAstOptimizationStep abortBeforeStep = ILAstOptimizationStep.None) @@ -61,6 +63,11 @@ namespace Decompiler.ControlFlow block.Body = FindConditions(new HashSet(graph.Nodes.Skip(3)), graph.EntryPoint); } + AnalyseLabels(method); + foreach(ILBlock block in method.GetSelfAndChildrenRecursive().ToList()) { + ReturnOptimizations(block); + } + if (abortBeforeStep == ILAstOptimizationStep.FlattenNestedMovableBlocks) return; FlattenBasicBlocks(method); @@ -77,8 +84,6 @@ namespace Decompiler.ControlFlow TypeAnalysis.Run(context, method); } - int nextBlockIndex = 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 @@ -93,7 +98,7 @@ namespace Decompiler.ControlFlow List basicBlocks = new List(); ILBasicBlock basicBlock = new ILBasicBlock() { - EntryLabel = new ILLabel() { Name = "Block_" + (nextBlockIndex++) } + EntryLabel = new ILLabel() { Name = "Block_" + (nextLabelIndex++) } }; basicBlocks.Add(basicBlock); block.EntryGoto = new ILExpression(ILCode.Br, basicBlock.EntryLabel); @@ -120,7 +125,7 @@ namespace Decompiler.ControlFlow // Insert as entry label basicBlock.EntryLabel = (ILLabel)currNode; } else { - basicBlock.EntryLabel = new ILLabel() { Name = "Block_" + (nextBlockIndex++) }; + basicBlock.EntryLabel = new ILLabel() { Name = "Block_" + (nextLabelIndex++) }; basicBlock.Body.Add(currNode); } @@ -176,11 +181,12 @@ namespace Decompiler.ControlFlow do { modified = false; for (int i = 0; i < block.Body.Count;) { - if (TrySimplifyShortCircuit(block.Body, (ILBasicBlock)block.Body[i])) { + ILBasicBlock bb = (ILBasicBlock)block.Body[i]; + if (TrySimplifyShortCircuit(block.Body, bb)) { modified = true; continue; } - if (TrySimplifyTernaryOperator(block.Body, (ILBasicBlock)block.Body[i])) { + if (TrySimplifyTernaryOperator(block.Body, bb)) { modified = true; continue; } @@ -323,6 +329,94 @@ namespace Decompiler.ControlFlow return false; } + void ReturnOptimizations(ILBlock block) + { + bool modified; + do { + modified = false; + for (int i = 0; i < block.Body.Count;) { + ILBasicBlock bb = block.Body[i] as ILBasicBlock; + // TODO: Isn't basicblock? + if (bb != null && TryDuplicateFollowingReturn(block.Body, bb)) { + modified = true; + continue; + } + i++; + } + } while(modified); + } + + bool TryDuplicateFollowingReturn(List scope, ILBasicBlock head) + { + bool modified = false; + if (head.FallthoughGoto != null) { + ILLabel fallLabel = (ILLabel)head.FallthoughGoto.Operand; + ILLabel dupLabel = null; + if (labelGlobalRefCount[fallLabel] > 1 && + TryDuplicateReturn(scope, fallLabel, ref dupLabel)) { + labelGlobalRefCount[fallLabel]--; + labelGlobalRefCount[dupLabel] = 1; + head.FallthoughGoto.Operand = dupLabel; + modified = true; + } + } + if (head.Body.Count == 1) { + ILExpression brExpr = head.Body[0] as ILExpression; + if (brExpr != null && brExpr.Operand is ILLabel) { + ILLabel brLabel = (ILLabel)brExpr.Operand; + ILLabel dupLabel = null; + if (labelGlobalRefCount[brLabel] > 1 && + TryDuplicateReturn(scope, brLabel, ref dupLabel)) { + labelGlobalRefCount[brLabel]--; + labelGlobalRefCount[dupLabel] = 1; + brExpr.Operand = dupLabel; + modified = true; + } + } + } + return modified; + } + + bool TryDuplicateReturn(List scope, ILLabel head, ref ILLabel newHead) + { + ILBasicBlock bb; + // TODO: mapping missing? + if (labelToBasicBlock.TryGetValue(head, out bb) && bb.Body.Count == 1 && !scope.Contains(bb)) { + ILExpression retExpr = bb.Body[0] as ILExpression; + if (retExpr != null && + retExpr.Prefixes == null && + retExpr.Code == ILCode.Ret) + { + if (retExpr.Arguments.Count == 0) { + newHead = new ILLabel() { Name = "Return_" + (nextLabelIndex++) }; + ILBasicBlock newBB = new ILBasicBlock() { + EntryLabel = newHead, + Body = { new ILExpression(ILCode.Ret, null) } + }; + scope.Add(newBB); + labelToBasicBlock[newHead] = newBB; + return true; + } else { + ILExpression argExpr = retExpr.Arguments[0] as ILExpression; + if (argExpr != null && + argExpr.Prefixes == null && + argExpr.Code == ILCode.Ldloc) + { + newHead = new ILLabel() { Name = "Return_" + (nextLabelIndex++) }; + ILBasicBlock newBB = new ILBasicBlock() { + EntryLabel = newHead, + Body = { new ILExpression(ILCode.Ret, null, new ILExpression(ILCode.Ldloc, argExpr.Operand)) } + }; + scope.Add(newBB); + labelToBasicBlock[newHead] = newBB; + return true; + } + } + } + } + return false; + } + ControlFlowGraph BuildGraph(List nodes, ILLabel entryLabel) { int index = 0; @@ -418,7 +512,7 @@ namespace Decompiler.ControlFlow loop.BodyBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, trueLabel) }; } else { // Give the block some explicit entry point - ILLabel entryLabel = new ILLabel() { Name = "Loop_" + (nextBlockIndex++) }; + ILLabel entryLabel = new ILLabel() { Name = "Loop_" + (nextLabelIndex++) }; loop.BodyBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, entryLabel) }; ((ILBasicBlock)node.UserData).Body.Insert(0, entryLabel); } From 1bc30662b00110bb5a63eeb4cddf01f26aa0412e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Srbeck=C3=BD?= Date: Sun, 27 Feb 2011 15:54:55 +0000 Subject: [PATCH 2/5] Rewritten goto removal. Closes #32 Closes #18 --- .../Ast/AstMethodBodyBuilder.cs | 13 +- .../Transforms/PatternStatementTransform.cs | 3 +- .../Ast/Transforms/RemoveDeadLabels.cs | 56 ----- .../Ast/Transforms/RemoveEmptyElseBody.cs | 19 -- .../Ast/Transforms/RemoveGotos.cs | 207 ------------------ .../Ast/Transforms/RestoreLoop.cs | 73 ------ .../Ast/Transforms/TransformationPipeline.cs | 13 -- .../ICSharpCode.Decompiler.csproj | 6 +- ICSharpCode.Decompiler/ILAst/GotoRemoval.cs | 202 +++++++++++++++++ .../ILAst/ILAstOptimizer.cs | 109 ++++----- ICSharpCode.Decompiler/ILAst/ILAstTypes.cs | 12 +- ICSharpCode.Decompiler/ILAst/ILCodes.cs | 2 + ICSharpCode.Decompiler/Options.cs | 14 -- 13 files changed, 258 insertions(+), 471 deletions(-) delete mode 100644 ICSharpCode.Decompiler/Ast/Transforms/RemoveDeadLabels.cs delete mode 100644 ICSharpCode.Decompiler/Ast/Transforms/RemoveEmptyElseBody.cs delete mode 100644 ICSharpCode.Decompiler/Ast/Transforms/RemoveGotos.cs delete mode 100644 ICSharpCode.Decompiler/Ast/Transforms/RestoreLoop.cs create mode 100644 ICSharpCode.Decompiler/ILAst/GotoRemoval.cs delete mode 100644 ICSharpCode.Decompiler/Options.cs diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index 20f8eed57..65ca65853 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -105,21 +105,18 @@ namespace Decompiler } } else if (node is ILWhileLoop) { ILWhileLoop ilLoop = (ILWhileLoop)node; - if (ilLoop.PreLoopLabel != null) - yield return TransformNode(ilLoop.PreLoopLabel).Single(); WhileStatement whileStmt = new WhileStatement() { Condition = ilLoop.Condition != null ? MakeBranchCondition(ilLoop.Condition) : new PrimitiveExpression(true), EmbeddedStatement = TransformBlock(ilLoop.BodyBlock) }; yield return whileStmt; - if (ilLoop.PostLoopGoto != null) - yield return (Statement)TransformExpression(ilLoop.PostLoopGoto); } else if (node is ILCondition) { ILCondition conditionalNode = (ILCondition)node; + bool hasFalseBlock = conditionalNode.FalseBlock.EntryGoto != null || conditionalNode.FalseBlock.Body.Count > 0; yield return new Ast.IfElseStatement { Condition = MakeBranchCondition(conditionalNode.Condition), TrueStatement = TransformBlock(conditionalNode.TrueBlock), - FalseStatement = TransformBlock(conditionalNode.FalseBlock) + FalseStatement = hasFalseBlock ? TransformBlock(conditionalNode.FalseBlock) : null }; } else if (node is ILSwitch) { ILSwitch ilSwitch = (ILSwitch)node; @@ -291,6 +288,10 @@ namespace Decompiler TrueExpression = (Expression)TransformExpression(byteCode.Arguments[1]), FalseExpression = (Expression)TransformExpression(byteCode.Arguments[2]), }; + case ILCode.LoopBreak: + return new Ast.BreakStatement(); + case ILCode.LoopContinue: + return new Ast.ContinueStatement(); } List args = TransformExpressionArguments(byteCode); @@ -568,7 +569,7 @@ namespace Decompiler } else { return InlineAssembly(byteCode, args); } - case Code.Leave: return null; + case Code.Leave: return new GotoStatement() { Label = ((ILLabel)operand).Name }; case Code.Localloc: return InlineAssembly(byteCode, args); case Code.Mkrefany: return InlineAssembly(byteCode, args); case Code.Newobj: diff --git a/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs index 15dc59905..78a9d3596 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs @@ -204,8 +204,7 @@ namespace Decompiler.Transforms Left = new Backreference("ident").ToExpression(), Operator = AssignmentOperatorType.Any, Right = new AnyNode().ToExpression() - })).ToStatement(), - new ContinueStatement() + })).ToStatement() } }; 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/Ast/Transforms/RemoveEmptyElseBody.cs b/ICSharpCode.Decompiler/Ast/Transforms/RemoveEmptyElseBody.cs deleted file mode 100644 index a33065232..000000000 --- a/ICSharpCode.Decompiler/Ast/Transforms/RemoveEmptyElseBody.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Linq; -using ICSharpCode.NRefactory.CSharp; - -namespace Decompiler.Transforms.Ast -{ - public class RemoveEmptyElseBody: DepthFirstAstVisitor - { - public override object VisitIfElseStatement(IfElseStatement ifElseStatement, object data) - { - base.VisitIfElseStatement(ifElseStatement, data); - BlockStatement block = ifElseStatement.FalseStatement as BlockStatement; - if (block != null && !block.Statements.Any()) { - ifElseStatement.FalseStatement = null; - } - return null; - } - } -} diff --git a/ICSharpCode.Decompiler/Ast/Transforms/RemoveGotos.cs b/ICSharpCode.Decompiler/Ast/Transforms/RemoveGotos.cs deleted file mode 100644 index 69a935568..000000000 --- a/ICSharpCode.Decompiler/Ast/Transforms/RemoveGotos.cs +++ /dev/null @@ -1,207 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using ICSharpCode.NRefactory.CSharp; - -namespace Decompiler.Transforms.Ast -{ - public class RemoveGotos: DepthFirstAstVisitor - { - Stack enteredLoops = new Stack(); - - Statement CurrentLoop { - get { - if (enteredLoops.Count > 0) { - return enteredLoops.Peek(); - } else { - return null; - } - } - } - - Statement CurrentLoopBody { - get { - if (this.CurrentLoop == null) { - return null; - } else if (this.CurrentLoop is ForStatement) { - return ((ForStatement)this.CurrentLoop).EmbeddedStatement; - } else if (this.CurrentLoop is WhileStatement) { - return ((WhileStatement)this.CurrentLoop).EmbeddedStatement; - } else { - return null; - } - } - } - - public override object VisitForStatement(ForStatement forStatement, object data) - { - enteredLoops.Push(forStatement); - base.VisitForStatement(forStatement, data); - enteredLoops.Pop(); - return null; - } - - public override object VisitWhileStatement(WhileStatement whileStatement, object data) - { - enteredLoops.Push(whileStatement); - base.VisitWhileStatement(whileStatement, data); - enteredLoops.Pop(); - return null; - } - - public override object VisitBlockStatement(BlockStatement blockStatement, object data) - { - base.VisitBlockStatement(blockStatement, data); - - // Remove redundant jump at the end of block - AstNode lastStmt = blockStatement.Children.LastOrDefault(); - // End of for loop - if (lastStmt is ContinueStatement && - blockStatement.Parent is ForStatement) - { - lastStmt.Remove(); - return null; - } - // End of method - if (lastStmt is ReturnStatement && - (blockStatement.Parent is MethodDeclaration || blockStatement.Parent is ConstructorDeclaration) && - ((ReturnStatement)lastStmt).Expression.IsNull) - { - lastStmt.Remove(); - return null; - } - - return null; - } - - // Get the next statement that will be executed after this one - // May return null - public static AstNode GetNextStatement(Statement statement) - { - if (statement == null) throw new ArgumentNullException(); - - Statement next = (Statement)statement.NextSibling; - - if (next != null) { - return EnterBlockStatement(next); - } else { - if (statement.Parent is BlockStatement && - statement.Parent.Parent is Statement) { - return ExitBlockStatement((Statement)statement.Parent.Parent); - } else { - return null; - } - } - } - - // Get the statement that will be executed once the given block exits by the end brace - // May return null - public static AstNode ExitBlockStatement(Statement statement) - { - if (statement == null) throw new ArgumentNullException(); - - // When an 'if' body is finished the execution continues with the - // next statement after the 'if' statement - if (statement is IfElseStatement) { - return GetNextStatement((IfElseStatement)statement); - } - - // When a 'for' body is finished the execution continues by: - // Iterator; Condition; Body - if (statement is ForStatement) { - ForStatement forLoop = statement as ForStatement; - if (forLoop.Iterators.Any()) { - return forLoop.Iterators.First(); - } else if (!forLoop.Condition.IsNull) { - return forLoop.Condition; - } else { - return EnterBlockStatement((Statement)forLoop.EmbeddedStatement.FirstChild); - } - } - - return null; - } - - // Get the first statement that will be executed in the given block - public static AstNode EnterBlockStatement(Statement statement) - { - if (statement == null) throw new ArgumentNullException(); - - // For loop starts as follows: Initializers; Condition; Body - if (statement is ForStatement) { - ForStatement forLoop = statement as ForStatement; - if (forLoop.Initializers.Any()) { - return forLoop.Initializers.First(); - } else if (!forLoop.Condition.IsNull) { - return forLoop.Condition; - } else if (forLoop.EmbeddedStatement.Children.FirstOrDefault() is Statement) { - return EnterBlockStatement((Statement)forLoop.EmbeddedStatement.FirstChild); // Simplify again - } - } - - return statement; // Can not simplify - } - - public override object VisitGotoStatement(GotoStatement gotoStatement, object data) - { - // Remove redundant goto which goes to a label that imideately follows - AstNode fallthoughTarget = GetNextStatement(gotoStatement); - while(true) { - if (fallthoughTarget is LabelStatement) { - if ((fallthoughTarget as LabelStatement).Label == gotoStatement.Label) { - gotoStatement.Remove(); - return null; - } else { - fallthoughTarget = GetNextStatement((LabelStatement)fallthoughTarget); - continue; - } - } - break; - } - - // Replace goto with 'break' - // Break statement moves right outside the looop - if (CurrentLoop != null) { - AstNode breakTarget = GetNextStatement(CurrentLoop); - if ((breakTarget is LabelStatement) && - (breakTarget as LabelStatement).Label == gotoStatement.Label) { - gotoStatement.ReplaceWith(new BreakStatement()); - return null; - } - } - - // Replace goto with 'continue' - // Continue statement which moves at the very end of loop - if (CurrentLoop != null && - (CurrentLoopBody is BlockStatement) && - ((CurrentLoopBody as BlockStatement).LastChild as LabelStatement) != null && - ((CurrentLoopBody as BlockStatement).LastChild as LabelStatement).Label == gotoStatement.Label) { - gotoStatement.ReplaceWith(new ContinueStatement()); - return null; - } - - // Replace goto with 'continue' - // Jump before while - if (CurrentLoop is WhileStatement && - CurrentLoop.PrevSibling != null && - CurrentLoop.PrevSibling is LabelStatement && - (CurrentLoop.PrevSibling as LabelStatement).Label == gotoStatement.Label) { - gotoStatement.ReplaceWith(new ContinueStatement()); - return null; - } - - // Replace goto with 'continue' - // Continue statement which moves at the very start of for loop - if (CurrentLoop != null) { - AstNode continueTarget = ExitBlockStatement(CurrentLoop); // The start of the loop - if ((continueTarget is LabelStatement) && - (continueTarget as LabelStatement).Label == gotoStatement.Label) { - gotoStatement.ReplaceWith(new ContinueStatement()); - return null; - } - } - - return null; - } - } -} diff --git a/ICSharpCode.Decompiler/Ast/Transforms/RestoreLoop.cs b/ICSharpCode.Decompiler/Ast/Transforms/RestoreLoop.cs deleted file mode 100644 index d177fc923..000000000 --- a/ICSharpCode.Decompiler/Ast/Transforms/RestoreLoop.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System; -using System.Linq; -using ICSharpCode.NRefactory.CSharp; - -namespace Decompiler.Transforms.Ast -{ - public class RestoreLoop: DepthFirstAstVisitor - { - public override object VisitForStatement(ForStatement forStatement, object data) - { - base.VisitForStatement(forStatement, data); - - // Restore loop initializer - if (!forStatement.Initializers.Any()) { - VariableDeclarationStatement varDeclr = forStatement.PrevSibling as VariableDeclarationStatement; - if (varDeclr != null) { - varDeclr.ReplaceWith(Statement.Null); - forStatement.Initializers.Add(varDeclr); - } - } - - // Restore loop condition - if (forStatement.Condition.IsNull && - forStatement.EmbeddedStatement.Children.Count() >= 3) - { - IfElseStatement condition = forStatement.EmbeddedStatement.Children.First() as IfElseStatement; - BreakStatement breakStmt = forStatement.EmbeddedStatement.Children.Skip(1).First() as BreakStatement; - LabelStatement label = forStatement.EmbeddedStatement.Children.Skip(2).First() as LabelStatement; - if (condition != null && breakStmt != null && label != null && - condition.TrueStatement.Children.Count() == 1) - { - GotoStatement gotoStmt = condition.TrueStatement.FirstChild as GotoStatement; - if (gotoStmt != null && gotoStmt.Label == label.Label) { - condition.Remove(); - breakStmt.Remove(); - forStatement.Condition = condition.Condition; - } - } - } - - // Restore loop condition (version 2) - if (forStatement.Condition.IsNull) { - IfElseStatement condition = forStatement.EmbeddedStatement.FirstChild as IfElseStatement; - if (condition != null && - condition.TrueStatement.Children.Any() && - condition.TrueStatement.FirstChild is BlockStatement && - condition.TrueStatement.Children.Count() == 1 && - condition.TrueStatement.FirstChild.FirstChild is BreakStatement && - condition.FalseStatement.Children.Any() && - condition.FalseStatement.FirstChild is BlockStatement && - condition.FalseStatement.Children.Count() == 0) - { - condition.Remove(); - forStatement.Condition = new UnaryOperatorExpression() { Expression = condition.Condition, Operator = UnaryOperatorType.Not }; - } - } - - // Restore loop iterator - if (forStatement.EmbeddedStatement.Children.Any() && - !forStatement.Iterators.Any()) - { - ExpressionStatement lastStmt = forStatement.EmbeddedStatement.LastChild as ExpressionStatement; - if (lastStmt != null && - (lastStmt.Expression is AssignmentExpression || lastStmt.Expression is UnaryOperatorExpression)) { - lastStmt.Remove(); - forStatement.Iterators.Add(lastStmt); - } - } - - return null; - } - } -} diff --git a/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs b/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs index 888269437..a559930cb 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs @@ -29,19 +29,6 @@ namespace Decompiler.Transforms { if (node == null) return; - for (int i = 0; i < 4; i++) { - context.CancellationToken.ThrowIfCancellationRequested(); - if (Options.ReduceAstJumps) { - node.AcceptVisitor(new Transforms.Ast.RemoveGotos(), null); - node.AcceptVisitor(new Transforms.Ast.RemoveDeadLabels(), null); - } - if (Options.ReduceAstLoops) { - node.AcceptVisitor(new Transforms.Ast.RestoreLoop(), null); - } - if (Options.ReduceAstOther) { - node.AcceptVisitor(new Transforms.Ast.RemoveEmptyElseBody(), null); - } - } foreach (var transform in CreatePipeline(context)) { context.CancellationToken.ThrowIfCancellationRequested(); diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index a18607f14..57d9af472 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -62,10 +62,6 @@ - - - - @@ -90,6 +86,7 @@ + @@ -98,7 +95,6 @@ - diff --git a/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs b/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs new file mode 100644 index 000000000..ba8c6b5cf --- /dev/null +++ b/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs @@ -0,0 +1,202 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Collections.Generic; +using System.Linq; +using Decompiler.ControlFlow; + +namespace Decompiler +{ + public class GotoRemoval + { + Dictionary parent = new Dictionary(); + Dictionary nextSibling = new Dictionary(); + + public bool RemoveGotos(ILBlock method) + { + // Build the navigation data + parent[method] = null; + foreach (ILNode node in method.GetSelfAndChildrenRecursive()) { + ILNode previousChild = null; + foreach (ILNode child in node.GetChildren()) { + Debug.Assert(!parent.ContainsKey(child)); + parent[child] = node; + if (previousChild != null) + nextSibling[previousChild] = child; + previousChild = child; + } + if (previousChild != null) + nextSibling[previousChild] = null; + } + + // Simplify gotos + bool modified = false; + foreach (ILExpression gotoExpr in method.GetSelfAndChildrenRecursive().Where(e => e.Code == ILCode.Br || e.Code == ILCode.Leave)) { + modified |= TrySimplifyGoto(gotoExpr); + } + + // Remove dead lables and nops + HashSet liveLabels = new HashSet(method.GetSelfAndChildrenRecursive().SelectMany(e => e.GetBranchTargets())); + foreach(ILBlock block in method.GetSelfAndChildrenRecursive().ToList()) { + int oldBodyCount = block.Body.Count; + block.Body = block.Body.Where(n => !n.Matches(ILCode.Nop) && !(n is ILLabel && !liveLabels.Contains((ILLabel)n))).ToList(); + modified |= block.Body.Count < oldBodyCount; + } + + // Remove redundant continue + foreach(ILWhileLoop loop in method.GetSelfAndChildrenRecursive()) { + var body = loop.BodyBlock.Body; + if (body.Count > 0 && body.Last().Matches(ILCode.LoopContinue)) { + body.RemoveAt(body.Count - 1); + } + } + + // Remove redundant return + if (method.Body.Count > 0 && method.Body.Last().Matches(ILCode.Ret) && ((ILExpression)method.Body.Last()).Arguments.Count == 0) { + method.Body.RemoveAt(method.Body.Count - 1); + } + + return modified; + } + + bool TrySimplifyGoto(ILExpression gotoExpr) + { + Debug.Assert(gotoExpr.Code == ILCode.Br || gotoExpr.Code == ILCode.Leave); + Debug.Assert(gotoExpr.Prefixes == null); + Debug.Assert(gotoExpr.Operand != null); + + ILExpression target = Enter(gotoExpr, new HashSet()); + if (target == null) + return false; + + if (target == Exit(gotoExpr, new HashSet())) { + gotoExpr.Code = ILCode.Nop; + gotoExpr.Operand = null; + target.ILRanges.AddRange(gotoExpr.ILRanges); + gotoExpr.ILRanges.Clear(); + return true; + } + + ILWhileLoop loop = null; + ILNode current = gotoExpr; + while(loop == null && current != null) { + current = parent[current]; + loop = current as ILWhileLoop; + } + + if (loop != null && target == Exit(loop, new HashSet())) { + gotoExpr.Code = ILCode.LoopBreak; + gotoExpr.Operand = null; + return true; + } + + if (loop != null && target == Enter(loop, new HashSet())) { + gotoExpr.Code = ILCode.LoopContinue; + gotoExpr.Operand = null; + return true; + } + + return false; + } + + /// + /// Get the first expression to be excecuted if the instruction pointer is at the start of the given node + /// + ILExpression Enter(ILNode node, HashSet visitedNodes) + { + if (node == null) + throw new ArgumentNullException(); + + if (!visitedNodes.Add(node)) + return null; // Infinite loop + + ILLabel label = node as ILLabel; + if (label != null) { + return Exit(label, visitedNodes); + } + + ILExpression expr = node as ILExpression; + if (expr != null) { + if (expr.Code == ILCode.Br || expr.Code == ILCode.Leave) { + return Enter((ILLabel)expr.Operand, visitedNodes); + } else if (expr.Code == ILCode.Nop) { + return Exit(expr, visitedNodes); + } else { + return expr; + } + } + + ILBlock block = node as ILBlock; + if (block != null) { + if (block.EntryGoto != null) { + return Enter(block.EntryGoto, visitedNodes); + } else if (block.Body.Count > 0) { + return Enter(block.Body[0], visitedNodes); + } else { + return Exit(block, visitedNodes); + } + } + + ILCondition cond = node as ILCondition; + if (cond != null) { + return cond.Condition; + } + + ILWhileLoop loop = node as ILWhileLoop; + if (loop != null) { + if (loop.Condition != null) { + return loop.Condition; + } else { + return Enter(loop.BodyBlock, visitedNodes); + } + } + + ILTryCatchBlock tryCatch = node as ILTryCatchBlock; + if (tryCatch != null) { + return Enter(tryCatch.TryBlock, visitedNodes); + } + + ILSwitch ilSwitch = node as ILSwitch; + if (ilSwitch != null) { + return ilSwitch.Condition; + } + + throw new NotSupportedException(node.GetType().ToString()); + } + + /// + /// Get the first expression to be excecuted if the instruction pointer is at the end of the given node + /// + ILExpression Exit(ILNode node, HashSet visitedNodes) + { + if (node == null) + throw new ArgumentNullException(); + + ILNode nodeParent = parent[node]; + if (nodeParent == null) + return null; // Exited main body + + if (nodeParent is ILBlock) { + ILNode nextNode = nextSibling[node]; + if (nextNode != null) { + return Enter(nextNode, visitedNodes); + } else { + return Exit(nodeParent, visitedNodes); + } + } + + if (nodeParent is ILCondition || + nodeParent is ILTryCatchBlock || + nodeParent is ILSwitch) + { + return Exit(nodeParent, visitedNodes); + } + + if (nodeParent is ILWhileLoop) { + return Enter(nodeParent, visitedNodes); + } + + throw new NotSupportedException(nodeParent.GetType().ToString()); + } + } +} diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs index 23971feb8..b53d07413 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -2,10 +2,10 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; - using ICSharpCode.Decompiler.FlowAnalysis; using Mono.Cecil; using Mono.Cecil.Cil; +using Mono.CSharp; namespace Decompiler.ControlFlow { @@ -16,8 +16,7 @@ namespace Decompiler.ControlFlow FindLoops, FindConditions, FlattenNestedMovableBlocks, - SimpleGotoRemoval, - RemoveDeadLabels, + GotoRemoval, HandleArrayInitializers, TypeInference, None @@ -63,19 +62,16 @@ namespace Decompiler.ControlFlow block.Body = FindConditions(new HashSet(graph.Nodes.Skip(3)), graph.EntryPoint); } - AnalyseLabels(method); - foreach(ILBlock block in method.GetSelfAndChildrenRecursive().ToList()) { - ReturnOptimizations(block); - } +// AnalyseLabels(method); +// foreach(ILBlock block in method.GetSelfAndChildrenRecursive().ToList()) { +// ReturnOptimizations(block); +// } if (abortBeforeStep == ILAstOptimizationStep.FlattenNestedMovableBlocks) return; FlattenBasicBlocks(method); - if (abortBeforeStep == ILAstOptimizationStep.SimpleGotoRemoval) return; - SimpleGotoRemoval(method); - - if (abortBeforeStep == ILAstOptimizationStep.RemoveDeadLabels) return; - RemoveDeadLabels(method); + if (abortBeforeStep == ILAstOptimizationStep.GotoRemoval) return; + new GotoRemoval().RemoveGotos(method); if (abortBeforeStep == ILAstOptimizationStep.HandleArrayInitializers) return; ArrayInitializers.Transform(method); @@ -494,33 +490,47 @@ namespace Decompiler.ControlFlow { HashSet loopContents = FindLoopContent(scope, node); - ILWhileLoop loop = new ILWhileLoop(); - - ILBasicBlock basicBlock = node.UserData as ILBasicBlock; + ILBasicBlock basicBlock = (ILBasicBlock)node.UserData; ILExpression branchExpr = null; ILLabel trueLabel = null; ILLabel falseLabel = null; - if(basicBlock != null && IsConditionalBranch(basicBlock, ref branchExpr, ref trueLabel, ref falseLabel)) { + if(IsConditionalBranch(basicBlock, ref branchExpr, ref trueLabel, ref falseLabel)) { loopContents.Remove(node); scope.Remove(node); branchExpr.Operand = null; // Do not keep label alive - // Use loop to implement condition - loop.Condition = branchExpr; - loop.PreLoopLabel = basicBlock.EntryLabel; - loop.PostLoopGoto = new ILExpression(ILCode.Br, falseLabel); - loop.BodyBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, trueLabel) }; + // Use loop to implement the condition + result.Add(new ILBasicBlock() { + EntryLabel = basicBlock.EntryLabel, + Body = new List() { + new ILWhileLoop() { + Condition = branchExpr, + BodyBlock = new ILBlock() { + EntryGoto = new ILExpression(ILCode.Br, trueLabel), + Body = FindLoops(loopContents, node, true) + } + }, + new ILExpression(ILCode.Br, falseLabel) + }, + FallthoughGoto = null + }); } else { - // Give the block some explicit entry point - ILLabel entryLabel = new ILLabel() { Name = "Loop_" + (nextLabelIndex++) }; - loop.BodyBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, entryLabel) }; - ((ILBasicBlock)node.UserData).Body.Insert(0, entryLabel); + result.Add(new ILBasicBlock() { + EntryLabel = new ILLabel() { Name = "Loop_" + (nextLabelIndex++) }, + Body = new List() { + new ILWhileLoop() { + BodyBlock = new ILBlock() { + EntryGoto = new ILExpression(ILCode.Br, basicBlock.EntryLabel), + Body = FindLoops(loopContents, node, true) + } + }, + }, + FallthoughGoto = null + }); } - loop.BodyBlock.Body = FindLoops(loopContents, node, true); // Move the content into loop block scope.ExceptWith(loopContents); - result.Add(loop); } // Using the dominator tree should ensure we find the the widest loop first @@ -784,45 +794,14 @@ namespace Decompiler.ControlFlow } } } - - void SimpleGotoRemoval(ILBlock ast) - { - // TODO: Assign IL ranges from br to something else - 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.Code == ILCode.Br)) { - // 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) + } + + public static class ILAstOptimizerExtensionMethods + { + public static bool Matches(this ILNode node, ILCode code) { - 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++; - } - } - } + ILExpression expr = node as ILExpression; + return expr != null && expr.Prefixes == null && expr.Code == code; } } } diff --git a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs index 24fb62672..7b4c5c82b 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs @@ -360,27 +360,19 @@ namespace Decompiler public class ILWhileLoop : ILNode { - public ILLabel PreLoopLabel; // Label allowing to jump to condition public ILExpression Condition; - public ILBlock BodyBlock; // BodyBlock.EntryGoto performs the goto for a met condition - public ILExpression PostLoopGoto; // Performs the goto for a failed condition + public ILBlock BodyBlock; public override IEnumerable GetChildren() { - if (this.PreLoopLabel != null) - yield return this.PreLoopLabel; if (this.Condition != null) yield return this.Condition; if (this.BodyBlock != null) yield return this.BodyBlock; - if (this.PostLoopGoto != null) - yield return this.PostLoopGoto; } public override void WriteTo(ITextOutput output) { - if (this.PreLoopLabel != null) - this.PreLoopLabel.WriteTo(output); output.WriteLine(""); output.Write("loop ("); if (this.Condition != null) @@ -390,8 +382,6 @@ namespace Decompiler this.BodyBlock.WriteTo(output); output.Unindent(); output.WriteLine("}"); - if (this.PostLoopGoto != null) - this.PostLoopGoto.WriteTo(output); } } diff --git a/ICSharpCode.Decompiler/ILAst/ILCodes.cs b/ICSharpCode.Decompiler/ILAst/ILCodes.cs index 75f9b1239..4871d0f40 100644 --- a/ICSharpCode.Decompiler/ILAst/ILCodes.cs +++ b/ICSharpCode.Decompiler/ILAst/ILCodes.cs @@ -260,6 +260,8 @@ namespace Decompiler BrLogicOr, InitArray, // Array Initializer TernaryOp, // ?: + LoopBreak, + LoopContinue, Pattern // used for ILAst pattern nodes } diff --git a/ICSharpCode.Decompiler/Options.cs b/ICSharpCode.Decompiler/Options.cs deleted file mode 100644 index 3d8e6e584..000000000 --- a/ICSharpCode.Decompiler/Options.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace Decompiler -{ - public static class Options - { - public static readonly bool NodeComments = false; - public static readonly bool ReduceLoops = true; - public static readonly bool ReduceConditonals = true; - public static readonly bool ReduceAstJumps = true; - public static readonly bool ReduceAstLoops = true; - public static readonly bool ReduceAstOther = true; - } -} From effe892409e5f74f5898b1dda6f57183d8970ef0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Srbeck=C3=BD?= Date: Sun, 27 Feb 2011 16:55:08 +0000 Subject: [PATCH 3/5] Duplicate return statements --- ICSharpCode.Decompiler/ILAst/GotoRemoval.cs | 26 ++-- .../ILAst/ILAstOptimizer.cs | 140 +++++++----------- 2 files changed, 70 insertions(+), 96 deletions(-) diff --git a/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs b/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs index ba8c6b5cf..365dc4464 100644 --- a/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs +++ b/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs @@ -12,7 +12,7 @@ namespace Decompiler Dictionary parent = new Dictionary(); Dictionary nextSibling = new Dictionary(); - public bool RemoveGotos(ILBlock method) + public void RemoveGotos(ILBlock method) { // Build the navigation data parent[method] = null; @@ -30,33 +30,33 @@ namespace Decompiler } // Simplify gotos - bool modified = false; foreach (ILExpression gotoExpr in method.GetSelfAndChildrenRecursive().Where(e => e.Code == ILCode.Br || e.Code == ILCode.Leave)) { - modified |= TrySimplifyGoto(gotoExpr); + TrySimplifyGoto(gotoExpr); } // Remove dead lables and nops - HashSet liveLabels = new HashSet(method.GetSelfAndChildrenRecursive().SelectMany(e => e.GetBranchTargets())); - foreach(ILBlock block in method.GetSelfAndChildrenRecursive().ToList()) { - int oldBodyCount = block.Body.Count; - block.Body = block.Body.Where(n => !n.Matches(ILCode.Nop) && !(n is ILLabel && !liveLabels.Contains((ILLabel)n))).ToList(); - modified |= block.Body.Count < oldBodyCount; - } + RemoveDeadLabels(method); // Remove redundant continue foreach(ILWhileLoop loop in method.GetSelfAndChildrenRecursive()) { var body = loop.BodyBlock.Body; - if (body.Count > 0 && body.Last().Matches(ILCode.LoopContinue)) { + if (body.Count > 0 && body.Last().Match(ILCode.LoopContinue)) { body.RemoveAt(body.Count - 1); } } // Remove redundant return - if (method.Body.Count > 0 && method.Body.Last().Matches(ILCode.Ret) && ((ILExpression)method.Body.Last()).Arguments.Count == 0) { + if (method.Body.Count > 0 && method.Body.Last().Match(ILCode.Ret) && ((ILExpression)method.Body.Last()).Arguments.Count == 0) { method.Body.RemoveAt(method.Body.Count - 1); } - - return modified; + } + + public static void RemoveDeadLabels(ILNode method) + { + HashSet liveLabels = new HashSet(method.GetSelfAndChildrenRecursive().SelectMany(e => e.GetBranchTargets())); + foreach(ILBlock block in method.GetSelfAndChildrenRecursive().ToList()) { + block.Body = block.Body.Where(n => !n.Match(ILCode.Nop) && !(n is ILLabel && !liveLabels.Contains((ILLabel)n))).ToList(); + } } bool TrySimplifyGoto(ILExpression gotoExpr) diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs index b53d07413..cb455e8dc 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -17,6 +17,7 @@ namespace Decompiler.ControlFlow FindConditions, FlattenNestedMovableBlocks, GotoRemoval, + DuplicateReturns, HandleArrayInitializers, TypeInference, None @@ -62,17 +63,16 @@ namespace Decompiler.ControlFlow block.Body = FindConditions(new HashSet(graph.Nodes.Skip(3)), graph.EntryPoint); } -// AnalyseLabels(method); -// foreach(ILBlock block in method.GetSelfAndChildrenRecursive().ToList()) { -// ReturnOptimizations(block); -// } - if (abortBeforeStep == ILAstOptimizationStep.FlattenNestedMovableBlocks) return; FlattenBasicBlocks(method); if (abortBeforeStep == ILAstOptimizationStep.GotoRemoval) return; new GotoRemoval().RemoveGotos(method); + if (abortBeforeStep == ILAstOptimizationStep.DuplicateReturns) return; + DuplicateReturnStatements(method); + GotoRemoval.RemoveDeadLabels(method); + if (abortBeforeStep == ILAstOptimizationStep.HandleArrayInitializers) return; ArrayInitializers.Transform(method); @@ -325,92 +325,48 @@ namespace Decompiler.ControlFlow return false; } - void ReturnOptimizations(ILBlock block) - { - bool modified; - do { - modified = false; - for (int i = 0; i < block.Body.Count;) { - ILBasicBlock bb = block.Body[i] as ILBasicBlock; - // TODO: Isn't basicblock? - if (bb != null && TryDuplicateFollowingReturn(block.Body, bb)) { - modified = true; - continue; - } - i++; - } - } while(modified); - } - - bool TryDuplicateFollowingReturn(List scope, ILBasicBlock head) + void DuplicateReturnStatements(ILBlock method) { - bool modified = false; - if (head.FallthoughGoto != null) { - ILLabel fallLabel = (ILLabel)head.FallthoughGoto.Operand; - ILLabel dupLabel = null; - if (labelGlobalRefCount[fallLabel] > 1 && - TryDuplicateReturn(scope, fallLabel, ref dupLabel)) { - labelGlobalRefCount[fallLabel]--; - labelGlobalRefCount[dupLabel] = 1; - head.FallthoughGoto.Operand = dupLabel; - modified = true; - } - } - if (head.Body.Count == 1) { - ILExpression brExpr = head.Body[0] as ILExpression; - if (brExpr != null && brExpr.Operand is ILLabel) { - ILLabel brLabel = (ILLabel)brExpr.Operand; - ILLabel dupLabel = null; - if (labelGlobalRefCount[brLabel] > 1 && - TryDuplicateReturn(scope, brLabel, ref dupLabel)) { - labelGlobalRefCount[brLabel]--; - labelGlobalRefCount[dupLabel] = 1; - brExpr.Operand = dupLabel; - modified = true; + Dictionary nextSibling = new Dictionary(); + + // Build navigation data + foreach(ILBlock block in method.GetSelfAndChildrenRecursive()) { + for (int i = 0; i < block.Body.Count - 1; i++) { + ILLabel curr = block.Body[i] as ILLabel; + if (curr != null) { + nextSibling[curr] = block.Body[i + 1]; } } } - return modified; - } - - bool TryDuplicateReturn(List scope, ILLabel head, ref ILLabel newHead) - { - ILBasicBlock bb; - // TODO: mapping missing? - if (labelToBasicBlock.TryGetValue(head, out bb) && bb.Body.Count == 1 && !scope.Contains(bb)) { - ILExpression retExpr = bb.Body[0] as ILExpression; - if (retExpr != null && - retExpr.Prefixes == null && - retExpr.Code == ILCode.Ret) - { - if (retExpr.Arguments.Count == 0) { - newHead = new ILLabel() { Name = "Return_" + (nextLabelIndex++) }; - ILBasicBlock newBB = new ILBasicBlock() { - EntryLabel = newHead, - Body = { new ILExpression(ILCode.Ret, null) } - }; - scope.Add(newBB); - labelToBasicBlock[newHead] = newBB; - return true; - } else { - ILExpression argExpr = retExpr.Arguments[0] as ILExpression; - if (argExpr != null && - argExpr.Prefixes == null && - argExpr.Code == ILCode.Ldloc) + + // Duplicate returns + foreach(ILBlock block in method.GetSelfAndChildrenRecursive()) { + for (int i = 0; i < block.Body.Count; i++) { + ILLabel targetLabel; + if (block.Body[i].Match(ILCode.Br, out targetLabel) || + block.Body[i].Match(ILCode.Leave, out targetLabel)) + { + // Skip extra labels + while(nextSibling.ContainsKey(targetLabel) && nextSibling[targetLabel] is ILLabel) { + targetLabel = (ILLabel)nextSibling[targetLabel]; + } + + // Inline return statement + ILNode target; + ILExpression retExpr; + ILVariable locVar = null; + if (nextSibling.TryGetValue(targetLabel, out target) && + target.Match(ILCode.Ret, out retExpr) && + (retExpr.Arguments.Count == 0 || retExpr.Arguments.Single().Match(ILCode.Ldloc, out locVar))) { - newHead = new ILLabel() { Name = "Return_" + (nextLabelIndex++) }; - ILBasicBlock newBB = new ILBasicBlock() { - EntryLabel = newHead, - Body = { new ILExpression(ILCode.Ret, null, new ILExpression(ILCode.Ldloc, argExpr.Operand)) } - }; - scope.Add(newBB); - labelToBasicBlock[newHead] = newBB; - return true; + ILExpression dup = new ILExpression(ILCode.Ret, null); + if (locVar != null) + dup.Arguments = new List() { new ILExpression(ILCode.Ldloc, locVar) }; + block.Body[i] = dup; } } } } - return false; } ControlFlowGraph BuildGraph(List nodes, ILLabel entryLabel) @@ -798,10 +754,28 @@ namespace Decompiler.ControlFlow public static class ILAstOptimizerExtensionMethods { - public static bool Matches(this ILNode node, ILCode code) + public static bool Match(this ILNode node, ILCode code) { ILExpression expr = node as ILExpression; return expr != null && expr.Prefixes == null && expr.Code == code; } + + public static bool Match(this ILNode node, ILCode code, out ILExpression expr) + { + expr = node as ILExpression; + return expr != null && expr.Prefixes == null && expr.Code == code; + } + + public static bool Match(this ILNode node, ILCode code, out T operand) + { + ILExpression expr = node as ILExpression; + if (expr != null && expr.Prefixes == null && expr.Code == code) { + operand = (T)expr.Operand; + return true; + } else { + operand = default(T); + return false; + } + } } } From 46c99ddc44b27037ff2c721ec5f588f0c32589fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Srbeck=C3=BD?= Date: Sun, 27 Feb 2011 19:36:25 +0000 Subject: [PATCH 4/5] Performance improvement - use accumulator list for GetSelfAndChildrenRecursive --- ICSharpCode.Decompiler/ILAst/ILAstTypes.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs index 7b4c5c82b..ec5aeb2b1 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs @@ -18,7 +18,19 @@ namespace Decompiler { public IEnumerable GetSelfAndChildrenRecursive() where T: ILNode { - return TreeTraversal.PreOrder(this, c => c != null ? c.GetChildren() : null).OfType(); + List result = new List(16); + AccumulateSelfAndChildrenRecursive(result); + return result; + } + + void AccumulateSelfAndChildrenRecursive(List list) where T:ILNode + { + if (this is T) + list.Add((T)this); + foreach (ILNode node in this.GetChildren()) { + if (node != null) + node.AccumulateSelfAndChildrenRecursive(list); + } } public virtual IEnumerable GetChildren() From 3f4578f7f54e2d99475d8cc7e5c5e424c148ae95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Srbeck=C3=BD?= Date: Sun, 27 Feb 2011 20:39:13 +0000 Subject: [PATCH 5/5] Duplicate return statement with const int/bool. --- ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs index cb455e8dc..668644821 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -354,15 +354,18 @@ namespace Decompiler.ControlFlow // Inline return statement ILNode target; ILExpression retExpr; - ILVariable locVar = null; if (nextSibling.TryGetValue(targetLabel, out target) && - target.Match(ILCode.Ret, out retExpr) && - (retExpr.Arguments.Count == 0 || retExpr.Arguments.Single().Match(ILCode.Ldloc, out locVar))) + target.Match(ILCode.Ret, out retExpr)) { - ILExpression dup = new ILExpression(ILCode.Ret, null); - if (locVar != null) - dup.Arguments = new List() { new ILExpression(ILCode.Ldloc, locVar) }; - block.Body[i] = dup; + ILVariable locVar; + object constValue; + if (retExpr.Arguments.Count == 0) { + block.Body[i] = new ILExpression(ILCode.Ret, null); + } else if (retExpr.Arguments.Single().Match(ILCode.Ldloc, out locVar)) { + block.Body[i] = new ILExpression(ILCode.Ret, null, new ILExpression(ILCode.Ldloc, locVar)); + } else if (retExpr.Arguments.Single().Match(ILCode.Ldc_I4, out constValue)) { + block.Body[i] = new ILExpression(ILCode.Ret, null, new ILExpression(ILCode.Ldc_I4, constValue)); + } } } }