Browse Source

Rewritten goto removal.

Closes #32
Closes #18
pull/70/head
David Srbecký 15 years ago
parent
commit
1bc30662b0
  1. 13
      ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
  2. 3
      ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs
  3. 56
      ICSharpCode.Decompiler/Ast/Transforms/RemoveDeadLabels.cs
  4. 19
      ICSharpCode.Decompiler/Ast/Transforms/RemoveEmptyElseBody.cs
  5. 207
      ICSharpCode.Decompiler/Ast/Transforms/RemoveGotos.cs
  6. 73
      ICSharpCode.Decompiler/Ast/Transforms/RestoreLoop.cs
  7. 13
      ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs
  8. 6
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  9. 202
      ICSharpCode.Decompiler/ILAst/GotoRemoval.cs
  10. 105
      ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
  11. 12
      ICSharpCode.Decompiler/ILAst/ILAstTypes.cs
  12. 2
      ICSharpCode.Decompiler/ILAst/ILCodes.cs
  13. 14
      ICSharpCode.Decompiler/Options.cs

13
ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

@ -105,21 +105,18 @@ namespace Decompiler
} }
} else if (node is ILWhileLoop) { } else if (node is ILWhileLoop) {
ILWhileLoop ilLoop = (ILWhileLoop)node; ILWhileLoop ilLoop = (ILWhileLoop)node;
if (ilLoop.PreLoopLabel != null)
yield return TransformNode(ilLoop.PreLoopLabel).Single();
WhileStatement whileStmt = new WhileStatement() { WhileStatement whileStmt = new WhileStatement() {
Condition = ilLoop.Condition != null ? MakeBranchCondition(ilLoop.Condition) : new PrimitiveExpression(true), Condition = ilLoop.Condition != null ? MakeBranchCondition(ilLoop.Condition) : new PrimitiveExpression(true),
EmbeddedStatement = TransformBlock(ilLoop.BodyBlock) EmbeddedStatement = TransformBlock(ilLoop.BodyBlock)
}; };
yield return whileStmt; yield return whileStmt;
if (ilLoop.PostLoopGoto != null)
yield return (Statement)TransformExpression(ilLoop.PostLoopGoto);
} else if (node is ILCondition) { } else if (node is ILCondition) {
ILCondition conditionalNode = (ILCondition)node; ILCondition conditionalNode = (ILCondition)node;
bool hasFalseBlock = conditionalNode.FalseBlock.EntryGoto != null || conditionalNode.FalseBlock.Body.Count > 0;
yield return new Ast.IfElseStatement { yield return new Ast.IfElseStatement {
Condition = MakeBranchCondition(conditionalNode.Condition), Condition = MakeBranchCondition(conditionalNode.Condition),
TrueStatement = TransformBlock(conditionalNode.TrueBlock), TrueStatement = TransformBlock(conditionalNode.TrueBlock),
FalseStatement = TransformBlock(conditionalNode.FalseBlock) FalseStatement = hasFalseBlock ? TransformBlock(conditionalNode.FalseBlock) : null
}; };
} else if (node is ILSwitch) { } else if (node is ILSwitch) {
ILSwitch ilSwitch = (ILSwitch)node; ILSwitch ilSwitch = (ILSwitch)node;
@ -291,6 +288,10 @@ namespace Decompiler
TrueExpression = (Expression)TransformExpression(byteCode.Arguments[1]), TrueExpression = (Expression)TransformExpression(byteCode.Arguments[1]),
FalseExpression = (Expression)TransformExpression(byteCode.Arguments[2]), FalseExpression = (Expression)TransformExpression(byteCode.Arguments[2]),
}; };
case ILCode.LoopBreak:
return new Ast.BreakStatement();
case ILCode.LoopContinue:
return new Ast.ContinueStatement();
} }
List<Ast.Expression> args = TransformExpressionArguments(byteCode); List<Ast.Expression> args = TransformExpressionArguments(byteCode);
@ -568,7 +569,7 @@ namespace Decompiler
} else { } else {
return InlineAssembly(byteCode, args); 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.Localloc: return InlineAssembly(byteCode, args);
case Code.Mkrefany: return InlineAssembly(byteCode, args); case Code.Mkrefany: return InlineAssembly(byteCode, args);
case Code.Newobj: case Code.Newobj:

3
ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs

@ -204,8 +204,7 @@ namespace Decompiler.Transforms
Left = new Backreference("ident").ToExpression(), Left = new Backreference("ident").ToExpression(),
Operator = AssignmentOperatorType.Any, Operator = AssignmentOperatorType.Any,
Right = new AnyNode().ToExpression() Right = new AnyNode().ToExpression()
})).ToStatement(), })).ToStatement()
new ContinueStatement()
} }
}; };

