Browse Source

Flatten temporary blocks;

Simple goto removal;
Remove dead labels
pull/10/head
David Srbecký 14 years ago
parent
commit
4f0c70fce3
  1. 2
      ICSharpCode.Decompiler/Ast/AstBuilder.cs
  2. 5
      ICSharpCode.Decompiler/Ast/AstMetodBodyBuilder.cs
  3. 56
      ICSharpCode.Decompiler/Ast/Transforms/RemoveDeadLabels.cs
  4. 1
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  5. 150
      ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
  6. 31
      ICSharpCode.Decompiler/ILAst/ILAstTypes.cs

2
ICSharpCode.Decompiler/Ast/AstBuilder.cs

@ -20,7 +20,7 @@ namespace Decompiler @@ -20,7 +20,7 @@ namespace Decompiler
for (int i = 0; i < 4; i++) {
if (Options.ReduceAstJumps) {
//astCompileUnit.AcceptVisitor(new Transforms.Ast.RemoveGotos(), null);
astCompileUnit.AcceptVisitor(new Transforms.Ast.RemoveDeadLabels(), null);
//astCompileUnit.AcceptVisitor(new Transforms.Ast.RemoveDeadLabels(), null);
}
if (Options.ReduceAstLoops) {
//astCompileUnit.AcceptVisitor(new Transforms.Ast.RestoreLoop(), null);

5
ICSharpCode.Decompiler/Ast/AstMetodBodyBuilder.cs

@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Ast = ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.CSharp;
using Cecil = Mono.Cecil;
@ -48,7 +49,7 @@ namespace Decompiler @@ -48,7 +49,7 @@ namespace Decompiler
List<ILNode> body = new ILAstBuilder().Build(methodDef, true);
ILAstOptimizer bodyGraph = new ILAstOptimizer();
bodyGraph.Optimize(body);
bodyGraph.Optimize(ref body);
List<string> intNames = new List<string>(new string[] {"i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t"});
Dictionary<string, int> typeNames = new Dictionary<string, int>();
@ -385,7 +386,7 @@ namespace Decompiler @@ -385,7 +386,7 @@ namespace Decompiler
throw new NotImplementedException();
#endregion
#region Branching
case Code.Br: return branchCommand;
case Code.Br: return new Ast.GotoStatement(((ILLabel)byteCode.Operand).Name);
case Code.Brfalse: return new Ast.IfElseStatement(new Ast.UnaryOperatorExpression(UnaryOperatorType.Not, arg1), branchCommand);
case Code.Brtrue: return new Ast.IfElseStatement(arg1, branchCommand);
case Code.Beq: return new Ast.IfElseStatement(new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, arg2), branchCommand);

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

@ -1,56 +0,0 @@ @@ -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;
}
}
}

1
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -56,7 +56,6 @@ @@ -56,7 +56,6 @@
<Compile Include="Ast\TextOutputFormatter.cs" />
<Compile Include="Ast\Transforms\Idioms.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" />

150
ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs

