|
|
@ -22,48 +22,47 @@ namespace Decompiler.ControlFlow |
|
|
|
public class ILAstOptimizer |
|
|
|
public class ILAstOptimizer |
|
|
|
{ |
|
|
|
{ |
|
|
|
Dictionary<ILLabel, ControlFlowNode> labelToCfNode = new Dictionary<ILLabel, ControlFlowNode>(); |
|
|
|
Dictionary<ILLabel, ControlFlowNode> labelToCfNode = new Dictionary<ILLabel, ControlFlowNode>(); |
|
|
|
|
|
|
|
Dictionary<ILLabel, int> labelRefCount; |
|
|
|
|
|
|
|
|
|
|
|
public void Optimize(DecompilerContext context, ILBlock method, ILAstOptimizationStep abortBeforeStep = ILAstOptimizationStep.None) |
|
|
|
public void Optimize(DecompilerContext context, ILBlock method, ILAstOptimizationStep abortBeforeStep = ILAstOptimizationStep.None) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (abortBeforeStep == ILAstOptimizationStep.SplitToMovableBlocks) return; |
|
|
|
if (abortBeforeStep == ILAstOptimizationStep.SplitToMovableBlocks) return; |
|
|
|
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().ToList()) { |
|
|
|
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().ToList()) { |
|
|
|
SplitToMovableBlocks(block); |
|
|
|
SplitToBasicBlocks(block); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (abortBeforeStep == ILAstOptimizationStep.FindLoops) return; |
|
|
|
if (abortBeforeStep == ILAstOptimizationStep.FindLoops) return; |
|
|
|
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().Where(b => !(b is ILMoveableBlock)).ToList()) { |
|
|
|
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().ToList()) { |
|
|
|
ControlFlowGraph graph; |
|
|
|
ControlFlowGraph graph; |
|
|
|
graph = BuildGraph(block.Body, block.EntryPoint); |
|
|
|
graph = BuildGraph(block.Body, (ILLabel)block.EntryGoto.Operand); |
|
|
|
graph.ComputeDominance(); |
|
|
|
graph.ComputeDominance(); |
|
|
|
graph.ComputeDominanceFrontier(); |
|
|
|
graph.ComputeDominanceFrontier(); |
|
|
|
block.Body = FindLoops(new HashSet<ControlFlowNode>(graph.Nodes.Skip(3)), graph.EntryPoint, true); |
|
|
|
block.Body = FindLoops(new HashSet<ControlFlowNode>(graph.Nodes.Skip(3)), graph.EntryPoint, true); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (abortBeforeStep == ILAstOptimizationStep.FindConditions) return; |
|
|
|
if (abortBeforeStep == ILAstOptimizationStep.FindConditions) return; |
|
|
|
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().Where(b => !(b is ILMoveableBlock)).ToList()) { |
|
|
|
UpdateLabelRefCounts(method); |
|
|
|
|
|
|
|
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().ToList()) { |
|
|
|
ControlFlowGraph graph; |
|
|
|
ControlFlowGraph graph; |
|
|
|
graph = BuildGraph(block.Body, block.EntryPoint); |
|
|
|
graph = BuildGraph(block.Body, (ILLabel)block.EntryGoto.Operand); |
|
|
|
graph.ComputeDominance(); |
|
|
|
graph.ComputeDominance(); |
|
|
|
graph.ComputeDominanceFrontier(); |
|
|
|
graph.ComputeDominanceFrontier(); |
|
|
|
block.Body = FindConditions(new HashSet<ControlFlowNode>(graph.Nodes.Skip(3)), graph.EntryPoint); |
|
|
|
block.Body = FindConditions(new HashSet<ControlFlowNode>(graph.Nodes.Skip(3)), graph.EntryPoint); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// OrderNodes(method);
|
|
|
|
|
|
|
|
if (abortBeforeStep == ILAstOptimizationStep.FlattenNestedMovableBlocks) return; |
|
|
|
if (abortBeforeStep == ILAstOptimizationStep.FlattenNestedMovableBlocks) return; |
|
|
|
FlattenNestedMovableBlocks(method); |
|
|
|
FlattenBasicBlocks(method); |
|
|
|
|
|
|
|
|
|
|
|
if (abortBeforeStep == ILAstOptimizationStep.SimpleGotoRemoval) return; |
|
|
|
if (abortBeforeStep == ILAstOptimizationStep.SimpleGotoRemoval) return; |
|
|
|
SimpleGotoRemoval(method); |
|
|
|
SimpleGotoRemoval(method); |
|
|
|
|
|
|
|
|
|
|
|
if (abortBeforeStep == ILAstOptimizationStep.RemoveDeadLabels) return; |
|
|
|
if (abortBeforeStep == ILAstOptimizationStep.RemoveDeadLabels) return; |
|
|
|
RemoveDeadLabels(method); |
|
|
|
RemoveDeadLabels(method); |
|
|
|
|
|
|
|
|
|
|
|
if (abortBeforeStep == ILAstOptimizationStep.TypeInference) return; |
|
|
|
if (abortBeforeStep == ILAstOptimizationStep.TypeInference) return; |
|
|
|
TypeAnalysis.Run(context, method); |
|
|
|
TypeAnalysis.Run(context, method); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
class ILMoveableBlock: ILBlock |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
public int OriginalOrder; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int nextBlockIndex = 0; |
|
|
|
int nextBlockIndex = 0; |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// <summary>
|
|
|
@ -71,26 +70,29 @@ namespace Decompiler.ControlFlow |
|
|
|
/// The method adds necessary branches to make control flow between blocks
|
|
|
|
/// The method adds necessary branches to make control flow between blocks
|
|
|
|
/// explicit and thus order independent.
|
|
|
|
/// explicit and thus order independent.
|
|
|
|
/// </summary>
|
|
|
|
/// </summary>
|
|
|
|
void SplitToMovableBlocks(ILBlock block) |
|
|
|
void SplitToBasicBlocks(ILBlock block) |
|
|
|
{ |
|
|
|
{ |
|
|
|
// Remve no-ops
|
|
|
|
// Remve no-ops
|
|
|
|
// TODO: Assign the no-op range to someting
|
|
|
|
// TODO: Assign the no-op range to someting
|
|
|
|
block.Body = block.Body.Where(n => !(n is ILExpression && ((ILExpression)n).Code == ILCode.Nop)).ToList(); |
|
|
|
block.Body = block.Body.Where(n => !(n is ILExpression && ((ILExpression)n).Code == ILCode.Nop)).ToList(); |
|
|
|
|
|
|
|
|
|
|
|
List<ILNode> moveableBlocks = new List<ILNode>(); |
|
|
|
List<ILNode> basicBlocks = new List<ILNode>(); |
|
|
|
|
|
|
|
|
|
|
|
ILMoveableBlock moveableBlock = new ILMoveableBlock() { OriginalOrder = (nextBlockIndex++) }; |
|
|
|
ILBasicBlock basicBlock = new ILBasicBlock() { |
|
|
|
moveableBlocks.Add(moveableBlock); |
|
|
|
EntryLabel = new ILLabel() { Name = "Block_" + (nextBlockIndex++) } |
|
|
|
block.EntryPoint = new ILLabel() { Name = "Block_" + moveableBlock.OriginalOrder }; |
|
|
|
}; |
|
|
|
moveableBlock.Body.Add(block.EntryPoint); |
|
|
|
basicBlocks.Add(basicBlock); |
|
|
|
|
|
|
|
block.EntryGoto = new ILExpression(ILCode.Br, basicBlock.EntryLabel); |
|
|
|
|
|
|
|
|
|
|
|
if (block.Body.Count > 0) { |
|
|
|
if (block.Body.Count > 0) { |
|
|
|
moveableBlock.Body.Add(block.Body[0]); |
|
|
|
basicBlock.Body.Add(block.Body[0]); |
|
|
|
|
|
|
|
|
|
|
|
for (int i = 1; i < block.Body.Count; i++) { |
|
|
|
for (int i = 1; i < block.Body.Count; i++) { |
|
|
|
ILNode lastNode = block.Body[i - 1]; |
|
|
|
ILNode lastNode = block.Body[i - 1]; |
|
|
|
ILNode currNode = block.Body[i]; |
|
|
|
ILNode currNode = block.Body[i]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool added = false; |
|
|
|
|
|
|
|
|
|
|
|
// Insert split
|
|
|
|
// Insert split
|
|
|
|
if ((currNode is ILLabel && !(lastNode is ILLabel)) || |
|
|
|
if ((currNode is ILLabel && !(lastNode is ILLabel)) || |
|
|
|
lastNode is ILTryCatchBlock || |
|
|
|
lastNode is ILTryCatchBlock || |
|
|
@ -98,24 +100,30 @@ namespace Decompiler.ControlFlow |
|
|
|
(lastNode is ILExpression) && ((ILExpression)lastNode).IsBranch() || |
|
|
|
(lastNode is ILExpression) && ((ILExpression)lastNode).IsBranch() || |
|
|
|
(currNode is ILExpression) && ((ILExpression)currNode).IsBranch()) |
|
|
|
(currNode is ILExpression) && ((ILExpression)currNode).IsBranch()) |
|
|
|
{ |
|
|
|
{ |
|
|
|
ILBlock lastBlock = moveableBlock; |
|
|
|
ILBasicBlock lastBlock = basicBlock; |
|
|
|
moveableBlock = new ILMoveableBlock() { OriginalOrder = (nextBlockIndex++) }; |
|
|
|
basicBlock = new ILBasicBlock(); |
|
|
|
moveableBlocks.Add(moveableBlock); |
|
|
|
basicBlocks.Add(basicBlock); |
|
|
|
|
|
|
|
if (currNode is ILLabel) { |
|
|
|
|
|
|
|
// Reuse the first label
|
|
|
|
|
|
|
|
basicBlock.EntryLabel = (ILLabel)currNode; |
|
|
|
|
|
|
|
added = true; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
basicBlock.EntryLabel = new ILLabel() { Name = "Block_" + (nextBlockIndex++) }; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Explicit branch from one block to other
|
|
|
|
// Explicit branch from one block to other
|
|
|
|
// (unless the last expression was unconditional branch)
|
|
|
|
// (unless the last expression was unconditional branch)
|
|
|
|
if (!(lastNode is ILExpression) || ((ILExpression)lastNode).Code.CanFallThough()) { |
|
|
|
if (!(lastNode is ILExpression) || ((ILExpression)lastNode).Code.CanFallThough()) { |
|
|
|
ILLabel blockLabel = new ILLabel() { Name = "Block_" + moveableBlock.OriginalOrder }; |
|
|
|
lastBlock.FallthoughGoto = new ILExpression(ILCode.Br, basicBlock.EntryLabel); |
|
|
|
lastBlock.Body.Add(new ILExpression(ILCode.Br, blockLabel)); |
|
|
|
|
|
|
|
moveableBlock.Body.Add(blockLabel); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
moveableBlock.Body.Add(currNode); |
|
|
|
if (!added) |
|
|
|
|
|
|
|
basicBlock.Body.Add(currNode); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
block.Body = moveableBlocks; |
|
|
|
block.Body = basicBlocks; |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -175,7 +183,7 @@ namespace Decompiler.ControlFlow |
|
|
|
return new ControlFlowGraph(cfNodes.ToArray()); |
|
|
|
return new ControlFlowGraph(cfNodes.ToArray()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
List<ILNode> FindLoops(HashSet<ControlFlowNode> nodes, ControlFlowNode entryPoint, bool excludeEntryPoint) |
|
|
|
List<ILNode> FindLoops(HashSet<ControlFlowNode> scope, ControlFlowNode entryPoint, bool excludeEntryPoint) |
|
|
|
{ |
|
|
|
{ |
|
|
|
List<ILNode> result = new List<ILNode>(); |
|
|
|
List<ILNode> result = new List<ILNode>(); |
|
|
|
|
|
|
|
|
|
|
@ -184,18 +192,22 @@ namespace Decompiler.ControlFlow |
|
|
|
while(agenda.Count > 0) { |
|
|
|
while(agenda.Count > 0) { |
|
|
|
ControlFlowNode node = agenda.Dequeue(); |
|
|
|
ControlFlowNode node = agenda.Dequeue(); |
|
|
|
|
|
|
|
|
|
|
|
if (nodes.Contains(node) |
|
|
|
if (scope.Contains(node) |
|
|
|
&& node.DominanceFrontier.Contains(node) |
|
|
|
&& node.DominanceFrontier.Contains(node) |
|
|
|
&& (node != entryPoint || !excludeEntryPoint)) |
|
|
|
&& (node != entryPoint || !excludeEntryPoint)) |
|
|
|
{ |
|
|
|
{ |
|
|
|
HashSet<ControlFlowNode> loopContents = new HashSet<ControlFlowNode>(); |
|
|
|
HashSet<ControlFlowNode> loopContents = new HashSet<ControlFlowNode>(); |
|
|
|
FindLoopContents(nodes, loopContents, node, node); |
|
|
|
FindLoopContents(scope, loopContents, node, node); |
|
|
|
|
|
|
|
|
|
|
|
// Move the content into loop block
|
|
|
|
// Move the content into loop block
|
|
|
|
nodes.ExceptWith(loopContents); |
|
|
|
scope.ExceptWith(loopContents); |
|
|
|
ILLabel entryLabel = new ILLabel() { Name = "Loop_" + (nextBlockIndex++) }; |
|
|
|
ILLabel entryLabel = new ILLabel() { Name = "Loop_" + (nextBlockIndex++) }; |
|
|
|
((ILBlock)node.UserData).Body.Insert(0, entryLabel); |
|
|
|
((ILBasicBlock)node.UserData).Body.Insert(0, entryLabel); |
|
|
|
result.Add(new ILLoop() { ContentBlock = new ILBlock(FindLoops(loopContents, node, true)) { EntryPoint = entryLabel } }); |
|
|
|
result.Add(new ILLoop() { |
|
|
|
|
|
|
|
ContentBlock = new ILBlock(FindLoops(loopContents, node, true)) { |
|
|
|
|
|
|
|
EntryGoto = new ILExpression(ILCode.Br, entryLabel) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Using the dominator tree should ensure we find the the widest loop first
|
|
|
|
// Using the dominator tree should ensure we find the the widest loop first
|
|
|
@ -205,23 +217,34 @@ namespace Decompiler.ControlFlow |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Add whatever is left
|
|
|
|
// Add whatever is left
|
|
|
|
foreach(var node in nodes) { |
|
|
|
foreach(var node in scope) { |
|
|
|
result.Add((ILNode)node.UserData); |
|
|
|
result.Add((ILNode)node.UserData); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return result; |
|
|
|
return result; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void FindLoopContents(HashSet<ControlFlowNode> nodes, HashSet<ControlFlowNode> loopContents, ControlFlowNode loopHead, ControlFlowNode addNode) |
|
|
|
static void FindLoopContents(HashSet<ControlFlowNode> scope, HashSet<ControlFlowNode> loopContents, ControlFlowNode loopHead, ControlFlowNode addNode) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (nodes.Contains(addNode) && loopHead.Dominates(addNode) && loopContents.Add(addNode)) { |
|
|
|
if (scope.Contains(addNode) && loopHead.Dominates(addNode) && loopContents.Add(addNode)) { |
|
|
|
foreach (var edge in addNode.Incoming) { |
|
|
|
foreach (var edge in addNode.Incoming) { |
|
|
|
FindLoopContents(nodes, loopContents, loopHead, edge.Source); |
|
|
|
FindLoopContents(scope, loopContents, loopHead, edge.Source); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void UpdateLabelRefCounts(ILBlock method) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
labelRefCount = new Dictionary<ILLabel, int>(); |
|
|
|
|
|
|
|
foreach(ILLabel label in method.GetSelfAndChildrenRecursive<ILLabel>()) { |
|
|
|
|
|
|
|
labelRefCount[label] = 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
foreach(ILLabel target in method.GetSelfAndChildrenRecursive<ILExpression>().SelectMany(e => e.GetBranchTargets())) { |
|
|
|
|
|
|
|
labelRefCount[target]++; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
List<ILNode> FindConditions(HashSet<ControlFlowNode> nodes, ControlFlowNode entryNode) |
|
|
|
List<ILNode> FindConditions(HashSet<ControlFlowNode> scope, ControlFlowNode entryNode) |
|
|
|
{ |
|
|
|
{ |
|
|
|
List<ILNode> result = new List<ILNode>(); |
|
|
|
List<ILNode> result = new List<ILNode>(); |
|
|
|
|
|
|
|
|
|
|
@ -236,41 +259,32 @@ namespace Decompiler.ControlFlow |
|
|
|
agenda.Remove(node); |
|
|
|
agenda.Remove(node); |
|
|
|
|
|
|
|
|
|
|
|
// Find a block that represents a simple condition
|
|
|
|
// Find a block that represents a simple condition
|
|
|
|
if (nodes.Contains(node)) { |
|
|
|
if (scope.Contains(node)) { |
|
|
|
|
|
|
|
|
|
|
|
ILMoveableBlock block = node.UserData as ILMoveableBlock; |
|
|
|
ILBasicBlock block = node.UserData as ILBasicBlock; |
|
|
|
|
|
|
|
|
|
|
|
if (block != null && block.Body.Count == 3) { |
|
|
|
if (block != null && block.Body.Count == 1) { |
|
|
|
|
|
|
|
|
|
|
|
ILLabel label = block.Body[0] as ILLabel; |
|
|
|
ILExpression condBranch = block.Body[0] as ILExpression; |
|
|
|
ILExpression condBranch = block.Body[1] as ILExpression; |
|
|
|
|
|
|
|
ILExpression statBranch = block.Body[2] as ILExpression; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Switch
|
|
|
|
// Switch
|
|
|
|
if (label != null && |
|
|
|
if (condBranch != null && condBranch.Operand is ILLabel[] && condBranch.Arguments.Count > 0) { |
|
|
|
condBranch != null && condBranch.Operand is ILLabel[] && condBranch.Arguments.Count > 0 && |
|
|
|
|
|
|
|
statBranch != null && statBranch.Operand is ILLabel && statBranch.Arguments.Count == 0) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
ILSwitch ilSwitch = new ILSwitch() { Condition = condBranch }; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Replace the two branches with a conditional structure - this preserves the node label
|
|
|
|
ILSwitch ilSwitch = new ILSwitch() { |
|
|
|
block.Body.Remove(condBranch); |
|
|
|
Condition = condBranch, |
|
|
|
block.Body.Remove(statBranch); |
|
|
|
DefaultGoto = block.FallthoughGoto |
|
|
|
block.Body.Add(ilSwitch); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
ControlFlowNode statTarget = null; |
|
|
|
ControlFlowNode fallTarget = null; |
|
|
|
labelToCfNode.TryGetValue((ILLabel)statBranch.Operand, out statTarget); |
|
|
|
labelToCfNode.TryGetValue((ILLabel)block.FallthoughGoto.Operand, out fallTarget); |
|
|
|
|
|
|
|
|
|
|
|
// Pull in the conditional code
|
|
|
|
|
|
|
|
HashSet<ControlFlowNode> frontiers = new HashSet<ControlFlowNode>(); |
|
|
|
HashSet<ControlFlowNode> frontiers = new HashSet<ControlFlowNode>(); |
|
|
|
|
|
|
|
if (fallTarget != null) |
|
|
|
if (statTarget != null) |
|
|
|
frontiers.UnionWith(fallTarget.DominanceFrontier); |
|
|
|
frontiers.UnionWith(statTarget.DominanceFrontier); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
foreach(ILLabel condLabel in (ILLabel[])condBranch.Operand) { |
|
|
|
foreach(ILLabel condLabel in (ILLabel[])condBranch.Operand) { |
|
|
|
ControlFlowNode condTarget = null; |
|
|
|
ControlFlowNode condTarget = null; |
|
|
|
labelToCfNode.TryGetValue(condLabel, out condTarget); |
|
|
|
labelToCfNode.TryGetValue(condLabel, out condTarget); |
|
|
|
|
|
|
|
|
|
|
|
if (condTarget != null) |
|
|
|
if (condTarget != null) |
|
|
|
frontiers.UnionWith(condTarget.DominanceFrontier); |
|
|
|
frontiers.UnionWith(condTarget.DominanceFrontier); |
|
|
|
} |
|
|
|
} |
|
|
@ -279,10 +293,12 @@ namespace Decompiler.ControlFlow |
|
|
|
ControlFlowNode condTarget = null; |
|
|
|
ControlFlowNode condTarget = null; |
|
|
|
labelToCfNode.TryGetValue(condLabel, out condTarget); |
|
|
|
labelToCfNode.TryGetValue(condLabel, out condTarget); |
|
|
|
|
|
|
|
|
|
|
|
ILBlock caseBlock = new ILBlock() { EntryPoint = condLabel }; |
|
|
|
ILBlock caseBlock = new ILBlock() { |
|
|
|
|
|
|
|
EntryGoto = new ILExpression(ILCode.Br, condLabel) |
|
|
|
|
|
|
|
}; |
|
|
|
if (condTarget != null && !frontiers.Contains(condTarget)) { |
|
|
|
if (condTarget != null && !frontiers.Contains(condTarget)) { |
|
|
|
HashSet<ControlFlowNode> content = FindDominatedNodes(nodes, condTarget); |
|
|
|
HashSet<ControlFlowNode> content = FindDominatedNodes(scope, condTarget); |
|
|
|
nodes.ExceptWith(content); |
|
|
|
scope.ExceptWith(content); |
|
|
|
caseBlock.Body.AddRange(FindConditions(content, condTarget)); |
|
|
|
caseBlock.Body.AddRange(FindConditions(content, condTarget)); |
|
|
|
} |
|
|
|
} |
|
|
|
ilSwitch.CaseBlocks.Add(caseBlock); |
|
|
|
ilSwitch.CaseBlocks.Add(caseBlock); |
|
|
@ -291,61 +307,61 @@ namespace Decompiler.ControlFlow |
|
|
|
// The labels will not be used - kill them
|
|
|
|
// The labels will not be used - kill them
|
|
|
|
condBranch.Operand = null; |
|
|
|
condBranch.Operand = null; |
|
|
|
|
|
|
|
|
|
|
|
result.Add(block); |
|
|
|
result.Add(new ILBasicBlock() { |
|
|
|
nodes.Remove(node); |
|
|
|
EntryLabel = block.EntryLabel, // Keep the entry label
|
|
|
|
|
|
|
|
Body = { ilSwitch } |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
scope.Remove(node); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Two-way branch
|
|
|
|
// Two-way branch
|
|
|
|
if (label != null && |
|
|
|
ILCondition ilCond; |
|
|
|
condBranch != null && condBranch.Operand is ILLabel && condBranch.Arguments.Count > 0 && |
|
|
|
HashSet<ControlFlowNode> matchedNodes; |
|
|
|
statBranch != null && statBranch.Operand is ILLabel && statBranch.Arguments.Count == 0) |
|
|
|
ILLabel condEntryLabel; |
|
|
|
{ |
|
|
|
if (TryMatchCondition(scope, new ControlFlowNode[] {}, node, out ilCond, out matchedNodes, out condEntryLabel)) { |
|
|
|
ControlFlowNode statTarget = null; |
|
|
|
|
|
|
|
labelToCfNode.TryGetValue((ILLabel)statBranch.Operand, out statTarget); |
|
|
|
|
|
|
|
ControlFlowNode condTarget = null; |
|
|
|
|
|
|
|
labelToCfNode.TryGetValue((ILLabel)condBranch.Operand, out condTarget); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ILCondition condition = new ILCondition() { |
|
|
|
// The branch labels will not be used - kill them
|
|
|
|
Condition = condBranch, |
|
|
|
foreach(ILExpression expr in ilCond.Condition.GetSelfAndChildrenRecursive<ILExpression>()) { |
|
|
|
TrueBlock = new ILBlock() { EntryPoint = (ILLabel)condBranch.Operand }, |
|
|
|
if (expr.GetBranchTargets().Any()) { |
|
|
|
FalseBlock = new ILBlock() { EntryPoint = (ILLabel)statBranch.Operand } |
|
|
|
expr.Operand = null; |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Replace the two branches with a conditional structure - this preserves the node label
|
|
|
|
ControlFlowNode trueTarget = null; |
|
|
|
block.Body.Remove(condBranch); |
|
|
|
labelToCfNode.TryGetValue((ILLabel)ilCond.TrueBlock.EntryGoto.Operand, out trueTarget); |
|
|
|
block.Body.Remove(statBranch); |
|
|
|
ControlFlowNode falseTarget = null; |
|
|
|
block.Body.Add(condition); |
|
|
|
labelToCfNode.TryGetValue((ILLabel)ilCond.FalseBlock.EntryGoto.Operand, out falseTarget); |
|
|
|
|
|
|
|
|
|
|
|
// Pull in the conditional code
|
|
|
|
// Pull in the conditional code
|
|
|
|
HashSet<ControlFlowNode> frontiers = new HashSet<ControlFlowNode>(); |
|
|
|
HashSet<ControlFlowNode> frontiers = new HashSet<ControlFlowNode>(); |
|
|
|
if (statTarget != null) |
|
|
|
if (trueTarget != null) |
|
|
|
frontiers.UnionWith(statTarget.DominanceFrontier); |
|
|
|
frontiers.UnionWith(trueTarget.DominanceFrontier); |
|
|
|
if (condTarget != null) |
|
|
|
if (falseTarget != null) |
|
|
|
frontiers.UnionWith(condTarget.DominanceFrontier); |
|
|
|
frontiers.UnionWith(falseTarget.DominanceFrontier); |
|
|
|
|
|
|
|
|
|
|
|
if (condTarget != null && !frontiers.Contains(condTarget)) { |
|
|
|
if (trueTarget != null && !frontiers.Contains(trueTarget)) { |
|
|
|
HashSet<ControlFlowNode> content = FindDominatedNodes(nodes, condTarget); |
|
|
|
HashSet<ControlFlowNode> content = FindDominatedNodes(scope, trueTarget); |
|
|
|
nodes.ExceptWith(content); |
|
|
|
scope.ExceptWith(content); |
|
|
|
condition.TrueBlock.Body.AddRange(FindConditions(content, condTarget)); |
|
|
|
ilCond.TrueBlock.Body.AddRange(FindConditions(content, trueTarget)); |
|
|
|
} |
|
|
|
} |
|
|
|
if (statTarget != null && !frontiers.Contains(statTarget)) { |
|
|
|
if (falseTarget != null && !frontiers.Contains(falseTarget)) { |
|
|
|
HashSet<ControlFlowNode> content = FindDominatedNodes(nodes, statTarget); |
|
|
|
HashSet<ControlFlowNode> content = FindDominatedNodes(scope, falseTarget); |
|
|
|
nodes.ExceptWith(content); |
|
|
|
scope.ExceptWith(content); |
|
|
|
condition.FalseBlock.Body.AddRange(FindConditions(content, statTarget)); |
|
|
|
ilCond.FalseBlock.Body.AddRange(FindConditions(content, falseTarget)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// The label will not be used - kill it
|
|
|
|
result.Add(new ILBasicBlock() { |
|
|
|
condBranch.Operand = null; |
|
|
|
EntryLabel = condEntryLabel, // Keep the entry label
|
|
|
|
|
|
|
|
Body = { ilCond } |
|
|
|
result.Add(block); |
|
|
|
}); |
|
|
|
nodes.Remove(node); |
|
|
|
scope.ExceptWith(matchedNodes); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Add the node now so that we have good ordering
|
|
|
|
// Add the node now so that we have good ordering
|
|
|
|
if (nodes.Contains(node)) { |
|
|
|
if (scope.Contains(node)) { |
|
|
|
result.Add((ILNode)node.UserData); |
|
|
|
result.Add((ILNode)node.UserData); |
|
|
|
nodes.Remove(node); |
|
|
|
scope.Remove(node); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -356,138 +372,160 @@ namespace Decompiler.ControlFlow |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Add whatever is left
|
|
|
|
// Add whatever is left
|
|
|
|
foreach(var node in nodes) { |
|
|
|
foreach(var node in scope) { |
|
|
|
result.Add((ILNode)node.UserData); |
|
|
|
result.Add((ILNode)node.UserData); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return result; |
|
|
|
return result; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static HashSet<ControlFlowNode> FindDominatedNodes(HashSet<ControlFlowNode> nodes, ControlFlowNode head) |
|
|
|
bool TryMatchCondition(HashSet<ControlFlowNode> scope, IEnumerable<ControlFlowNode> scopeExcept, ControlFlowNode head, out ILCondition condition, out HashSet<ControlFlowNode> matchedNodes, out ILLabel entryLabel) |
|
|
|
{ |
|
|
|
{ |
|
|
|
var exitNodes = head.DominanceFrontier.SelectMany(n => n.Predecessors); |
|
|
|
condition = null; |
|
|
|
HashSet<ControlFlowNode> agenda = new HashSet<ControlFlowNode>(exitNodes); |
|
|
|
matchedNodes = null; |
|
|
|
HashSet<ControlFlowNode> result = new HashSet<ControlFlowNode>(); |
|
|
|
entryLabel = null; |
|
|
|
|
|
|
|
if (!scope.Contains(head) || scopeExcept.Contains(head)) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
|
|
while(agenda.Count > 0) { |
|
|
|
ILBasicBlock basicBlock = head.UserData as ILBasicBlock; |
|
|
|
ControlFlowNode addNode = agenda.First(); |
|
|
|
|
|
|
|
agenda.Remove(addNode); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (nodes.Contains(addNode) && head.Dominates(addNode) && result.Add(addNode)) { |
|
|
|
if (basicBlock == null || basicBlock.Body.Count != 1) |
|
|
|
foreach (var predecessor in addNode.Predecessors) { |
|
|
|
return false; |
|
|
|
agenda.Add(predecessor); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (nodes.Contains(head)) |
|
|
|
|
|
|
|
result.Add(head); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return result; |
|
|
|
ILExpression condBranch = basicBlock.Body[0] as ILExpression; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* |
|
|
|
if (condBranch != null && condBranch.Operand is ILLabel && condBranch.Arguments.Count > 0) { |
|
|
|
|
|
|
|
|
|
|
|
public enum ShortCircuitOperator |
|
|
|
// We have found a two-way condition
|
|
|
|
{ |
|
|
|
condition = new ILCondition() { |
|
|
|
LeftAndRight, |
|
|
|
Condition = condBranch, |
|
|
|
LeftOrRight, |
|
|
|
TrueBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, condBranch.Operand) }, |
|
|
|
NotLeftAndRight, |
|
|
|
FalseBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, basicBlock.FallthoughGoto.Operand) } |
|
|
|
NotLeftOrRight, |
|
|
|
}; |
|
|
|
} |
|
|
|
// We are done with the node so "remove" it from scope
|
|
|
|
|
|
|
|
scopeExcept = scopeExcept.Union(new[] {head}); |
|
|
|
|
|
|
|
matchedNodes = new HashSet<ControlFlowNode>() { head }; |
|
|
|
|
|
|
|
entryLabel = basicBlock.EntryLabel; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Optimize short-circut expressions
|
|
|
|
|
|
|
|
while(true) { |
|
|
|
|
|
|
|
|
|
|
|
static bool TryOptimizeShortCircuit(Node head) |
|
|
|
// Consider condition.TrueBlock
|
|
|
|
{ |
|
|
|
{ |
|
|
|
if ((head is BasicBlock) && |
|
|
|
ILLabel nextLabel = (ILLabel)condition.TrueBlock.EntryGoto.Operand; |
|
|
|
(head as BasicBlock).BranchBasicBlock != null && |
|
|
|
ControlFlowNode nextTarget; |
|
|
|
(head as BasicBlock).FallThroughBasicBlock != null) { |
|
|
|
labelToCfNode.TryGetValue(nextLabel, out nextTarget); |
|
|
|
head.Parent.MergeChilds<SimpleBranch>(head); |
|
|
|
ILCondition nextCond; |
|
|
|
return true; |
|
|
|
HashSet<ControlFlowNode> nextMatchedNodes; |
|
|
|
|
|
|
|
ILLabel nextEnteryLabel; |
|
|
|
|
|
|
|
if (nextTarget != null && |
|
|
|
|
|
|
|
TryMatchCondition(scope, scopeExcept, nextTarget, out nextCond, out nextMatchedNodes, out nextEnteryLabel) && |
|
|
|
|
|
|
|
labelRefCount[nextEnteryLabel] == 1) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (condition.FalseBlock.EntryGoto.Operand == nextCond.FalseBlock.EntryGoto.Operand) { |
|
|
|
|
|
|
|
condition.Condition = new ILExpression(ILCode.LogicAnd, null, condition.Condition, nextCond.Condition); |
|
|
|
|
|
|
|
condition.TrueBlock = nextCond.TrueBlock; |
|
|
|
|
|
|
|
condition.FalseBlock = nextCond.FalseBlock; |
|
|
|
|
|
|
|
scopeExcept = scopeExcept.Union(nextMatchedNodes); |
|
|
|
|
|
|
|
matchedNodes.UnionWith(nextMatchedNodes); |
|
|
|
|
|
|
|
continue; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Branch top = head as Branch; |
|
|
|
if (condition.FalseBlock.EntryGoto.Operand == nextCond.TrueBlock.EntryGoto.Operand) { |
|
|
|
if (top == null) return false; |
|
|
|
condition.Condition = new ILExpression(ILCode.LogicOr, null, new ILExpression(ILCode.LogicNot, null, condition.Condition), nextCond.Condition); |
|
|
|
|
|
|
|
condition.TrueBlock = nextCond.TrueBlock; |
|
|
|
Branch left = head.FloatUpToNeighbours(top.TrueSuccessor) as Branch; |
|
|
|
condition.FalseBlock = nextCond.FalseBlock; |
|
|
|
Branch right = head.FloatUpToNeighbours(top.FalseSuccessor) as Branch; |
|
|
|
scopeExcept = scopeExcept.Union(nextMatchedNodes); |
|
|
|
|
|
|
|
matchedNodes.UnionWith(nextMatchedNodes); |
|
|
|
// A & B
|
|
|
|
continue; |
|
|
|
if (left != null && |
|
|
|
} |
|
|
|
left.Predecessors.Count == 1 && |
|
|
|
|
|
|
|
left.FalseSuccessor == top.FalseSuccessor) { |
|
|
|
|
|
|
|
ShortCircuitBranch scBranch = top.Parent.MergeChilds<ShortCircuitBranch>(top, left); |
|
|
|
|
|
|
|
scBranch.Operator = ShortCircuitOperator.LeftAndRight; |
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// ~A | B
|
|
|
|
|
|
|
|
if (left != null && |
|
|
|
|
|
|
|
left.Predecessors.Count == 1 && |
|
|
|
|
|
|
|
left.TrueSuccessor == top.FalseSuccessor) { |
|
|
|
|
|
|
|
ShortCircuitBranch scBranch = top.Parent.MergeChilds<ShortCircuitBranch>(top, left); |
|
|
|
|
|
|
|
scBranch.Operator = ShortCircuitOperator.NotLeftOrRight; |
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// A | B
|
|
|
|
// Consider condition.FalseBlock
|
|
|
|
if (right != null && |
|
|
|
{ |
|
|
|
right.Predecessors.Count == 1 && |
|
|
|
ILLabel nextLabel = (ILLabel)condition.FalseBlock.EntryGoto.Operand; |
|
|
|
right.TrueSuccessor == top.TrueSuccessor) { |
|
|
|
ControlFlowNode nextTarget; |
|
|
|
ShortCircuitBranch scBranch = top.Parent.MergeChilds<ShortCircuitBranch>(top, right); |
|
|
|
labelToCfNode.TryGetValue(nextLabel, out nextTarget); |
|
|
|
scBranch.Operator = ShortCircuitOperator.LeftOrRight; |
|
|
|
ILCondition nextCond; |
|
|
|
return true; |
|
|
|
HashSet<ControlFlowNode> nextMatchedNodes; |
|
|
|
|
|
|
|
ILLabel nextEnteryLabel; |
|
|
|
|
|
|
|
if (nextTarget != null && |
|
|
|
|
|
|
|
TryMatchCondition(scope, scopeExcept, nextTarget, out nextCond, out nextMatchedNodes, out nextEnteryLabel) && |
|
|
|
|
|
|
|
labelRefCount[nextEnteryLabel] == 1) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (condition.TrueBlock.EntryGoto.Operand == nextCond.FalseBlock.EntryGoto.Operand) { |
|
|
|
|
|
|
|
condition.Condition = new ILExpression(ILCode.LogicAnd, null, new ILExpression(ILCode.LogicNot, null, condition.Condition), nextCond.Condition); |
|
|
|
|
|
|
|
condition.TrueBlock = nextCond.TrueBlock; |
|
|
|
|
|
|
|
condition.FalseBlock = nextCond.FalseBlock; |
|
|
|
|
|
|
|
scopeExcept = scopeExcept.Union(nextMatchedNodes); |
|
|
|
|
|
|
|
matchedNodes.UnionWith(nextMatchedNodes); |
|
|
|
|
|
|
|
continue; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// ~A & B
|
|
|
|
if (condition.TrueBlock.EntryGoto.Operand == nextCond.TrueBlock.EntryGoto.Operand) { |
|
|
|
if (right != null && |
|
|
|
condition.Condition = new ILExpression(ILCode.LogicOr, null, condition.Condition, nextCond.Condition); |
|
|
|
right.Predecessors.Count == 1 && |
|
|
|
condition.TrueBlock = nextCond.TrueBlock; |
|
|
|
right.FalseSuccessor == top.TrueSuccessor) { |
|
|
|
condition.FalseBlock = nextCond.FalseBlock; |
|
|
|
ShortCircuitBranch scBranch = top.Parent.MergeChilds<ShortCircuitBranch>(top, right); |
|
|
|
scopeExcept = scopeExcept.Union(nextMatchedNodes); |
|
|
|
scBranch.Operator = ShortCircuitOperator.NotLeftAndRight; |
|
|
|
matchedNodes.UnionWith(nextMatchedNodes); |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
return true; |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
static HashSet<ControlFlowNode> FindDominatedNodes(HashSet<ControlFlowNode> scope, ControlFlowNode head) |
|
|
|
|
|
|
|
|
|
|
|
void OrderNodes(ILBlock ast) |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
// Order movable nodes
|
|
|
|
var exitNodes = head.DominanceFrontier.SelectMany(n => n.Predecessors); |
|
|
|
var blocks = ast.GetSelfAndChildrenRecursive<ILBlock>().Where(b => !(b is ILMoveableBlock)).ToList(); |
|
|
|
HashSet<ControlFlowNode> agenda = new HashSet<ControlFlowNode>(exitNodes); |
|
|
|
ILMoveableBlock first = new ILMoveableBlock() { OriginalOrder = -1 }; |
|
|
|
HashSet<ControlFlowNode> result = new HashSet<ControlFlowNode>(); |
|
|
|
foreach(ILBlock block in blocks) { |
|
|
|
|
|
|
|
block.Body = block.Body.OrderBy(n => (n.GetSelfAndChildrenRecursive<ILMoveableBlock>().FirstOrDefault() ?? first).OriginalOrder).ToList(); |
|
|
|
while(agenda.Count > 0) { |
|
|
|
|
|
|
|
ControlFlowNode addNode = agenda.First(); |
|
|
|
|
|
|
|
agenda.Remove(addNode); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (scope.Contains(addNode) && head.Dominates(addNode) && result.Add(addNode)) { |
|
|
|
|
|
|
|
foreach (var predecessor in addNode.Predecessors) { |
|
|
|
|
|
|
|
agenda.Add(predecessor); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (scope.Contains(head)) |
|
|
|
|
|
|
|
result.Add(head); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return result; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// <summary>
|
|
|
|
/// Flattens all nested movable blocks, except the the top level 'node' argument
|
|
|
|
/// Flattens all nested basic blocks, except the the top level 'node' argument
|
|
|
|
/// </summary>
|
|
|
|
/// </summary>
|
|
|
|
void FlattenNestedMovableBlocks(ILNode node) |
|
|
|
void FlattenBasicBlocks(ILNode node) |
|
|
|
{ |
|
|
|
{ |
|
|
|
ILBlock block = node as ILBlock; |
|
|
|
ILBlock block = node as ILBlock; |
|
|
|
if (block != null) { |
|
|
|
if (block != null) { |
|
|
|
List<ILNode> flatBody = new List<ILNode>(); |
|
|
|
List<ILNode> flatBody = new List<ILNode>(); |
|
|
|
if (block.EntryPoint != null) { |
|
|
|
foreach (ILNode child in block.GetChildren()) { |
|
|
|
flatBody.Add(new ILExpression(ILCode.Br, block.EntryPoint)); |
|
|
|
FlattenBasicBlocks(child); |
|
|
|
block.EntryPoint = null; |
|
|
|
if (child is ILBasicBlock) { |
|
|
|
} |
|
|
|
flatBody.AddRange(child.GetChildren()); |
|
|
|
foreach (ILNode child in block.Body) { |
|
|
|
|
|
|
|
FlattenNestedMovableBlocks(child); |
|
|
|
|
|
|
|
if (child is ILMoveableBlock) { |
|
|
|
|
|
|
|
flatBody.AddRange(((ILMoveableBlock)child).Body); |
|
|
|
|
|
|
|
} else { |
|
|
|
} else { |
|
|
|
flatBody.Add(child); |
|
|
|
flatBody.Add(child); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
block.EntryGoto = null; |
|
|
|
block.Body = flatBody; |
|
|
|
block.Body = flatBody; |
|
|
|
} else if (node is ILExpression) { |
|
|
|
} else if (node is ILExpression) { |
|
|
|
// Optimization - no need to check expressions
|
|
|
|
// Optimization - no need to check expressions
|
|
|
|
} else if (node != null) { |
|
|
|
} else if (node != null) { |
|
|
|
// Recursively find all ILBlocks
|
|
|
|
// Recursively find all ILBlocks
|
|
|
|
foreach(ILNode child in node.GetChildren()) { |
|
|
|
foreach(ILNode child in node.GetChildren()) { |
|
|
|
FlattenNestedMovableBlocks(child); |
|
|
|
FlattenBasicBlocks(child); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|