Browse Source

Support for short circuit conditional operators (&& and ||)

pull/37/head
David Srbecký 15 years ago
parent
commit
7c4111267a
  1. 75
      ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
  2. 412
      ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
  3. 65
      ICSharpCode.Decompiler/ILAst/ILAstTypes.cs
  4. 3
      ICSharpCode.Decompiler/ILAst/ILCodes.cs
  5. 14
      ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

75
ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

@ -76,6 +76,8 @@ namespace Decompiler
{ {
Ast.BlockStatement astBlock = new BlockStatement(); Ast.BlockStatement astBlock = new BlockStatement();
if (block != null) { if (block != null) {
if (block.EntryGoto != null)
astBlock.AddStatement((Statement)TransformExpression(block.EntryGoto));
foreach(ILNode node in block.Body) { foreach(ILNode node in block.Body) {
astBlock.AddStatements(TransformNode(node)); astBlock.AddStatements(TransformNode(node));
} }
@ -104,24 +106,6 @@ namespace Decompiler
yield return new Ast.ForStatement { yield return new Ast.ForStatement {
EmbeddedStatement = TransformBlock(((ILLoop)node).ContentBlock) EmbeddedStatement = TransformBlock(((ILLoop)node).ContentBlock)
}; };
/*
} else if (node is Branch) {
yield return new Ast.LabelStatement { Label = ((Branch)node).FirstBasicBlock.Label };
Ast.BlockStatement trueBlock = new Ast.BlockStatement();
trueBlock.AddStatement(new Ast.GotoStatement(((Branch)node).TrueSuccessor.Label));
Ast.BlockStatement falseBlock = new Ast.BlockStatement();
falseBlock.AddStatement(new Ast.GotoStatement(((Branch)node).FalseSuccessor.Label));
Ast.IfElseStatement ifElseStmt = new Ast.IfElseStatement {
Condition = MakeBranchCondition((Branch)node),
TrueStatement = trueBlock,
FalseStatement = falseBlock
};
yield return ifElseStmt;
*/
} else if (node is ILCondition) { } else if (node is ILCondition) {
ILCondition conditionalNode = (ILCondition)node; ILCondition conditionalNode = (ILCondition)node;
if (conditionalNode.FalseBlock.Body.Any()) { if (conditionalNode.FalseBlock.Body.Any()) {
@ -148,6 +132,8 @@ namespace Decompiler
switchStmt.SwitchSections.Add(section); switchStmt.SwitchSections.Add(section);
} }
yield return switchStmt; yield return switchStmt;
if (ilSwitch.DefaultGoto != null)
yield return (Statement)TransformExpression(ilSwitch.DefaultGoto);
} else if (node is ILTryCatchBlock) { } else if (node is ILTryCatchBlock) {
ILTryCatchBlock tryCatchNode = ((ILTryCatchBlock)node); ILTryCatchBlock tryCatchNode = ((ILTryCatchBlock)node);
var tryCatchStmt = new Ast.TryCatchStatement(); var tryCatchStmt = new Ast.TryCatchStatement();
@ -190,6 +176,23 @@ namespace Decompiler
Ast.Expression MakeBranchCondition(ILExpression expr) Ast.Expression MakeBranchCondition(ILExpression expr)
{ {
switch(expr.Code) {
case ILCode.LogicNot:
return new Ast.UnaryOperatorExpression(UnaryOperatorType.Not, MakeBranchCondition(expr.Arguments[0]));
case ILCode.LogicAnd:
return new Ast.BinaryOperatorExpression(
MakeBranchCondition(expr.Arguments[0]),
BinaryOperatorType.ConditionalAnd,
MakeBranchCondition(expr.Arguments[1])
);
case ILCode.LogicOr:
return new Ast.BinaryOperatorExpression(
MakeBranchCondition(expr.Arguments[0]),
BinaryOperatorType.ConditionalOr,
MakeBranchCondition(expr.Arguments[1])
);
}
List<Ast.Expression> args = TransformExpressionArguments(expr); List<Ast.Expression> args = TransformExpressionArguments(expr);
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;
@ -229,43 +232,9 @@ namespace Decompiler
return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2);
case Code.Bne_Un: case Code.Bne_Un:
return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.InEquality, arg2); return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.InEquality, arg2);
default: throw new Exception("Bad opcode");
}
/*
} else if (branch is ShortCircuitBranch) {
ShortCircuitBranch scBranch = (ShortCircuitBranch)branch;
switch(scBranch.Operator) {
case ShortCircuitOperator.LeftAndRight:
return new BinaryOperatorExpression(
MakeBranchCondition(scBranch.Left),
BinaryOperatorType.ConditionalAnd,
MakeBranchCondition(scBranch.Right)
);
case ShortCircuitOperator.LeftOrRight:
return new BinaryOperatorExpression(
MakeBranchCondition(scBranch.Left),
BinaryOperatorType.ConditionalOr,
MakeBranchCondition(scBranch.Right)
);
case ShortCircuitOperator.NotLeftAndRight:
return new BinaryOperatorExpression(
new UnaryOperatorExpression(UnaryOperatorType.Not, MakeBranchCondition(scBranch.Left)),
BinaryOperatorType.ConditionalAnd,
MakeBranchCondition(scBranch.Right)
);
case ShortCircuitOperator.NotLeftOrRight:
return new BinaryOperatorExpression(
new UnaryOperatorExpression(UnaryOperatorType.Not, MakeBranchCondition(scBranch.Left)),
BinaryOperatorType.ConditionalOr,
MakeBranchCondition(scBranch.Right)
);
default: default:
throw new Exception("Bad operator"); throw new Exception("Bad opcode");
}
} else {
throw new Exception("Bad type");
} }
*/
} }
AstNode TransformByteCode(ILExpression byteCode, List<Ast.Expression> args) AstNode TransformByteCode(ILExpression byteCode, List<Ast.Expression> args)