56
ICSharpCode.Decompiler/Ast/Transforms/RemoveDeadLabels.cs

@ -1,56 +0,0 @@
using System;
using System.Collections.Generic;
using ICSharpCode.NRefactory.CSharp;
namespace Decompiler.Transforms.Ast
{
public class RemoveDeadLabels : DepthFirstAstVisitor<object, object>
{
List<string> usedLabels = new List<string>();
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;
}
}
}

19
ICSharpCode.Decompiler/Ast/Transforms/RemoveEmptyElseBody.cs

@ -1,19 +0,0 @@
using System;
using System.Linq;
using ICSharpCode.NRefactory.CSharp;
namespace Decompiler.Transforms.Ast
{
public class RemoveEmptyElseBody: DepthFirstAstVisitor<object, object>
{
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;
}
}
}

207
ICSharpCode.Decompiler/Ast/Transforms/RemoveGotos.cs

@ -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<object, object>
{
Stack<Statement> enteredLoops = new Stack<Statement>();
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;
}
}
}

73
ICSharpCode.Decompiler/Ast/Transforms/RestoreLoop.cs

@ -1,73 +0,0 @@
using System;
using System.Linq;
using ICSharpCode.NRefactory.CSharp;
namespace Decompiler.Transforms.Ast
{
public class RestoreLoop: DepthFirstAstVisitor<object, object>
{
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;
}
}
}

13
ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs

@ -29,19 +29,6 @@ namespace Decompiler.Transforms
{ {
if (node == null) if (node == null)
return; 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)) { foreach (var transform in CreatePipeline(context)) {
context.CancellationToken.ThrowIfCancellationRequested(); context.CancellationToken.ThrowIfCancellationRequested();

6
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -62,10 +62,6 @@
<Compile Include="Ast\Transforms\DelegateConstruction.cs" /> <Compile Include="Ast\Transforms\DelegateConstruction.cs" />
<Compile Include="Ast\Transforms\ReplaceMethodCallsWithOperators.cs" /> <Compile Include="Ast\Transforms\ReplaceMethodCallsWithOperators.cs" />
<Compile Include="Ast\Transforms\PushNegation.cs" /> <Compile Include="Ast\Transforms\PushNegation.cs" />
<Compile Include="Ast\Transforms\RemoveDeadLabels.cs" />
<Compile Include="Ast\Transforms\RemoveEmptyElseBody.cs" />
<Compile Include="Ast\Transforms\RemoveGotos.cs" />
<Compile Include="Ast\Transforms\RestoreLoop.cs" />
<Compile Include="Ast\Transforms\TransformationPipeline.cs" /> <Compile Include="Ast\Transforms\TransformationPipeline.cs" />
<Compile Include="Ast\Transforms\PatternStatementTransform.cs" /> <Compile Include="Ast\Transforms\PatternStatementTransform.cs" />
<Compile Include="CecilExtensions.cs" /> <Compile Include="CecilExtensions.cs" />
@ -90,6 +86,7 @@
<Compile Include="FlowAnalysis\TransformToSsa.cs" /> <Compile Include="FlowAnalysis\TransformToSsa.cs" />
<Compile Include="GraphVizGraph.cs" /> <Compile Include="GraphVizGraph.cs" />
<Compile Include="ILAst\ArrayInitializers.cs" /> <Compile Include="ILAst\ArrayInitializers.cs" />
<Compile Include="ILAst\GotoRemoval.cs" />
<Compile Include="ILAst\ILAstBuilder.cs" /> <Compile Include="ILAst\ILAstBuilder.cs" />
<Compile Include="ILAst\ILAstOptimizer.cs" /> <Compile Include="ILAst\ILAstOptimizer.cs" />
<Compile Include="ILAst\ILAstTypes.cs" /> <Compile Include="ILAst\ILAstTypes.cs" />
@ -98,7 +95,6 @@
<Compile Include="ILAst\Pattern.cs" /> <Compile Include="ILAst\Pattern.cs" />
<Compile Include="ILAst\TypeAnalysis.cs" /> <Compile Include="ILAst\TypeAnalysis.cs" />
<Compile Include="ITextOutput.cs" /> <Compile Include="ITextOutput.cs" />
<Compile Include="Options.cs" />
<Compile Include="PlainTextOutput.cs" /> <Compile Include="PlainTextOutput.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TextOutputWriter.cs" /> <Compile Include="TextOutputWriter.cs" />

202
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<ILNode, ILNode> parent = new Dictionary<ILNode, ILNode>();
Dictionary<ILNode, ILNode> nextSibling = new Dictionary<ILNode, ILNode>();
public bool RemoveGotos(ILBlock method)
{
// Build the navigation data
parent[method] = null;
foreach (ILNode node in method.GetSelfAndChildrenRecursive<ILNode>()) {
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<ILExpression>().Where(e => e.Code == ILCode.Br || e.Code == ILCode.Leave)) {
modified |= TrySimplifyGoto(gotoExpr);
}
// Remove dead lables and nops
HashSet<ILLabel> liveLabels = new HashSet<ILLabel>(method.GetSelfAndChildrenRecursive<ILExpression>().SelectMany(e => e.GetBranchTargets()));
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().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<ILWhileLoop>()) {
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<ILNode>());
if (target == null)
return false;
if (target == Exit(gotoExpr, new HashSet<ILNode>())) {
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<ILNode>())) {
gotoExpr.Code = ILCode.LoopBreak;
gotoExpr.Operand = null;
return true;
}
if (loop != null && target == Enter(loop, new HashSet<ILNode>())) {
gotoExpr.Code = ILCode.LoopContinue;
gotoExpr.Operand = null;
return true;
}
return false;
}
/// <summary>
/// Get the first expression to be excecuted if the instruction pointer is at the start of the given node
/// </summary>
ILExpression Enter(ILNode node, HashSet<ILNode> 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());
}
/// <summary>
/// Get the first expression to be excecuted if the instruction pointer is at the end of the given node
/// </summary>
ILExpression Exit(ILNode node, HashSet<ILNode> 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());
}
}
}

