Browse Source

Find conditions

pull/10/head
David Srbecký 15 years ago
parent
commit
9fb7d244ed
  1. 64
      ICSharpCode.Decompiler/Ast/AstMetodBodyBuilder.cs
  2. 140
      ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
  3. 14
      ICSharpCode.Decompiler/ILAst/ILAstTypes.cs

64
ICSharpCode.Decompiler/Ast/AstMetodBodyBuilder.cs

@ -150,15 +150,12 @@ namespace Decompiler
*/ */
} else if (node is ILCondition) { } else if (node is ILCondition) {
ILCondition conditionalNode = (ILCondition)node; ILCondition conditionalNode = (ILCondition)node;
yield return TransformBlock(conditionalNode.ConditionBlock); // Swap bodies
yield return new Ast.IfElseStatement {
Ast.IfElseStatement ifElseStmt = new Ast.IfElseStatement { Condition = new UnaryOperatorExpression(UnaryOperatorType.Not, MakeBranchCondition(conditionalNode.Condition)),
Condition = new PrimitiveExpression(true), TrueStatement = TransformBlock(conditionalNode.FalseBlock),
TrueStatement = TransformBlock(conditionalNode.Block1), FalseStatement = TransformBlock(conditionalNode.TrueBlock)
FalseStatement = TransformBlock(conditionalNode.Block2)
}; };
yield return ifElseStmt;
} else if (node is ILTryCatchBlock) { } else if (node is ILTryCatchBlock) {
ILTryCatchBlock tryCachNode = ((ILTryCatchBlock)node); ILTryCatchBlock tryCachNode = ((ILTryCatchBlock)node);
List<Ast.CatchClause> catchClauses = new List<CatchClause>(); List<Ast.CatchClause> catchClauses = new List<CatchClause>();
@ -197,35 +194,27 @@ namespace Decompiler
return TransformByteCode(methodDef, expr, args); return TransformByteCode(methodDef, expr, args);
} }
/* Ast.Expression MakeBranchCondition(ILExpression expr)
Ast.Expression MakeBranchCondition(Branch branch)
{
return MakeBranchCondition_Internal(branch);
}
Ast.Expression MakeBranchCondition_Internal(Branch branch)
{ {
if (branch is SimpleBranch) { List<Ast.Expression> args = TransformExpressionArguments(expr);
List<Ast.Expression> args = TransformExpressionArguments((ILExpression)((SimpleBranch)branch).BasicBlock.Body[0]); Ast.Expression arg1 = args.Count >= 1 ? args[0] : null;
Ast.Expression arg1 = args.Count >= 1 ? args[0] : null; Ast.Expression arg2 = args.Count >= 2 ? args[1] : null;
Ast.Expression arg2 = args.Count >= 2 ? args[1] : null; switch(expr.OpCode.Code) {
switch(((ILExpression)((SimpleBranch)branch).BasicBlock.Body[0]).OpCode.Code) { case Code.Brfalse: return new Ast.UnaryOperatorExpression(UnaryOperatorType.Not, arg1);
case Code.Brfalse: return new Ast.UnaryOperatorExpression(UnaryOperatorType.Not, arg1); case Code.Brtrue: return arg1;
case Code.Brtrue: return arg1; case Code.Beq: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, arg2);
case Code.Beq: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, arg2); case Code.Bge: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThanOrEqual, arg2);
case Code.Bge: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThanOrEqual, arg2); case Code.Bge_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThanOrEqual, arg2);
case Code.Bge_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThanOrEqual, arg2); case Code.Bgt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2);
case Code.Bgt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); case Code.Bgt_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2);
case Code.Bgt_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); case Code.Ble: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThanOrEqual, arg2);
case Code.Ble: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThanOrEqual, arg2); case Code.Ble_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThanOrEqual, arg2);
case Code.Ble_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThanOrEqual, arg2); case Code.Blt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2);
case Code.Blt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); case Code.Blt_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2);
case Code.Blt_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); case Code.Bne_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.InEquality, arg2);
case Code.Bne_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.InEquality, arg2); default: throw new Exception("Bad opcode");
case Code.Leave: return new Ast.PrimitiveExpression(true); }
default: throw new Exception("Bad opcode"); /*
}
} else if (branch is ShortCircuitBranch) { } else if (branch is ShortCircuitBranch) {
ShortCircuitBranch scBranch = (ShortCircuitBranch)branch; ShortCircuitBranch scBranch = (ShortCircuitBranch)branch;
switch(scBranch.Operator) { switch(scBranch.Operator) {
@ -259,10 +248,9 @@ namespace Decompiler
} else { } else {
throw new Exception("Bad type"); throw new Exception("Bad type");
} }
*/
} }
*/
static object TransformByteCode(MethodDefinition methodDef, ILExpression byteCode, List<Ast.Expression> args) static object TransformByteCode(MethodDefinition methodDef, ILExpression byteCode, List<Ast.Expression> args)
{ {
try { try {

140
ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs

@ -10,6 +10,8 @@ namespace Decompiler.ControlFlow
{ {
public class ILAstOptimizer public class ILAstOptimizer
{ {
Dictionary<ILLabel, ControlFlowNode> labelToCfNode = new Dictionary<ILLabel, ControlFlowNode>();
public void Optimize(ref List<ILNode> ast) public void Optimize(ref List<ILNode> ast)
{ {
OptimizeRecursive(ref ast); OptimizeRecursive(ref ast);
@ -17,6 +19,7 @@ namespace Decompiler.ControlFlow
// Provide a container for the algorithms below // Provide a container for the algorithms below
ILBlock astBlock = new ILBlock(ast); ILBlock astBlock = new ILBlock(ast);
OrderNodes(astBlock);
FlattenNestedMovableBlocks(astBlock); FlattenNestedMovableBlocks(astBlock);
SimpleGotoRemoval(astBlock); SimpleGotoRemoval(astBlock);
RemoveDeadLabels(astBlock); RemoveDeadLabels(astBlock);
@ -52,14 +55,11 @@ namespace Decompiler.ControlFlow
Optimize(ref 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)); ast.Insert(0, new ILExpression(OpCodes.Br, entryLabel));
} }
class ILMoveAbleBlock: ILBlock class ILMoveableBlock: ILBlock
{ {
public int OriginalOrder; public int OriginalOrder;
} }
@ -75,7 +75,7 @@ namespace Decompiler.ControlFlow
{ {
List<ILNode> blocks = new List<ILNode>(); List<ILNode> blocks = new List<ILNode>();
ILMoveAbleBlock block = new ILMoveAbleBlock() { OriginalOrder = (nextBlockIndex++) }; ILMoveableBlock block = new ILMoveableBlock() { OriginalOrder = (nextBlockIndex++) };
blocks.Add(block); blocks.Add(block);
entryLabel = new ILLabel() { Name = "Block_" + block.OriginalOrder }; entryLabel = new ILLabel() { Name = "Block_" + block.OriginalOrder };
block.Body.Add(entryLabel); block.Body.Add(entryLabel);
@ -96,7 +96,7 @@ namespace Decompiler.ControlFlow
(currNode is ILExpression) && ((ILExpression)currNode).OpCode.IsBranch()) (currNode is ILExpression) && ((ILExpression)currNode).OpCode.IsBranch())
{ {
ILBlock lastBlock = block; ILBlock lastBlock = block;
block = new ILMoveAbleBlock() { OriginalOrder = (nextBlockIndex++) }; block = new ILMoveableBlock() { OriginalOrder = (nextBlockIndex++) };
blocks.Add(block); blocks.Add(block);
// Explicit branch from one block to other // Explicit branch from one block to other
@ -126,7 +126,7 @@ namespace Decompiler.ControlFlow
cfNodes.Add(exceptionalExit); cfNodes.Add(exceptionalExit);
// Create graph nodes // Create graph nodes
Dictionary<ILLabel, ControlFlowNode> labelToCfNode = new Dictionary<ILLabel, ControlFlowNode>(); labelToCfNode = new Dictionary<ILLabel, ControlFlowNode>();
Dictionary<ILNode, ControlFlowNode> astNodeToCfNode = new Dictionary<ILNode, ControlFlowNode>(); Dictionary<ILNode, ControlFlowNode> astNodeToCfNode = new Dictionary<ILNode, ControlFlowNode>();
foreach(ILNode node in nodes) { foreach(ILNode node in nodes) {
ControlFlowNode cfNode = new ControlFlowNode(index++, -1, ControlFlowNodeType.Normal); ControlFlowNode cfNode = new ControlFlowNode(index++, -1, ControlFlowNodeType.Normal);
@ -214,28 +214,7 @@ namespace Decompiler.ControlFlow
} }
} }
static HashSet<ControlFlowNode> FindDominatedNodes(HashSet<ControlFlowNode> nodes, ControlFlowNode head) List<ILNode> FindConditions(HashSet<ControlFlowNode> nodes, ControlFlowNode entryNode)
{
var exitNodes = head.DominanceFrontier.SelectMany(n => n.Predecessors);
HashSet<ControlFlowNode> agenda = new HashSet<ControlFlowNode>(exitNodes);
HashSet<ControlFlowNode> result = new HashSet<ControlFlowNode>();
while(agenda.Count > 0) {
ControlFlowNode addNode = agenda.First();
agenda.Remove(addNode);
if (nodes.Contains(addNode) && head.Dominates(addNode) && result.Add(addNode)) {
foreach (var predecessor in addNode.Predecessors) {
agenda.Add(predecessor);
}
}
}
result.Add(head);
return result;
}
static List<ILNode> FindConditions(HashSet<ControlFlowNode> nodes, ControlFlowNode entryNode)
{ {
List<ILNode> result = new List<ILNode>(); List<ILNode> result = new List<ILNode>();
@ -244,25 +223,58 @@ namespace Decompiler.ControlFlow
while(agenda.Count > 0) { while(agenda.Count > 0) {
ControlFlowNode node = agenda.Dequeue(); ControlFlowNode node = agenda.Dequeue();
if (nodes.Contains(node) && node.Outgoing.Count == 2) { ILMoveableBlock block = node.UserData as ILMoveableBlock;
ILCondition condition = new ILCondition() {
ConditionBlock = new ILBlock((ILNode)node.UserData) // Find a block that represents a simple condition
}; if (nodes.Contains(node) && block != null && block.Body.Count == 3) {
HashSet<ControlFlowNode> frontiers = new HashSet<ControlFlowNode>();
frontiers.UnionWith(node.Outgoing[0].Target.DominanceFrontier); ILLabel label = block.Body[0] as ILLabel;
frontiers.UnionWith(node.Outgoing[1].Target.DominanceFrontier); ILExpression condBranch = block.Body[1] as ILExpression;
if (!frontiers.Contains(node.Outgoing[0].Target)) { ILExpression statBranch = block.Body[2] as ILExpression;
HashSet<ControlFlowNode> content1 = FindDominatedNodes(nodes, node.Outgoing[0].Target);
nodes.ExceptWith(content1); if (label != null &&
condition.Block1 = new ILBlock(FindConditions(content1, node.Outgoing[0].Target)); condBranch != null && condBranch.Operand is ILLabel && condBranch.Arguments.Count > 0 &&
} statBranch != null && statBranch.Operand is ILLabel && statBranch.Arguments.Count == 0)
if (!frontiers.Contains(node.Outgoing[1].Target)) { {
HashSet<ControlFlowNode> content2 = FindDominatedNodes(nodes, node.Outgoing[1].Target); ControlFlowNode condTarget;
nodes.ExceptWith(content2); ControlFlowNode statTarget;
condition.Block2 = new ILBlock(FindConditions(content2, node.Outgoing[1].Target)); if (labelToCfNode.TryGetValue((ILLabel)condBranch.Operand, out condTarget) &&
labelToCfNode.TryGetValue((ILLabel)statBranch.Operand, out statTarget))
{
ILCondition condition = new ILCondition() {
Condition = condBranch,
TrueTarget = (ILLabel)condBranch.Operand,
FalseTarget = (ILLabel)statBranch.Operand
};
// TODO: Use the labels to ensre correctness
// TODO: Ensure that the labels are considered live in dead label removal
// Replace the two branches with a conditional structure
block.Body.Remove(condBranch);
block.Body.Remove(statBranch);
block.Body.Add(condition);
result.Add(block);
// Pull in the conditional code
HashSet<ControlFlowNode> frontiers = new HashSet<ControlFlowNode>();
frontiers.UnionWith(condTarget.DominanceFrontier);
frontiers.UnionWith(statTarget.DominanceFrontier);
if (!frontiers.Contains(condTarget)) {
HashSet<ControlFlowNode> content = FindDominatedNodes(nodes, condTarget);
nodes.ExceptWith(content);
condition.TrueBlock = new ILBlock(FindConditions(content, condTarget));
}
if (!frontiers.Contains(statTarget)) {
HashSet<ControlFlowNode> content = FindDominatedNodes(nodes, statTarget);
nodes.ExceptWith(content);
condition.FalseBlock = new ILBlock(FindConditions(content, statTarget));
}
nodes.Remove(node);
}
} }
nodes.Remove(node);
result.Add(condition);
} }
// 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
@ -279,6 +291,27 @@ namespace Decompiler.ControlFlow
return result; return result;
} }
static HashSet<ControlFlowNode> FindDominatedNodes(HashSet<ControlFlowNode> nodes, ControlFlowNode head)
{
var exitNodes = head.DominanceFrontier.SelectMany(n => n.Predecessors);
HashSet<ControlFlowNode> agenda = new HashSet<ControlFlowNode>(exitNodes);
HashSet<ControlFlowNode> result = new HashSet<ControlFlowNode>();
while(agenda.Count > 0) {
ControlFlowNode addNode = agenda.First();
agenda.Remove(addNode);
if (nodes.Contains(addNode) && head.Dominates(addNode) && result.Add(addNode)) {
foreach (var predecessor in addNode.Predecessors) {
agenda.Add(predecessor);
}
}
}
result.Add(head);
return result;
}
/* /*
public enum ShortCircuitOperator public enum ShortCircuitOperator
@ -345,6 +378,15 @@ namespace Decompiler.ControlFlow
*/ */
void OrderNodes(ILBlock ast)
{
var blocks = ast.GetSelfAndChildrenRecursive<ILBlock>().ToList();
ILMoveableBlock first = new ILMoveableBlock() { OriginalOrder = -1 };
foreach(ILBlock block in blocks) {
block.Body = block.Body.OrderBy(n => (n.GetSelfAndChildrenRecursive<ILMoveableBlock>().FirstOrDefault() ?? first).OriginalOrder).ToList();
}
}
/// <summary> /// <summary>
/// Flattens all nested movable blocks, except the the top level 'node' argument /// Flattens all nested movable blocks, except the the top level 'node' argument
/// </summary> /// </summary>
@ -355,8 +397,8 @@ namespace Decompiler.ControlFlow
List<ILNode> flatBody = new List<ILNode>(); List<ILNode> flatBody = new List<ILNode>();
foreach (ILNode child in block.Body) { foreach (ILNode child in block.Body) {
FlattenNestedMovableBlocks(child); FlattenNestedMovableBlocks(child);
if (child is ILMoveAbleBlock) { if (child is ILMoveableBlock) {
flatBody.AddRange(((ILMoveAbleBlock)child).Body); flatBody.AddRange(((ILMoveableBlock)child).Body);
} else { } else {
flatBody.Add(child); flatBody.Add(child);
} }

14
ICSharpCode.Decompiler/ILAst/ILAstTypes.cs

@ -174,15 +174,17 @@ namespace Decompiler
public class ILCondition: ILNode public class ILCondition: ILNode
{ {
public ILBlock ConditionBlock; public ILExpression Condition;
public ILBlock Block1; public ILBlock TrueBlock; // Branch was taken
public ILBlock Block2; public ILLabel TrueTarget; // Entry label
public ILBlock FalseBlock; // Fall-though
public ILLabel FalseTarget; // Entry label
public override IEnumerable<ILNode> GetChildren() public override IEnumerable<ILNode> GetChildren()
{ {
yield return ConditionBlock; yield return Condition;
yield return Block1; yield return TrueBlock;
yield return Block2; yield return FalseBlock;
} }
} }
} }

Loading…
Cancel
Save