Browse Source

Fixed some control flow correctness issues

pull/10/head
David Srbecký 15 years ago
parent
commit
daf6643434
  1. 7
      ICSharpCode.Decompiler/Ast/AstMetodBodyBuilder.cs
  2. 149
      ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
  3. 9
      ICSharpCode.Decompiler/ILAst/ILAstTypes.cs

7
ICSharpCode.Decompiler/Ast/AstMetodBodyBuilder.cs

@ -46,10 +46,11 @@ namespace Decompiler
{ {
if (methodDef.Body == null) return null; if (methodDef.Body == null) return null;
List<ILNode> body = new ILAstBuilder().Build(methodDef, true); ILBlock ilMethod = new ILBlock();
ilMethod.Body = new ILAstBuilder().Build(methodDef, true);
ILAstOptimizer bodyGraph = new ILAstOptimizer(); ILAstOptimizer bodyGraph = new ILAstOptimizer();
bodyGraph.Optimize(ref body); bodyGraph.Optimize(ilMethod);
List<string> intNames = new List<string>(new string[] {"i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t"}); 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>(); Dictionary<string, int> typeNames = new Dictionary<string, int>();
@ -95,7 +96,7 @@ namespace Decompiler
// astBlock.Children.Add(astLocalVar); // astBlock.Children.Add(astLocalVar);
} }
Ast.BlockStatement astBlock = TransformBlock(new ILBlock(body)); Ast.BlockStatement astBlock = TransformBlock(ilMethod);
CommentStatement.ReplaceAll(astBlock); // convert CommentStatements to Comments CommentStatement.ReplaceAll(astBlock); // convert CommentStatements to Comments
return astBlock; return astBlock;
} }

149
ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs

@ -12,54 +12,32 @@ namespace Decompiler.ControlFlow
{ {
Dictionary<ILLabel, ControlFlowNode> labelToCfNode = new Dictionary<ILLabel, ControlFlowNode>(); Dictionary<ILLabel, ControlFlowNode> labelToCfNode = new Dictionary<ILLabel, ControlFlowNode>();
public void Optimize(ref List<ILNode> ast) public void Optimize(ILBlock method)
{ {
OptimizeRecursive(ref ast); var blocks = method.GetSelfAndChildrenRecursive<ILBlock>().ToList();
// Provide a container for the algorithms below foreach(ILBlock block in blocks) {
ILBlock astBlock = new ILBlock(ast); ControlFlowGraph graph;
OrderNodes(astBlock); SplitToMovableBlocks(block);
FlattenNestedMovableBlocks(astBlock);
SimpleGotoRemoval(astBlock); graph = BuildGraph(block.Body, block.EntryPoint);
RemoveDeadLabels(astBlock); graph.ComputeDominance();
graph.ComputeDominanceFrontier();
ast = astBlock.Body; block.Body = FindLoops(new HashSet<ControlFlowNode>(graph.Nodes.Skip(3)), graph.EntryPoint);
}
graph = BuildGraph(block.Body, block.EntryPoint);
void OptimizeRecursive(ref List<ILNode> ast) graph.ComputeDominance();
{ graph.ComputeDominanceFrontier();
ILLabel entryLabel; block.Body = FindConditions(new HashSet<ControlFlowNode>(graph.Nodes.Skip(3)), graph.EntryPoint);
List<ILTryCatchBlock> tryCatchBlocks = ast.OfType<ILTryCatchBlock>().ToList();
ControlFlowGraph graph;
ast = SplitToMovableBlocks(ast, out entryLabel);
graph = BuildGraph(ast, entryLabel);
graph.ComputeDominance();
graph.ComputeDominanceFrontier();
ast = FindLoops(new HashSet<ControlFlowNode>(graph.Nodes.Skip(3)), graph.EntryPoint);
graph = BuildGraph(ast, entryLabel);
graph.ComputeDominance();
graph.ComputeDominanceFrontier();
ast = FindConditions(new HashSet<ControlFlowNode>(graph.Nodes.Skip(3)), graph.EntryPoint);
// Recursively optimze try-cath blocks
foreach(ILTryCatchBlock tryCatchBlock in tryCatchBlocks) {
Optimize(ref tryCatchBlock.TryBlock.Body);
foreach(ILTryCatchBlock.CatchBlock catchBlock in tryCatchBlock.CatchBlocks) {
Optimize(ref catchBlock.Body);
}
if (tryCatchBlock.FinallyBlock != null)
Optimize(ref tryCatchBlock.FinallyBlock.Body);
} }
ast.Insert(0, new ILExpression(OpCodes.Br, entryLabel)); OrderNodes(method);
FlattenNestedMovableBlocks(method);
SimpleGotoRemoval(method);
RemoveDeadLabels(method);
} }
class ILMoveableBlock: ILBlock class ILMoveableBlock: ILBlock
{ {
public int OriginalOrder; public int OriginalOrder;
@ -72,47 +50,51 @@ 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>
List<ILNode> SplitToMovableBlocks(List<ILNode> ast, out ILLabel entryLabel) void SplitToMovableBlocks(ILBlock block)
{ {
List<ILNode> blocks = new List<ILNode>(); // Remve no-ops
block.Body = block.Body.Where(n => !(n is ILExpression && ((ILExpression)n).OpCode == OpCodes.Nop)).ToList();
ILMoveableBlock block = new ILMoveableBlock() { OriginalOrder = (nextBlockIndex++) }; List<ILNode> moveableBlocks = new List<ILNode>();
blocks.Add(block);
entryLabel = new ILLabel() { Name = "Block_" + block.OriginalOrder };
block.Body.Add(entryLabel);
if (ast.Count == 0) ILMoveableBlock moveableBlock = new ILMoveableBlock() { OriginalOrder = (nextBlockIndex++) };
return blocks; moveableBlocks.Add(moveableBlock);
block.Body.Add(ast[0]); block.EntryPoint = new ILLabel() { Name = "Block_" + moveableBlock.OriginalOrder };
moveableBlock.Body.Add(block.EntryPoint);
for (int i = 1; i < ast.Count; i++) { if (block.Body.Count > 0) {
ILNode lastNode = ast[i - 1]; moveableBlock.Body.Add(block.Body[0]);
ILNode currNode = ast[i];
// Insert split for (int i = 1; i < block.Body.Count; i++) {
if ((currNode is ILLabel && !(lastNode is ILLabel)) || ILNode lastNode = block.Body[i - 1];
lastNode is ILTryCatchBlock || ILNode currNode = block.Body[i];
currNode is ILTryCatchBlock ||
(lastNode is ILExpression) && ((ILExpression)lastNode).OpCode.IsBranch() ||
(currNode is ILExpression) && ((ILExpression)currNode).OpCode.IsBranch())
{
ILBlock lastBlock = block;
block = new ILMoveableBlock() { OriginalOrder = (nextBlockIndex++) };
blocks.Add(block);
// Explicit branch from one block to other // Insert split
// (unless the last expression was unconditional branch) if ((currNode is ILLabel && !(lastNode is ILLabel)) ||
if (!(lastNode is ILExpression) || ((ILExpression)lastNode).OpCode.CanFallThough()) { lastNode is ILTryCatchBlock ||
ILLabel blockLabel = new ILLabel() { Name = "Block_" + block.OriginalOrder }; currNode is ILTryCatchBlock ||
lastBlock.Body.Add(new ILExpression(OpCodes.Br, blockLabel)); (lastNode is ILExpression) && ((ILExpression)lastNode).OpCode.IsBranch() ||
block.Body.Add(blockLabel); (currNode is ILExpression) && ((ILExpression)currNode).OpCode.IsBranch())
{
ILBlock lastBlock = moveableBlock;
moveableBlock = new ILMoveableBlock() { OriginalOrder = (nextBlockIndex++) };
moveableBlocks.Add(moveableBlock);
// 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_" + moveableBlock.OriginalOrder };
lastBlock.Body.Add(new ILExpression(OpCodes.Br, blockLabel));
moveableBlock.Body.Add(blockLabel);
}
} }
moveableBlock.Body.Add(currNode);
} }
block.Body.Add(currNode);
} }
return blocks; block.Body = moveableBlocks;
return;
} }
ControlFlowGraph BuildGraph(List<ILNode> nodes, ILLabel entryLabel) ControlFlowGraph BuildGraph(List<ILNode> nodes, ILLabel entryLabel)
@ -243,13 +225,13 @@ namespace Decompiler.ControlFlow
labelToCfNode.TryGetValue((ILLabel)statBranch.Operand, out statTarget)) labelToCfNode.TryGetValue((ILLabel)statBranch.Operand, out statTarget))
{ {
ILCondition condition = new ILCondition() { ILCondition condition = new ILCondition() {
Condition = condBranch, Condition = condBranch,
TrueTarget = (ILLabel)condBranch.Operand, TrueBlock = new ILBlock() { EntryPoint = (ILLabel)condBranch.Operand },
FalseTarget = (ILLabel)statBranch.Operand FalseBlock = new ILBlock() { EntryPoint = (ILLabel)statBranch.Operand }
}; };
// TODO: Use the labels to ensre correctness // The label will not be used - kill it
// TODO: Ensure that the labels are considered live in dead label removal condBranch.Operand = null;
// Replace the two branches with a conditional structure // Replace the two branches with a conditional structure
block.Body.Remove(condBranch); block.Body.Remove(condBranch);
@ -265,12 +247,12 @@ namespace Decompiler.ControlFlow
if (!frontiers.Contains(condTarget)) { if (!frontiers.Contains(condTarget)) {
HashSet<ControlFlowNode> content = FindDominatedNodes(nodes, condTarget); HashSet<ControlFlowNode> content = FindDominatedNodes(nodes, condTarget);
nodes.ExceptWith(content); nodes.ExceptWith(content);
condition.TrueBlock = new ILBlock(FindConditions(content, condTarget)); condition.TrueBlock.Body.AddRange(FindConditions(content, condTarget));
} }
if (!frontiers.Contains(statTarget)) { if (!frontiers.Contains(statTarget)) {
HashSet<ControlFlowNode> content = FindDominatedNodes(nodes, statTarget); HashSet<ControlFlowNode> content = FindDominatedNodes(nodes, statTarget);
nodes.ExceptWith(content); nodes.ExceptWith(content);
condition.FalseBlock = new ILBlock(FindConditions(content, statTarget)); condition.FalseBlock.Body.AddRange(FindConditions(content, statTarget));
} }
nodes.Remove(node); nodes.Remove(node);
@ -381,7 +363,8 @@ namespace Decompiler.ControlFlow
void OrderNodes(ILBlock ast) void OrderNodes(ILBlock ast)
{ {
var blocks = ast.GetSelfAndChildrenRecursive<ILBlock>().ToList(); // Order movable nodes
var blocks = ast.GetSelfAndChildrenRecursive<ILBlock>().Where(b => !(b is ILMoveableBlock)).ToList();
ILMoveableBlock first = new ILMoveableBlock() { OriginalOrder = -1 }; ILMoveableBlock first = new ILMoveableBlock() { OriginalOrder = -1 };
foreach(ILBlock block in blocks) { foreach(ILBlock block in blocks) {
block.Body = block.Body.OrderBy(n => (n.GetSelfAndChildrenRecursive<ILMoveableBlock>().FirstOrDefault() ?? first).OriginalOrder).ToList(); block.Body = block.Body.OrderBy(n => (n.GetSelfAndChildrenRecursive<ILMoveableBlock>().FirstOrDefault() ?? first).OriginalOrder).ToList();
@ -396,6 +379,10 @@ namespace Decompiler.ControlFlow
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) {
flatBody.Add(new ILExpression(OpCodes.Br, block.EntryPoint));
block.EntryPoint = null;
}
foreach (ILNode child in block.Body) { foreach (ILNode child in block.Body) {
FlattenNestedMovableBlocks(child); FlattenNestedMovableBlocks(child);
if (child is ILMoveableBlock) { if (child is ILMoveableBlock) {

9
ICSharpCode.Decompiler/ILAst/ILAstTypes.cs

@ -48,6 +48,8 @@ namespace Decompiler
public class ILBlock: ILNode public class ILBlock: ILNode
{ {
public ILLabel EntryPoint;
public List<ILNode> Body; public List<ILNode> Body;
public ILBlock(params ILNode[] body) public ILBlock(params ILNode[] body)
@ -62,7 +64,10 @@ namespace Decompiler
public override IEnumerable<ILNode> GetChildren() public override IEnumerable<ILNode> GetChildren()
{ {
return this.Body; yield return EntryPoint;
foreach(ILNode child in this.Body) {
yield return child;
}
} }
} }
@ -177,9 +182,7 @@ namespace Decompiler
{ {
public ILExpression Condition; public ILExpression Condition;
public ILBlock TrueBlock; // Branch was taken public ILBlock TrueBlock; // Branch was taken
public ILLabel TrueTarget; // Entry label
public ILBlock FalseBlock; // Fall-though public ILBlock FalseBlock; // Fall-though
public ILLabel FalseTarget; // Entry label
public override IEnumerable<ILNode> GetChildren() public override IEnumerable<ILNode> GetChildren()
{ {

Loading…
Cancel
Save