@ -10,7 +10,21 @@ namespace Decompiler.ControlFlow @@ -10,7 +10,21 @@ namespace Decompiler.ControlFlow
{
public class ILAstOptimizer
{
public void Optimize(List<ILNode> ast)
public void Optimize(ref List<ILNode> ast)
{
OptimizeRecursive(ref ast);
// Provide a container for the algorithms below
ILBlock astBlock = new ILBlock(ast);
FlattenNestedMovableBlocks(astBlock);
SimpleGotoRemoval(astBlock);
RemoveDeadLabels(astBlock);
ast = astBlock.Body;
}
void OptimizeRecursive(ref List<ILNode> ast)
{
ILLabel entryLabel;
List<ILTryCatchBlock> tryCatchBlocks = ast.OfType<ILTryCatchBlock>().ToList();
@ -31,15 +45,26 @@ namespace Decompiler.ControlFlow @@ -31,15 +45,26 @@ namespace Decompiler.ControlFlow
// Recursively optimze try-cath blocks
foreach(ILTryCatchBlock tryCatchBlock in tryCatchBlocks) {
Optimize(tryCatchBlock.TryBlock.Body);
Optimize(ref tryCatchBlock.TryBlock.Body);
foreach(ILTryCatchBlock.CatchBlock catchBlock in tryCatchBlock.CatchBlocks) {
Optimize(catchBlock.Body);
Optimize(ref catchBlock.Body);
}
Optimize(ref tryCatchBlock.FinallyBlock.Body);
}
Optimize(tryCatchBlock.FinallyBlock.Body);
// Sort the nodes in the original order
ast = ast.OrderBy(n => n.GetSelfAndChildrenRecursive<ILMoveAbleBlock>().First().OriginalOrder).ToList();
ast.Insert(0, new ILExpression(OpCodes.Br, entryLabel));
}
class ILMoveAbleBlock: ILBlock
{
public int OriginalOrder;
}
int nextLabelIndex = 0;
int nextBlockIndex = 0;
/// <summary>
/// Group input into a set of blocks that can be later arbitraliby schufled.
@ -50,9 +75,9 @@ namespace Decompiler.ControlFlow @@ -50,9 +75,9 @@ namespace Decompiler.ControlFlow
{
List<ILNode> blocks = new List<ILNode>();
ILBlock block = new ILBlock();
ILMoveAbleBlock block = new ILMoveAbleBlock() { OriginalOrder = (nextBlockIndex++) };
blocks.Add(block);
entryLabel = new ILLabel() { Name = "Block_" + (nextLabelIndex++) };
entryLabel = new ILLabel() { Name = "Block_" + block.OriginalOrder };
block.Body.Add(entryLabel);
if (ast.Count == 0)
@ -71,14 +96,14 @@ namespace Decompiler.ControlFlow @@ -71,14 +96,14 @@ namespace Decompiler.ControlFlow
(currNode is ILExpression) && ((ILExpression)currNode).OpCode.IsBranch())
{
ILBlock lastBlock = block;
block = new ILBlock();
block = new ILMoveAbleBlock() { OriginalOrder = (nextBlockIndex++) };
blocks.Add(block);
// Explicit branch from one block to other
// (unless the last expression was unconditional branch)
if (!(lastNode is ILExpression) || ((ILExpression)lastNode).OpCode.CanFallThough()) {
ILLabel blockLabel = new ILLabel() { Name = "Block_" + (nextLabelIndex++) };
lastBlock.Body.Add(new ILExpression(OpCodes.Br_S, blockLabel));
ILLabel blockLabel = new ILLabel() { Name = "Block_" + block.OriginalOrder };
lastBlock.Body.Add(new ILExpression(OpCodes.Br, blockLabel));
block.Body.Add(blockLabel);
}
}
@ -110,7 +135,7 @@ namespace Decompiler.ControlFlow @@ -110,7 +135,7 @@ namespace Decompiler.ControlFlow
cfNode.UserData = node;
// Find all contained labels
foreach(ILLabel label in node.GetChildrenRecursive<ILLabel>()) {
foreach(ILLabel label in node.GetSelfAndChildrenRecursive<ILLabel>()) {
labelToCfNode[label] = cfNode;
}
}
@ -126,7 +151,7 @@ namespace Decompiler.ControlFlow @@ -126,7 +151,7 @@ namespace Decompiler.ControlFlow
ControlFlowNode source = astNodeToCfNode[node];
// Find all branches
foreach(ILExpression child in node.GetChildrenRecursive<ILExpression>()) {
foreach(ILExpression child in node.GetSelfAndChildrenRecursive<ILExpression>()) {
IEnumerable<ILLabel> targets = child.GetBranchTargets();
if (targets != null) {
foreach(ILLabel target in targets) {
@ -145,7 +170,7 @@ namespace Decompiler.ControlFlow @@ -145,7 +170,7 @@ namespace Decompiler.ControlFlow
return new ControlFlowGraph(cfNodes.ToArray());
}
static List<ILNode> FindLoops(HashSet<ControlFlowNode> body, ControlFlowNode entryPoint)
static List<ILNode> FindLoops(HashSet<ControlFlowNode> nodes, ControlFlowNode entryPoint)
{
List<ILNode> result = new List<ILNode>();
@ -154,15 +179,15 @@ namespace Decompiler.ControlFlow @@ -154,15 +179,15 @@ namespace Decompiler.ControlFlow
while(agenda.Count > 0) {
ControlFlowNode node = agenda.Dequeue();
if (body.Contains(node)
if (nodes.Contains(node)
&& node.DominanceFrontier.Contains(node)
&& node != entryPoint)
{
HashSet<ControlFlowNode> loopContents = new HashSet<ControlFlowNode>();
FindLoopContents(body, loopContents, node, node);
FindLoopContents(nodes, loopContents, node, node);
// Move the content into loop block
body.ExceptWith(loopContents);
nodes.ExceptWith(loopContents);
result.Add(new ILLoop() { ContentBlock = new ILBlock(FindLoops(loopContents, node)) });
}
@ -173,23 +198,23 @@ namespace Decompiler.ControlFlow @@ -173,23 +198,23 @@ namespace Decompiler.ControlFlow
}
// Add whatever is left
foreach(var node in body) {
foreach(var node in nodes) {
result.Add((ILNode)node.UserData);
}
return result;
}
static void FindLoopContents(HashSet<ControlFlowNode> body, HashSet<ControlFlowNode> loopContents, ControlFlowNode loopHead, ControlFlowNode addNode)
static void FindLoopContents(HashSet<ControlFlowNode> nodes, HashSet<ControlFlowNode> loopContents, ControlFlowNode loopHead, ControlFlowNode addNode)
{
if (body.Contains(addNode) && loopHead.Dominates(addNode) && loopContents.Add(addNode)) {
if (nodes.Contains(addNode) && loopHead.Dominates(addNode) && loopContents.Add(addNode)) {
foreach (var edge in addNode.Incoming) {
FindLoopContents(body, loopContents, loopHead, edge.Source);
FindLoopContents(nodes, loopContents, loopHead, edge.Source);
}
}
}
static HashSet<ControlFlowNode> FindDominatedNodes(HashSet<ControlFlowNode> body, ControlFlowNode head)
static HashSet<ControlFlowNode> FindDominatedNodes(HashSet<ControlFlowNode> nodes, ControlFlowNode head)
{
var exitNodes = head.DominanceFrontier.SelectMany(n => n.Predecessors);
HashSet<ControlFlowNode> agenda = new HashSet<ControlFlowNode>(exitNodes);
@ -199,7 +224,7 @@ namespace Decompiler.ControlFlow @@ -199,7 +224,7 @@ namespace Decompiler.ControlFlow
ControlFlowNode addNode = agenda.First();
agenda.Remove(addNode);
if (body.Contains(addNode) && head.Dominates(addNode) && result.Add(addNode)) {
if (nodes.Contains(addNode) && head.Dominates(addNode) && result.Add(addNode)) {
foreach (var predecessor in addNode.Predecessors) {
agenda.Add(predecessor);
}
@ -210,7 +235,7 @@ namespace Decompiler.ControlFlow @@ -210,7 +235,7 @@ namespace Decompiler.ControlFlow
return result;
}
static List<ILNode> FindConditions(HashSet<ControlFlowNode> body, ControlFlowNode entryNode)
static List<ILNode> FindConditions(HashSet<ControlFlowNode> nodes, ControlFlowNode entryNode)
{
List<ILNode> result = new List<ILNode>();
@ -219,7 +244,7 @@ namespace Decompiler.ControlFlow @@ -219,7 +244,7 @@ namespace Decompiler.ControlFlow
while(agenda.Count > 0) {
ControlFlowNode node = agenda.Dequeue();
if (body.Contains(node) && node.Outgoing.Count == 2) {
if (nodes.Contains(node) && node.Outgoing.Count == 2) {
ILCondition condition = new ILCondition() {
ConditionBlock = new ILBlock((ILNode)node.UserData)
};
@ -227,15 +252,16 @@ namespace Decompiler.ControlFlow @@ -227,15 +252,16 @@ namespace Decompiler.ControlFlow
frontiers.UnionWith(node.Outgoing[0].Target.DominanceFrontier);
frontiers.UnionWith(node.Outgoing[1].Target.DominanceFrontier);
if (!frontiers.Contains(node.Outgoing[0].Target)) {
HashSet<ControlFlowNode> content1 = FindDominatedNodes(body, node.Outgoing[0].Target);
body.ExceptWith(content1);
HashSet<ControlFlowNode> content1 = FindDominatedNodes(nodes, node.Outgoing[0].Target);
nodes.ExceptWith(content1);
condition.Block1 = new ILBlock(FindConditions(content1, node.Outgoing[0].Target));
}
if (!frontiers.Contains(node.Outgoing[1].Target)) {
HashSet<ControlFlowNode> content2 = FindDominatedNodes(body, node.Outgoing[1].Target);
body.ExceptWith(content2);
HashSet<ControlFlowNode> content2 = FindDominatedNodes(nodes, node.Outgoing[1].Target);
nodes.ExceptWith(content2);
condition.Block2 = new ILBlock(FindConditions(content2, node.Outgoing[1].Target));
}
nodes.Remove(node);
result.Add(condition);
}
@ -246,7 +272,7 @@ namespace Decompiler.ControlFlow @@ -246,7 +272,7 @@ namespace Decompiler.ControlFlow
}
// Add whatever is left
foreach(var node in body) {
foreach(var node in nodes) {
result.Add((ILNode)node.UserData);
}
@ -318,5 +344,71 @@ namespace Decompiler.ControlFlow @@ -318,5 +344,71 @@ namespace Decompiler.ControlFlow
}
*/
/// <summary>
/// Flattens all nested movable blocks, except the the top level 'node' argument
/// </summary>
void FlattenNestedMovableBlocks(ILNode node)
{
ILBlock block = node as ILBlock;
if (block != null) {
List<ILNode> flatBody = new List<ILNode>();
foreach (ILNode child in block.Body) {
FlattenNestedMovableBlocks(child);
if (child is ILMoveAbleBlock) {
flatBody.AddRange(((ILMoveAbleBlock)child).Body);
} else {
flatBody.Add(child);
}
}
block.Body = flatBody;
} else if (node is ILExpression) {
// Optimization - no need to check expressions
} else if (node != null) {
// Recursively find all ILBlocks
foreach(ILNode child in node.GetChildren()) {
FlattenNestedMovableBlocks(child);
}
}
}
void SimpleGotoRemoval(ILBlock ast)
{
var blocks = ast.GetSelfAndChildrenRecursive<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.OpCode == OpCodes.Br || expr.OpCode == OpCodes.Br_S)) {
// Check that branch is followed by its label (allow multiple labels)
for (int j = i + 1; j < block.Body.Count; j++) {
ILLabel label = block.Body[j] as ILLabel;
if (label == null)
break; // Can not optimize
if (expr.Operand == label) {
block.Body.RemoveAt(i);
break; // Branch removed
}
}
}
}
}
}
void RemoveDeadLabels(ILBlock ast)
{
HashSet<ILLabel> liveLabels = new HashSet<ILLabel>(ast.GetSelfAndChildrenRecursive<ILExpression>().SelectMany(e => e.GetBranchTargets()));
var blocks = ast.GetSelfAndChildrenRecursive<ILBlock>().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++;
}
}
}
}
}
}

31
ICSharpCode.Decompiler/ILAst/ILAstTypes.cs

@ -11,14 +11,18 @@ namespace Decompiler @@ -11,14 +11,18 @@ namespace Decompiler
{
public abstract class ILNode
{
public IEnumerable<T> GetChildrenRecursive<T>() where T: ILNode
public IEnumerable<T> GetSelfAndChildrenRecursive<T>() where T: ILNode
{
if (this is T)
yield return (T)this;
Stack<IEnumerator<ILNode>> stack = new Stack<IEnumerator<ILNode>>();
try {
stack.Push(GetChildren().GetEnumerator());
while (stack.Count > 0) {
while (stack.Peek().MoveNext()) {
ILNode element = stack.Peek().Current;
if (element != null) {
if (element is T)
yield return (T)element;
IEnumerable<ILNode> children = element.GetChildren();
@ -26,6 +30,7 @@ namespace Decompiler @@ -26,6 +30,7 @@ namespace Decompiler
stack.Push(children.GetEnumerator());
}
}
}
stack.Pop().Dispose();
}
} finally {
@ -37,17 +42,7 @@ namespace Decompiler @@ -37,17 +42,7 @@ namespace Decompiler
public virtual IEnumerable<ILNode> GetChildren()
{
return null;
}
}
public class ILLabel: ILNode
{
public string Name;
public override string ToString()
{
return Name + ":";
yield break;
}
}
@ -71,6 +66,16 @@ namespace Decompiler @@ -71,6 +66,16 @@ namespace Decompiler
}
}
public class ILLabel: ILNode
{
public string Name;
public override string ToString()
{
return Name + ":";
}
}
public class ILTryCatchBlock: ILNode
{
public class CatchBlock: ILBlock
@ -128,7 +133,7 @@ namespace Decompiler @@ -128,7 +133,7 @@ namespace Decompiler
} else if (this.Operand is ILLabel[]) {
return (ILLabel[])this.Operand;
} else {
return null;
return new ILLabel[] { };
}
}

Loading…
Cancel
Save