105
ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs

@ -2,10 +2,10 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using ICSharpCode.Decompiler.FlowAnalysis; using ICSharpCode.Decompiler.FlowAnalysis;
using Mono.Cecil; using Mono.Cecil;
using Mono.Cecil.Cil; using Mono.Cecil.Cil;
using Mono.CSharp;
namespace Decompiler.ControlFlow namespace Decompiler.ControlFlow
{ {
@ -16,8 +16,7 @@ namespace Decompiler.ControlFlow
FindLoops, FindLoops,
FindConditions, FindConditions,
FlattenNestedMovableBlocks, FlattenNestedMovableBlocks,
SimpleGotoRemoval, GotoRemoval,
RemoveDeadLabels,
HandleArrayInitializers, HandleArrayInitializers,
TypeInference, TypeInference,
None None
@ -63,19 +62,16 @@ namespace Decompiler.ControlFlow
block.Body = FindConditions(new HashSet<ControlFlowNode>(graph.Nodes.Skip(3)), graph.EntryPoint); block.Body = FindConditions(new HashSet<ControlFlowNode>(graph.Nodes.Skip(3)), graph.EntryPoint);
} }
AnalyseLabels(method); // AnalyseLabels(method);
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().ToList()) { // foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().ToList()) {
ReturnOptimizations(block); // ReturnOptimizations(block);
} // }
if (abortBeforeStep == ILAstOptimizationStep.FlattenNestedMovableBlocks) return; if (abortBeforeStep == ILAstOptimizationStep.FlattenNestedMovableBlocks) return;
FlattenBasicBlocks(method); FlattenBasicBlocks(method);
if (abortBeforeStep == ILAstOptimizationStep.SimpleGotoRemoval) return; if (abortBeforeStep == ILAstOptimizationStep.GotoRemoval) return;
SimpleGotoRemoval(method); new GotoRemoval().RemoveGotos(method);
if (abortBeforeStep == ILAstOptimizationStep.RemoveDeadLabels) return;
RemoveDeadLabels(method);
if (abortBeforeStep == ILAstOptimizationStep.HandleArrayInitializers) return; if (abortBeforeStep == ILAstOptimizationStep.HandleArrayInitializers) return;
ArrayInitializers.Transform(method); ArrayInitializers.Transform(method);
@ -494,33 +490,47 @@ namespace Decompiler.ControlFlow
{ {
HashSet<ControlFlowNode> loopContents = FindLoopContent(scope, node); HashSet<ControlFlowNode> loopContents = FindLoopContent(scope, node);
ILWhileLoop loop = new ILWhileLoop(); ILBasicBlock basicBlock = (ILBasicBlock)node.UserData;
ILBasicBlock basicBlock = node.UserData as ILBasicBlock;
ILExpression branchExpr = null; ILExpression branchExpr = null;
ILLabel trueLabel = null; ILLabel trueLabel = null;
ILLabel falseLabel = 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); loopContents.Remove(node);
scope.Remove(node); scope.Remove(node);
branchExpr.Operand = null; // Do not keep label alive branchExpr.Operand = null; // Do not keep label alive
// Use loop to implement condition // Use loop to implement the condition
loop.Condition = branchExpr; result.Add(new ILBasicBlock() {
loop.PreLoopLabel = basicBlock.EntryLabel; EntryLabel = basicBlock.EntryLabel,
loop.PostLoopGoto = new ILExpression(ILCode.Br, falseLabel); Body = new List<ILNode>() {
loop.BodyBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, trueLabel) }; 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 { } else {
// Give the block some explicit entry point result.Add(new ILBasicBlock() {
ILLabel entryLabel = new ILLabel() { Name = "Loop_" + (nextLabelIndex++) }; EntryLabel = new ILLabel() { Name = "Loop_" + (nextLabelIndex++) },
loop.BodyBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, entryLabel) }; Body = new List<ILNode>() {
((ILBasicBlock)node.UserData).Body.Insert(0, entryLabel); 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 // Move the content into loop block
scope.ExceptWith(loopContents); scope.ExceptWith(loopContents);
result.Add(loop);
} }
// Using the dominator tree should ensure we find the the widest loop first // 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<ILBlock>().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
{ {
HashSet<ILLabel> liveLabels = new HashSet<ILLabel>(ast.GetSelfAndChildrenRecursive<ILExpression>().SelectMany(e => e.GetBranchTargets())); public static bool Matches(this ILNode node, ILCode code)
var blocks = ast.GetSelfAndChildrenRecursive<ILBlock>().ToList(); {
foreach(ILBlock block in blocks) { ILExpression expr = node as ILExpression;
for (int i = 0; i < block.Body.Count;) { return expr != null && expr.Prefixes == null && expr.Code == code;
ILLabel label = block.Body[i] as ILLabel;
if (label != null && !liveLabels.Contains(label)) {
block.Body.RemoveAt(i);
} else {
i++;
}
}
}
} }
} }
} }