412
ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs

@ -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);
} }
} }
} }

65
ICSharpCode.Decompiler/ILAst/ILAstTypes.cs

@ -3,13 +3,13 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using Decompiler.ControlFlow; using Decompiler.ControlFlow;
using ICSharpCode.Decompiler; using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.Decompiler.Disassembler;
using ICSharpCode.NRefactory.Utils; using ICSharpCode.NRefactory.Utils;
using Mono.Cecil; using Mono.Cecil;
using Mono.Cecil.Cil; using Mono.Cecil.Cil;
using Mono.CSharp;
using Cecil = Mono.Cecil; using Cecil = Mono.Cecil;
namespace Decompiler namespace Decompiler
@ -38,8 +38,7 @@ namespace Decompiler
public class ILBlock: ILNode public class ILBlock: ILNode
{ {
// TODO: This should really be a goto, not a label public ILExpression EntryGoto;
public ILLabel EntryPoint;
public List<ILNode> Body; public List<ILNode> Body;
@ -55,8 +54,8 @@ namespace Decompiler
public override IEnumerable<ILNode> GetChildren() public override IEnumerable<ILNode> GetChildren()
{ {
if (EntryPoint != null) if (this.EntryGoto != null)
yield return EntryPoint; yield return this.EntryGoto;
foreach(ILNode child in this.Body) { foreach(ILNode child in this.Body) {
yield return child; yield return child;
} }
@ -64,9 +63,33 @@ namespace Decompiler
public override void WriteTo(ITextOutput output) public override void WriteTo(ITextOutput output)
{ {
if (EntryPoint != null) foreach(ILNode child in this.GetChildren()) {
EntryPoint.WriteTo(output); child.WriteTo(output);
output.WriteLine();
}
}
}
public class ILBasicBlock: ILNode
{
public ILLabel EntryLabel;
public List<ILNode> Body = new List<ILNode>();
public ILExpression FallthoughGoto;
public override IEnumerable<ILNode> GetChildren()
{
if (this.EntryLabel != null)
yield return this.EntryLabel;
foreach (ILNode child in this.Body) { foreach (ILNode child in this.Body) {
yield return child;
}
if (this.FallthoughGoto != null)
yield return this.FallthoughGoto;
}
public override void WriteTo(ITextOutput output)
{
foreach(ILNode child in this.GetChildren()) {
child.WriteTo(output); child.WriteTo(output);
output.WriteLine(); output.WriteLine();
} }
@ -119,6 +142,7 @@ namespace Decompiler
public override IEnumerable<ILNode> GetChildren() public override IEnumerable<ILNode> GetChildren()
{ {
if (this.TryBlock != null)
yield return this.TryBlock; yield return this.TryBlock;
foreach (var catchBlock in this.CatchBlocks) { foreach (var catchBlock in this.CatchBlocks) {
yield return catchBlock; yield return catchBlock;
@ -188,6 +212,11 @@ namespace Decompiler
this.ILRanges = new List<ILRange>(1); this.ILRanges = new List<ILRange>(1);
} }
public override IEnumerable<ILNode> GetChildren()
{
return Arguments;
}
public bool IsBranch() public bool IsBranch()
{ {
return this.Operand is ILLabel || this.Operand is ILLabel[]; return this.Operand is ILLabel || this.Operand is ILLabel[];
@ -225,11 +254,6 @@ namespace Decompiler
return ranges; return ranges;
} }
public override IEnumerable<ILNode> GetChildren()
{
return Arguments;
}
public override void WriteTo(ITextOutput output) public override void WriteTo(ITextOutput output)
{ {
if (Operand is ILVariable && ((ILVariable)Operand).IsGenerated) { if (Operand is ILVariable && ((ILVariable)Operand).IsGenerated) {
@ -283,6 +307,7 @@ namespace Decompiler
public override IEnumerable<ILNode> GetChildren() public override IEnumerable<ILNode> GetChildren()
{ {
if (this.ContentBlock != null)
yield return ContentBlock; yield return ContentBlock;
} }
@ -304,10 +329,12 @@ namespace Decompiler
public override IEnumerable<ILNode> GetChildren() public override IEnumerable<ILNode> GetChildren()
{ {
yield return Condition; if (this.Condition != null)
yield return TrueBlock; yield return this.Condition;
if (FalseBlock != null) if (this.TrueBlock != null)
yield return FalseBlock; yield return this.TrueBlock;
if (this.FalseBlock != null)
yield return this.FalseBlock;
} }
public override void WriteTo(ITextOutput output) public override void WriteTo(ITextOutput output)
@ -333,13 +360,17 @@ namespace Decompiler
{ {
public ILExpression Condition; public ILExpression Condition;
public List<ILBlock> CaseBlocks = new List<ILBlock>(); public List<ILBlock> CaseBlocks = new List<ILBlock>();
public ILExpression DefaultGoto;
public override IEnumerable<ILNode> GetChildren() public override IEnumerable<ILNode> GetChildren()
{ {
yield return Condition; if (this.Condition != null)
yield return this.Condition;
foreach (ILBlock caseBlock in this.CaseBlocks) { foreach (ILBlock caseBlock in this.CaseBlocks) {
yield return caseBlock; yield return caseBlock;
} }
if (this.DefaultGoto != null)
yield return this.DefaultGoto;
} }
public override void WriteTo(ITextOutput output) public override void WriteTo(ITextOutput output)

3
ICSharpCode.Decompiler/ILAst/ILCodes.cs

@ -255,6 +255,9 @@ namespace Decompiler
// Virtual codes - defined for convenience // Virtual codes - defined for convenience
Ldexception, // Operand holds the CatchType for catch handler, null for filter Ldexception, // Operand holds the CatchType for catch handler, null for filter
LogicNot,
LogicAnd,
LogicOr
} }
public static class ILCodeUtil public static class ILCodeUtil

14
ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

@ -103,6 +103,20 @@ namespace Decompiler
TypeReference DoInferTypeForExpression(ILExpression expr, TypeReference expectedType, bool forceInferChildren = false) TypeReference DoInferTypeForExpression(ILExpression expr, TypeReference expectedType, bool forceInferChildren = false)
{ {
switch (expr.Code) {
case ILCode.LogicNot:
if (forceInferChildren) {
InferTypeForExpression(expr.Arguments.Single(), typeSystem.Boolean);
}
return typeSystem.Boolean;
case ILCode.LogicAnd:
case ILCode.LogicOr:
if (forceInferChildren) {
InferTypeForExpression(expr.Arguments[0], typeSystem.Boolean);
InferTypeForExpression(expr.Arguments[0], typeSystem.Boolean);
}
return typeSystem.Boolean;
}
switch ((Code)expr.Code) { switch ((Code)expr.Code) {
#region Variable load/store #region Variable load/store
case Code.Stloc: case Code.Stloc:

Loading…
Cancel
Save