Browse Source

Do not put brtrue into its own basic block - store it together with the previous instructions.

Handle single-statement loops.
pull/100/head
David Srbecký 15 years ago
parent
commit
6046fdcdfb
  1. 4
      ICSharpCode.Decompiler/ILAst/GotoRemoval.cs
  2. 117
      ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
  3. 92
      ICSharpCode.Decompiler/ILAst/LoopsAndConditions.cs
  4. 16
      ICSharpCode.Decompiler/ILAst/PatternMatching.cs

4
ICSharpCode.Decompiler/ILAst/GotoRemoval.cs

@ -18,8 +18,8 @@ namespace ICSharpCode.Decompiler.ILAst
foreach (ILNode node in method.GetSelfAndChildrenRecursive<ILNode>()) { foreach (ILNode node in method.GetSelfAndChildrenRecursive<ILNode>()) {
ILNode previousChild = null; ILNode previousChild = null;
foreach (ILNode child in node.GetChildren()) { foreach (ILNode child in node.GetChildren()) {
// TODO: Add back if (parent.ContainsKey(child))
// Debug.Assert(!parent.ContainsKey(child)); throw new Exception("The following expression is linked from several locations: " + child.ToString());
parent[child] = node; parent[child] = node;
if (previousChild != null) if (previousChild != null)
nextSibling[previousChild] = child; nextSibling[previousChild] = child;

117
ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs

@ -168,6 +168,7 @@ namespace ICSharpCode.Decompiler.ILAst
if (abortBeforeStep == ILAstOptimizationStep.CachedDelegateInitialization) return; if (abortBeforeStep == ILAstOptimizationStep.CachedDelegateInitialization) return;
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) { foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
for (int i = 0; i < block.Body.Count; i++) { for (int i = 0; i < block.Body.Count; i++) {
// TODO: Move before loops
CachedDelegateInitialization(block, ref i); CachedDelegateInitialization(block, ref i);
} }
} }
@ -175,6 +176,7 @@ namespace ICSharpCode.Decompiler.ILAst
if (abortBeforeStep == ILAstOptimizationStep.IntroduceFixedStatements) return; if (abortBeforeStep == ILAstOptimizationStep.IntroduceFixedStatements) return;
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) { foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
for (int i = 0; i < block.Body.Count; i++) { for (int i = 0; i < block.Body.Count; i++) {
// TODO: Move before loops
IntroduceFixedStatements(block.Body, i); IntroduceFixedStatements(block.Body, i);
} }
} }
@ -285,59 +287,51 @@ namespace ICSharpCode.Decompiler.ILAst
List<ILNode> basicBlocks = new List<ILNode>(); List<ILNode> basicBlocks = new List<ILNode>();
ILBasicBlock basicBlock = new ILBasicBlock() { ILBasicBlock basicBlock = new ILBasicBlock() {
EntryLabel = new ILLabel() { Name = "Block_" + (nextLabelIndex++) } EntryLabel = block.Body.FirstOrDefault() as ILLabel ?? new ILLabel() { Name = "Block_" + (nextLabelIndex++) }
}; };
basicBlocks.Add(basicBlock); basicBlocks.Add(basicBlock);
block.EntryGoto = new ILExpression(ILCode.Br, basicBlock.EntryLabel); block.EntryGoto = new ILExpression(ILCode.Br, basicBlock.EntryLabel);
if (block.Body.Count > 0) { if (block.Body.Count > 0) {
if (block.Body[0] != basicBlock.EntryLabel)
basicBlock.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];
// Insert split // Start a new basic block if necessary
if (currNode is ILLabel || if (currNode is ILLabel ||
lastNode is ILTryCatchBlock || lastNode is ILTryCatchBlock ||
currNode is ILTryCatchBlock || currNode is ILTryCatchBlock ||
(lastNode is ILExpression) && ((ILExpression)lastNode).IsBranch() || (lastNode is ILExpression && ((ILExpression)lastNode).IsBranch()))
(currNode is ILExpression) && (((ILExpression)currNode).IsBranch() && ((ILExpression)currNode).Code.CanFallThough() && basicBlock.Body.Count > 0))
{ {
ILBasicBlock lastBlock = basicBlock; // Try to reuse the label
basicBlock = new ILBasicBlock(); ILLabel label = currNode is ILLabel ? ((ILLabel)currNode) : new ILLabel() { Name = "Block_" + (nextLabelIndex++) };
basicBlocks.Add(basicBlock);
if (currNode is ILLabel) { // Terminate the last block
// Insert as entry label if (lastNode.CanFallThough()) {
basicBlock.EntryLabel = (ILLabel)currNode; // Explicit branch from one block to other
} else { basicBlock.FallthoughGoto = new ILExpression(ILCode.Br, label);
basicBlock.EntryLabel = new ILLabel() { Name = "Block_" + (nextLabelIndex++) }; } else if (lastNode.Match(ILCode.Br)) {
basicBlock.Body.Add(currNode); // Reuse the existing goto as FallthoughGoto
basicBlock.FallthoughGoto = (ILExpression)lastNode;
basicBlock.Body.RemoveAt(basicBlock.Body.Count - 1);
} }
// Explicit branch from one block to other // Start the new block
// (unless the last expression was unconditional branch) basicBlock = new ILBasicBlock();
if (!(lastNode is ILExpression) || ((ILExpression)lastNode).Code.CanFallThough()) { basicBlocks.Add(basicBlock);
lastBlock.FallthoughGoto = new ILExpression(ILCode.Br, basicBlock.EntryLabel); basicBlock.EntryLabel = label;
} }
} else {
// Add the node to the basic block
if (currNode != basicBlock.EntryLabel) {
basicBlock.Body.Add(currNode); basicBlock.Body.Add(currNode);
} }
} }
} }
foreach (ILBasicBlock bb in basicBlocks) {
if (bb.Body.Count > 0 &&
bb.Body.Last() is ILExpression &&
((ILExpression)bb.Body.Last()).Code == ILCode.Br)
{
Debug.Assert(bb.FallthoughGoto == null);
bb.FallthoughGoto = (ILExpression)bb.Body.Last();
bb.Body.RemoveAt(bb.Body.Count - 1);
}
}
block.Body = basicBlocks; block.Body = basicBlocks;
return; return;
} }
@ -362,7 +356,6 @@ namespace ICSharpCode.Decompiler.ILAst
} }
} }
// scope is modified if successful
bool SimplifyTernaryOperator(List<ILNode> body, ILBasicBlock head, int pos) bool SimplifyTernaryOperator(List<ILNode> body, ILBasicBlock head, int pos)
{ {
Debug.Assert(body.Contains(head)); Debug.Assert(body.Contains(head));
@ -378,13 +371,13 @@ namespace ICSharpCode.Decompiler.ILAst
ILLabel falseFall; ILLabel falseFall;
object unused; object unused;
if (head.Match(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel) && if (head.MatchLast(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel) &&
labelGlobalRefCount[trueLabel] == 1 && labelGlobalRefCount[trueLabel] == 1 &&
labelGlobalRefCount[falseLabel] == 1 && labelGlobalRefCount[falseLabel] == 1 &&
((labelToBasicBlock[trueLabel].Match(ILCode.Stloc, out trueLocVar, out trueExpr, out trueFall) && ((labelToBasicBlock[trueLabel].MatchSingle(ILCode.Stloc, out trueLocVar, out trueExpr, out trueFall) &&
labelToBasicBlock[falseLabel].Match(ILCode.Stloc, out falseLocVar, out falseExpr, out falseFall)) || labelToBasicBlock[falseLabel].MatchSingle(ILCode.Stloc, out falseLocVar, out falseExpr, out falseFall)) ||
(labelToBasicBlock[trueLabel].Match(ILCode.Ret, out unused, out trueExpr, out trueFall) && (labelToBasicBlock[trueLabel].MatchSingle(ILCode.Ret, out unused, out trueExpr, out trueFall) &&
labelToBasicBlock[falseLabel].Match(ILCode.Ret, out unused, out falseExpr, out falseFall))) && labelToBasicBlock[falseLabel].MatchSingle(ILCode.Ret, out unused, out falseExpr, out falseFall))) &&
trueLocVar == falseLocVar && trueLocVar == falseLocVar &&
trueFall == falseFall && trueFall == falseFall &&
body.Contains(labelToBasicBlock[trueLabel]) && body.Contains(labelToBasicBlock[trueLabel]) &&
@ -436,7 +429,7 @@ namespace ICSharpCode.Decompiler.ILAst
// Create ternary expression // Create ternary expression
newExpr = new ILExpression(ILCode.TernaryOp, null, condExpr, trueExpr, falseExpr); newExpr = new ILExpression(ILCode.TernaryOp, null, condExpr, trueExpr, falseExpr);
} }
head.Body = new List<ILNode>() { new ILExpression(opCode, trueLocVar, newExpr) }; head.Body[head.Body.Count - 1] = new ILExpression(opCode, trueLocVar, newExpr);
head.FallthoughGoto = trueFall != null ? new ILExpression(ILCode.Br, trueFall) : null; head.FallthoughGoto = trueFall != null ? new ILExpression(ILCode.Br, trueFall) : null;
// Remove the old basic blocks // Remove the old basic blocks
@ -455,57 +448,45 @@ namespace ICSharpCode.Decompiler.ILAst
{ {
// ... // ...
// v = ldloc(leftVar) // v = ldloc(leftVar)
// br(condBBLabel)
//
// condBBLabel:
// brtrue(endBBLabel, ldloc(leftVar)) // brtrue(endBBLabel, ldloc(leftVar))
// br(rightBBLabel) // br(rightBBLabel)
// //
// rightBBLabel: // rightBBLabel:
// v = rightExpr // v = rightExpr
// br(endBBLabel) // br(endBBLabel)
//
// endBBLabel:
// ... // ...
// =>
// ...
// v = NullCoalescing(ldloc(leftVar), rightExpr)
// br(endBBLabel)
ILVariable v, v2; ILVariable v, v2;
ILExpression leftExpr, leftExpr2; ILExpression leftExpr, leftExpr2;
ILVariable leftVar, leftVar2; ILVariable leftVar;
ILLabel condBBLabel;
ILBasicBlock condBB;
ILLabel endBBLabel, endBBLabel2; ILLabel endBBLabel, endBBLabel2;
ILLabel rightBBLabel; ILLabel rightBBLabel;
ILBasicBlock rightBB; ILBasicBlock rightBB;
ILExpression rightExpr; ILExpression rightExpr;
ILBasicBlock endBB; if (head.Body.Count >= 2 &&
if (head.Body.LastOrDefault().Match(ILCode.Stloc, out v, out leftExpr) && head.Body[head.Body.Count - 2].Match(ILCode.Stloc, out v, out leftExpr) &&
leftExpr.Match(ILCode.Ldloc, out leftVar) && leftExpr.Match(ILCode.Ldloc, out leftVar) &&
head.FallthoughGoto.Match(ILCode.Br, out condBBLabel) && head.MatchLast(ILCode.Brtrue, out endBBLabel, out leftExpr2, out rightBBLabel) &&
labelToBasicBlock.TryGetValue(condBBLabel, out condBB) && leftExpr2.Match(ILCode.Ldloc, leftVar) &&
condBB.Match(ILCode.Brtrue, out endBBLabel, out leftExpr2, out rightBBLabel) &&
leftExpr2.Match(ILCode.Ldloc, out leftVar2) &&
leftVar == leftVar2 &&
labelToBasicBlock.TryGetValue(rightBBLabel, out rightBB) && labelToBasicBlock.TryGetValue(rightBBLabel, out rightBB) &&
rightBB.Match(ILCode.Stloc, out v2, out rightExpr, out endBBLabel2) && rightBB.MatchSingle(ILCode.Stloc, out v2, out rightExpr, out endBBLabel2) &&
v == v2 && v == v2 &&
endBBLabel == endBBLabel2 && endBBLabel == endBBLabel2 &&
labelToBasicBlock.TryGetValue(endBBLabel, out endBB) &&
labelGlobalRefCount.GetOrDefault(condBBLabel) == 1 &&
labelGlobalRefCount.GetOrDefault(rightBBLabel) == 1 && labelGlobalRefCount.GetOrDefault(rightBBLabel) == 1 &&
labelGlobalRefCount.GetOrDefault(endBBLabel) == 2 && body.Contains(rightBB)
body.Contains(condBB) &&
body.Contains(rightBB) &&
body.Contains(endBB)
) )
{ {
head.Body[head.Body.Count - 1] = new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.NullCoalescing, null, leftExpr, rightExpr)); head.Body[head.Body.Count - 2] = new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.NullCoalescing, null, leftExpr, rightExpr));
head.Body.RemoveAt(head.Body.Count - 1);
head.FallthoughGoto = new ILExpression(ILCode.Br, endBBLabel); head.FallthoughGoto = new ILExpression(ILCode.Br, endBBLabel);
foreach(ILLabel deleteLabel in new [] { condBBLabel, rightBBLabel }) { body.RemoveOrThrow(labelToBasicBlock[rightBBLabel]);
body.RemoveOrThrow(labelToBasicBlock[deleteLabel]); labelGlobalRefCount.RemoveOrThrow(rightBBLabel);
labelGlobalRefCount.RemoveOrThrow(deleteLabel); labelToBasicBlock.RemoveOrThrow(rightBBLabel);
labelToBasicBlock.RemoveOrThrow(deleteLabel);
}
return true; return true;
} }
return false; return false;
@ -518,7 +499,7 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression condExpr; ILExpression condExpr;
ILLabel trueLabel; ILLabel trueLabel;
ILLabel falseLabel; ILLabel falseLabel;
if(head.Match(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) { if(head.MatchLast(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) {
for (int pass = 0; pass < 2; pass++) { for (int pass = 0; pass < 2; pass++) {
// On the second pass, swap labels and negate expression of the first branch // On the second pass, swap labels and negate expression of the first branch
@ -534,14 +515,14 @@ namespace ICSharpCode.Decompiler.ILAst
if (body.Contains(nextBasicBlock) && if (body.Contains(nextBasicBlock) &&
nextBasicBlock != head && nextBasicBlock != head &&
labelGlobalRefCount[nextBasicBlock.EntryLabel] == 1 && labelGlobalRefCount[nextBasicBlock.EntryLabel] == 1 &&
nextBasicBlock.Match(ILCode.Brtrue, out nextTrueLablel, out nextCondExpr, out nextFalseLabel) && nextBasicBlock.MatchSingle(ILCode.Brtrue, out nextTrueLablel, out nextCondExpr, out nextFalseLabel) &&
(otherLablel == nextFalseLabel || otherLablel == nextTrueLablel)) (otherLablel == nextFalseLabel || otherLablel == nextTrueLablel))
{ {
// Create short cicuit branch // Create short cicuit branch
if (otherLablel == nextFalseLabel) { if (otherLablel == nextFalseLabel) {
head.Body[0] = new ILExpression(ILCode.Brtrue, nextTrueLablel, new ILExpression(ILCode.LogicAnd, null, negate ? new ILExpression(ILCode.LogicNot, null, condExpr) : condExpr, nextCondExpr)); head.Body[head.Body.Count - 1] = new ILExpression(ILCode.Brtrue, nextTrueLablel, new ILExpression(ILCode.LogicAnd, null, negate ? new ILExpression(ILCode.LogicNot, null, condExpr) : condExpr, nextCondExpr));
} else { } else {
head.Body[0] = new ILExpression(ILCode.Brtrue, nextTrueLablel, new ILExpression(ILCode.LogicOr, null, negate ? condExpr : new ILExpression(ILCode.LogicNot, null, condExpr), nextCondExpr)); head.Body[head.Body.Count - 1] = new ILExpression(ILCode.Brtrue, nextTrueLablel, new ILExpression(ILCode.LogicOr, null, negate ? condExpr : new ILExpression(ILCode.LogicNot, null, condExpr), nextCondExpr));
} }
head.FallthoughGoto = new ILExpression(ILCode.Br, nextFalseLabel); head.FallthoughGoto = new ILExpression(ILCode.Br, nextFalseLabel);

92
ICSharpCode.Decompiler/ILAst/LoopsAndConditions.cs

@ -27,21 +27,25 @@ namespace ICSharpCode.Decompiler.ILAst
public void FindLoops(ILBlock block) public void FindLoops(ILBlock block)
{ {
if (block.Body.Count > 0) {
ControlFlowGraph graph; ControlFlowGraph graph;
graph = BuildGraph(block.Body, (ILLabel)block.EntryGoto.Operand); graph = BuildGraph(block.Body, (ILLabel)block.EntryGoto.Operand);
graph.ComputeDominance(context.CancellationToken); graph.ComputeDominance(context.CancellationToken);
graph.ComputeDominanceFrontier(); graph.ComputeDominanceFrontier();
block.Body = FindLoops(new HashSet<ControlFlowNode>(graph.Nodes.Skip(3)), graph.EntryPoint, false); block.Body = FindLoops(new HashSet<ControlFlowNode>(graph.Nodes.Skip(3)), graph.EntryPoint, false);
} }
}
public void FindConditions(ILBlock block) public void FindConditions(ILBlock block)
{ {
if (block.Body.Count > 0) {
ControlFlowGraph graph; ControlFlowGraph graph;
graph = BuildGraph(block.Body, (ILLabel)block.EntryGoto.Operand); graph = BuildGraph(block.Body, (ILLabel)block.EntryGoto.Operand);
graph.ComputeDominance(context.CancellationToken); graph.ComputeDominance(context.CancellationToken);
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);
} }
}
ControlFlowGraph BuildGraph(List<ILNode> nodes, ILLabel entryLabel) ControlFlowGraph BuildGraph(List<ILNode> nodes, ILLabel entryLabel)
{ {
@ -57,7 +61,7 @@ namespace ICSharpCode.Decompiler.ILAst
// Create graph nodes // Create graph nodes
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(ILBasicBlock node in nodes) {
ControlFlowNode cfNode = new ControlFlowNode(index++, -1, ControlFlowNodeType.Normal); ControlFlowNode cfNode = new ControlFlowNode(index++, -1, ControlFlowNodeType.Normal);
cfNodes.Add(cfNode); cfNodes.Add(cfNode);
astNodeToCfNode[node] = cfNode; astNodeToCfNode[node] = cfNode;
@ -76,25 +80,21 @@ namespace ICSharpCode.Decompiler.ILAst
entryNode.Incoming.Add(entryEdge); entryNode.Incoming.Add(entryEdge);
// Create edges // Create edges
foreach(ILNode node in nodes) { foreach(ILBasicBlock node in nodes) {
ControlFlowNode source = astNodeToCfNode[node]; ControlFlowNode source = astNodeToCfNode[node];
// Find all branches // Find all branches
foreach(ILExpression child in node.GetSelfAndChildrenRecursive<ILExpression>()) { foreach(ILLabel target in node.GetSelfAndChildrenRecursive<ILExpression>(e => e.IsBranch()).SelectMany(e => e.GetBranchTargets())) {
IEnumerable<ILLabel> targets = child.GetBranchTargets();
if (targets != null) {
foreach(ILLabel target in targets) {
ControlFlowNode destination; ControlFlowNode destination;
// Labels which are out of out scope will not be int the collection // Labels which are out of out scope will not be int the collection
if (labelToCfNode.TryGetValue(target, out destination) && destination != source) { // Insert self edge only if we are sure we are a loop
if (labelToCfNode.TryGetValue(target, out destination) && (destination != source || target == node.EntryLabel)) {
ControlFlowEdge edge = new ControlFlowEdge(source, destination, JumpType.Normal); ControlFlowEdge edge = new ControlFlowEdge(source, destination, JumpType.Normal);
source.Outgoing.Add(edge); source.Outgoing.Add(edge);
destination.Incoming.Add(edge); destination.Incoming.Add(edge);
} }
} }
} }
}
}
return new ControlFlowGraph(cfNodes.ToArray()); return new ControlFlowGraph(cfNodes.ToArray());
} }
@ -123,7 +123,7 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression condExpr; ILExpression condExpr;
ILLabel trueLabel; ILLabel trueLabel;
ILLabel falseLabel; ILLabel falseLabel;
if(basicBlock.Match(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) if(basicBlock.MatchSingle(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel))
{ {
ControlFlowNode trueTarget; ControlFlowNode trueTarget;
labelToCfNode.TryGetValue(trueLabel, out trueTarget); labelToCfNode.TryGetValue(trueLabel, out trueTarget);
@ -138,7 +138,7 @@ namespace ICSharpCode.Decompiler.ILAst
scope.RemoveOrThrow(node); scope.RemoveOrThrow(node);
// If false means enter the loop // If false means enter the loop
if (loopContents.Contains(falseTarget)) if (loopContents.Contains(falseTarget) || falseTarget == node)
{ {
// Negate the condition // Negate the condition
condExpr = new ILExpression(ILCode.LogicNot, null, condExpr); condExpr = new ILExpression(ILCode.LogicNot, null, condExpr);
@ -156,21 +156,19 @@ namespace ICSharpCode.Decompiler.ILAst
loopContents.UnionWith(pullIn); loopContents.UnionWith(pullIn);
} }
// Use loop to implement the condition // Use loop to implement the brtrue
result.Add(new ILBasicBlock() { basicBlock.Body.RemoveAt(basicBlock.Body.Count - 1);
EntryLabel = basicBlock.EntryLabel, basicBlock.Body.Add(new ILWhileLoop() {
Body = new List<ILNode>() {
new ILWhileLoop() {
Condition = condExpr, Condition = condExpr,
BodyBlock = new ILBlock() { BodyBlock = new ILBlock() {
EntryGoto = new ILExpression(ILCode.Br, trueLabel), EntryGoto = new ILExpression(ILCode.Br, trueLabel),
Body = FindLoops(loopContents, node, true) Body = FindLoops(loopContents, node, false)
} }
},
new ILExpression(ILCode.Br, falseLabel)
},
FallthoughGoto = null
}); });
basicBlock.FallthoughGoto = new ILExpression(ILCode.Br, falseLabel);
result.Add(basicBlock);
scope.ExceptWith(loopContents);
} }
} }
@ -188,11 +186,10 @@ namespace ICSharpCode.Decompiler.ILAst
}, },
FallthoughGoto = null FallthoughGoto = null
}); });
}
// Move the content into loop block
scope.ExceptWith(loopContents); scope.ExceptWith(loopContents);
} }
}
// 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
foreach(var child in node.DominatorTreeChildren) { foreach(var child in node.DominatorTreeChildren) {
@ -229,24 +226,20 @@ namespace ICSharpCode.Decompiler.ILAst
// Find a block that represents a simple condition // Find a block that represents a simple condition
if (scope.Contains(node)) { if (scope.Contains(node)) {
ILBasicBlock block = node.UserData as ILBasicBlock; ILBasicBlock block = (ILBasicBlock)node.UserData;
if (block != null && block.Body.Count == 1) {
ILExpression condBranch = block.Body[0] as ILExpression;
{
// Switch // Switch
ILLabel[] caseLabels; ILLabel[] caseLabels;
List<ILExpression> switchArgs; ILExpression switchArg;
if (condBranch.Match(ILCode.Switch, out caseLabels, out switchArgs)) { ILLabel fallLabel;
if (block.MatchLast(ILCode.Switch, out caseLabels, out switchArg, out fallLabel)) {
ILSwitch ilSwitch = new ILSwitch() { Condition = switchArgs.Single() };
ILBasicBlock newBB = new ILBasicBlock() { // Replace the switch code with ILSwitch
EntryLabel = block.EntryLabel, // Keep the entry label ILSwitch ilSwitch = new ILSwitch() { Condition = switchArg };
Body = { ilSwitch }, block.Body.RemoveAt(block.Body.Count - 1);
FallthoughGoto = block.FallthoughGoto block.Body.Add(ilSwitch);
}; result.Add(block);
result.Add(newBB);
// Remove the item so that it is not picked up as content // Remove the item so that it is not picked up as content
scope.RemoveOrThrow(node); scope.RemoveOrThrow(node);
@ -259,19 +252,18 @@ namespace ICSharpCode.Decompiler.ILAst
} }
// Pull in code of cases // Pull in code of cases
ILLabel fallLabel = (ILLabel)block.FallthoughGoto.Operand;
ControlFlowNode fallTarget = null; ControlFlowNode fallTarget = null;
labelToCfNode.TryGetValue(fallLabel, out fallTarget); labelToCfNode.TryGetValue(fallLabel, out fallTarget);
HashSet<ControlFlowNode> frontiers = new HashSet<ControlFlowNode>(); HashSet<ControlFlowNode> frontiers = new HashSet<ControlFlowNode>();
if (fallTarget != null) if (fallTarget != null)
frontiers.UnionWith(fallTarget.DominanceFrontier); frontiers.UnionWith(fallTarget.DominanceFrontier.Except(new [] { fallTarget }));
foreach(ILLabel condLabel in caseLabels) { foreach(ILLabel condLabel in caseLabels) {
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.Except(new [] { condTarget }));
} }
for (int i = 0; i < caseLabels.Length; i++) { for (int i = 0; i < caseLabels.Length; i++) {
@ -305,7 +297,7 @@ namespace ICSharpCode.Decompiler.ILAst
if (content.Any()) { if (content.Any()) {
var caseBlock = new ILSwitch.CaseBlock() { EntryGoto = new ILExpression(ILCode.Br, fallLabel) }; var caseBlock = new ILSwitch.CaseBlock() { EntryGoto = new ILExpression(ILCode.Br, fallLabel) };
ilSwitch.CaseBlocks.Add(caseBlock); ilSwitch.CaseBlocks.Add(caseBlock);
newBB.FallthoughGoto = null; block.FallthoughGoto = null;
scope.ExceptWith(content); scope.ExceptWith(content);
caseBlock.Body.AddRange(FindConditions(content, fallTarget)); caseBlock.Body.AddRange(FindConditions(content, fallTarget));
@ -319,7 +311,7 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression condExpr; ILExpression condExpr;
ILLabel trueLabel; ILLabel trueLabel;
ILLabel falseLabel; ILLabel falseLabel;
if(block.Match(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) { if(block.MatchLast(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) {
// Swap bodies since that seems to be the usual C# order // Swap bodies since that seems to be the usual C# order
ILLabel temp = trueLabel; ILLabel temp = trueLabel;
@ -327,16 +319,16 @@ namespace ICSharpCode.Decompiler.ILAst
falseLabel = temp; falseLabel = temp;
condExpr = new ILExpression(ILCode.LogicNot, null, condExpr); condExpr = new ILExpression(ILCode.LogicNot, null, condExpr);
// Convert the basic block to ILCondition // Convert the brtrue to ILCondition
ILCondition ilCond = new ILCondition() { ILCondition ilCond = new ILCondition() {
Condition = condExpr, Condition = condExpr,
TrueBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, trueLabel) }, TrueBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, trueLabel) },
FalseBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, falseLabel) } FalseBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, falseLabel) }
}; };
result.Add(new ILBasicBlock() { block.Body.RemoveAt(block.Body.Count - 1);
EntryLabel = block.EntryLabel, // Keep the entry label block.Body.Add(ilCond);
Body = { ilCond } block.FallthoughGoto = null;
}); result.Add(block);
// Remove the item immediately so that it is not picked up as content // Remove the item immediately so that it is not picked up as content
scope.RemoveOrThrow(node); scope.RemoveOrThrow(node);
@ -349,9 +341,9 @@ namespace ICSharpCode.Decompiler.ILAst
// Pull in the conditional code // Pull in the conditional code
HashSet<ControlFlowNode> frontiers = new HashSet<ControlFlowNode>(); HashSet<ControlFlowNode> frontiers = new HashSet<ControlFlowNode>();
if (trueTarget != null) if (trueTarget != null)
frontiers.UnionWith(trueTarget.DominanceFrontier); frontiers.UnionWith(trueTarget.DominanceFrontier.Except(new [] { trueTarget }));
if (falseTarget != null) if (falseTarget != null)
frontiers.UnionWith(falseTarget.DominanceFrontier); frontiers.UnionWith(falseTarget.DominanceFrontier.Except(new [] { falseTarget }));
if (trueTarget != null && !frontiers.Contains(trueTarget)) { if (trueTarget != null && !frontiers.Contains(trueTarget)) {
HashSet<ControlFlowNode> content = FindDominatedNodes(scope, trueTarget); HashSet<ControlFlowNode> content = FindDominatedNodes(scope, trueTarget);

16
ICSharpCode.Decompiler/ILAst/PatternMatching.cs

@ -4,7 +4,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq;
using Mono.Cecil; using Mono.Cecil;
namespace ICSharpCode.Decompiler.ILAst namespace ICSharpCode.Decompiler.ILAst
@ -98,7 +98,7 @@ namespace ICSharpCode.Decompiler.ILAst
return false; return false;
} }
public static bool Match<T>(this ILBasicBlock bb, ILCode code, out T operand, out ILExpression arg, out ILLabel fallLabel) public static bool MatchSingle<T>(this ILBasicBlock bb, ILCode code, out T operand, out ILExpression arg, out ILLabel fallLabel)
{ {
if (bb.Body.Count == 1) { if (bb.Body.Count == 1) {
if (bb.Body[0].Match(code, out operand, out arg)) { if (bb.Body[0].Match(code, out operand, out arg)) {
@ -112,6 +112,18 @@ namespace ICSharpCode.Decompiler.ILAst
return false; return false;
} }
public static bool MatchLast<T>(this ILBasicBlock bb, ILCode code, out T operand, out ILExpression arg, out ILLabel fallLabel)
{
if (bb.Body.LastOrDefault().Match(code, out operand, out arg)) {
fallLabel = bb.FallthoughGoto != null ? (ILLabel)bb.FallthoughGoto.Operand : null;
return true;
}
operand = default(T);
arg = null;
fallLabel = null;
return false;
}
public static bool MatchThis(this ILNode node) public static bool MatchThis(this ILNode node)
{ {
ILVariable v; ILVariable v;

Loading…
Cancel
Save