12
ICSharpCode.Decompiler/ILAst/ILAstTypes.cs

@ -360,27 +360,19 @@ namespace Decompiler
public class ILWhileLoop : ILNode public class ILWhileLoop : ILNode
{ {
public ILLabel PreLoopLabel; // Label allowing to jump to condition
public ILExpression Condition; public ILExpression Condition;
public ILBlock BodyBlock; // BodyBlock.EntryGoto performs the goto for a met condition public ILBlock BodyBlock;
public ILExpression PostLoopGoto; // Performs the goto for a failed condition
public override IEnumerable<ILNode> GetChildren() public override IEnumerable<ILNode> GetChildren()
{ {
if (this.PreLoopLabel != null)
yield return this.PreLoopLabel;
if (this.Condition != null) if (this.Condition != null)
yield return this.Condition; yield return this.Condition;
if (this.BodyBlock != null) if (this.BodyBlock != null)
yield return this.BodyBlock; yield return this.BodyBlock;
if (this.PostLoopGoto != null)
yield return this.PostLoopGoto;
} }
public override void WriteTo(ITextOutput output) public override void WriteTo(ITextOutput output)
{ {
if (this.PreLoopLabel != null)
this.PreLoopLabel.WriteTo(output);
output.WriteLine(""); output.WriteLine("");
output.Write("loop ("); output.Write("loop (");
if (this.Condition != null) if (this.Condition != null)
@ -390,8 +382,6 @@ namespace Decompiler
this.BodyBlock.WriteTo(output); this.BodyBlock.WriteTo(output);
output.Unindent(); output.Unindent();
output.WriteLine("}"); output.WriteLine("}");
if (this.PostLoopGoto != null)
this.PostLoopGoto.WriteTo(output);
} }
} }

2
ICSharpCode.Decompiler/ILAst/ILCodes.cs

@ -260,6 +260,8 @@ namespace Decompiler
BrLogicOr, BrLogicOr,
InitArray, // Array Initializer InitArray, // Array Initializer
TernaryOp, // ?: TernaryOp, // ?:
LoopBreak,
LoopContinue,
Pattern // used for ILAst pattern nodes Pattern // used for ILAst pattern nodes
} }

14
ICSharpCode.Decompiler/Options.cs

@ -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;
}
}
Loading…
Cancel
Save