Browse Source

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

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

77
ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

@ -76,6 +76,8 @@ namespace Decompiler @@ -76,6 +76,8 @@ namespace Decompiler
{
Ast.BlockStatement astBlock = new BlockStatement();
if (block != null) {
if (block.EntryGoto != null)
astBlock.AddStatement((Statement)TransformExpression(block.EntryGoto));
foreach(ILNode node in block.Body) {
astBlock.AddStatements(TransformNode(node));
}
@ -104,24 +106,6 @@ namespace Decompiler @@ -104,24 +106,6 @@ namespace Decompiler
yield return new Ast.ForStatement {
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) {
ILCondition conditionalNode = (ILCondition)node;
if (conditionalNode.FalseBlock.Body.Any()) {
@ -148,6 +132,8 @@ namespace Decompiler @@ -148,6 +132,8 @@ namespace Decompiler
switchStmt.SwitchSections.Add(section);
}
yield return switchStmt;
if (ilSwitch.DefaultGoto != null)
yield return (Statement)TransformExpression(ilSwitch.DefaultGoto);
} else if (node is ILTryCatchBlock) {
ILTryCatchBlock tryCatchNode = ((ILTryCatchBlock)node);
var tryCatchStmt = new Ast.TryCatchStatement();
@ -190,6 +176,23 @@ namespace Decompiler @@ -190,6 +176,23 @@ namespace Decompiler
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);
Ast.Expression arg1 = args.Count >= 1 ? args[0] : null;
Ast.Expression arg2 = args.Count >= 2 ? args[1] : null;
@ -229,43 +232,9 @@ namespace Decompiler @@ -229,43 +232,9 @@ namespace Decompiler
return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2);
case Code.Bne_Un:
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:
throw new Exception("Bad operator");
}
} else {
throw new Exception("Bad type");
default:
throw new Exception("Bad opcode");
}
*/
}
AstNode TransformByteCode(ILExpression byteCode, List<Ast.Expression> args)

422
ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs

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

71
ICSharpCode.Decompiler/ILAst/ILAstTypes.cs

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

3
ICSharpCode.Decompiler/ILAst/ILCodes.cs

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

14
ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

@ -103,6 +103,20 @@ namespace Decompiler @@ -103,6 +103,20 @@ namespace Decompiler
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) {
#region Variable load/store
case Code.Stloc:

Loading…
Cancel
Save