|
|
|
@ -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(tryCatchBlock.FinallyBlock.Body); |
|
|
|
|
Optimize(ref 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++; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|