diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
index 784a0e12a..9e9a5e4f3 100644
--- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
+++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
@@ -97,6 +97,7 @@
+
diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
index a1555a3f9..d0b873516 100644
--- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
+++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
@@ -86,20 +86,19 @@ namespace ICSharpCode.Decompiler.ILAst
// Types are needed for the ternary operator optimization
TypeAnalysis.Run(context, method);
- AnalyseLabels(method);
foreach(ILBlock block in method.GetSelfAndChildrenRecursive()) {
bool modified;
do {
modified = false;
if (abortBeforeStep == ILAstOptimizationStep.SimplifyShortCircuit) return;
- modified |= block.RunOptimization(SimplifyShortCircuit);
+ modified |= block.RunOptimization(new SimpleControlFlow(context, method).SimplifyShortCircuit);
if (abortBeforeStep == ILAstOptimizationStep.SimplifyTernaryOperator) return;
- modified |= block.RunOptimization(SimplifyTernaryOperator);
+ modified |= block.RunOptimization(new SimpleControlFlow(context, method).SimplifyTernaryOperator);
if (abortBeforeStep == ILAstOptimizationStep.SimplifyNullCoalescing) return;
- modified |= block.RunOptimization(SimplifyNullCoalescing);
+ modified |= block.RunOptimization(new SimpleControlFlow(context, method).SimplifyNullCoalescing);
} while(modified);
}
@@ -344,226 +343,6 @@ namespace ICSharpCode.Decompiler.ILAst
return;
}
- Dictionary labelGlobalRefCount;
- Dictionary labelToBasicBlock;
-
- void AnalyseLabels(ILBlock method)
- {
- labelGlobalRefCount = new Dictionary();
- foreach(ILLabel target in method.GetSelfAndChildrenRecursive(e => e.IsBranch()).SelectMany(e => e.GetBranchTargets())) {
- if (!labelGlobalRefCount.ContainsKey(target))
- labelGlobalRefCount[target] = 0;
- labelGlobalRefCount[target]++;
- }
-
- labelToBasicBlock = new Dictionary();
- foreach(ILBasicBlock bb in method.GetSelfAndChildrenRecursive()) {
- foreach(ILLabel label in bb.GetChildren().OfType()) {
- labelToBasicBlock[label] = bb;
- }
- }
- }
-
- bool SimplifyTernaryOperator(List body, ILBasicBlock head, int pos)
- {
- Debug.Assert(body.Contains(head));
-
- ILExpression condExpr;
- ILLabel trueLabel;
- ILLabel falseLabel;
- ILVariable trueLocVar = null;
- ILExpression trueExpr;
- ILLabel trueFall;
- ILVariable falseLocVar = null;
- ILExpression falseExpr;
- ILLabel falseFall;
- object unused;
-
- if (head.MatchLast(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel) &&
- labelGlobalRefCount[trueLabel] == 1 &&
- labelGlobalRefCount[falseLabel] == 1 &&
- ((labelToBasicBlock[trueLabel].MatchSingle(ILCode.Stloc, out trueLocVar, out trueExpr, out trueFall) &&
- labelToBasicBlock[falseLabel].MatchSingle(ILCode.Stloc, out falseLocVar, out falseExpr, out falseFall)) ||
- (labelToBasicBlock[trueLabel].MatchSingle(ILCode.Ret, out unused, out trueExpr, out trueFall) &&
- labelToBasicBlock[falseLabel].MatchSingle(ILCode.Ret, out unused, out falseExpr, out falseFall))) &&
- trueLocVar == falseLocVar &&
- trueFall == falseFall &&
- body.Contains(labelToBasicBlock[trueLabel]) &&
- body.Contains(labelToBasicBlock[falseLabel])
- )
- {
- ILCode opCode = trueLocVar != null ? ILCode.Stloc : ILCode.Ret;
- TypeReference retType = trueLocVar != null ? trueLocVar.Type : this.context.CurrentMethod.ReturnType;
- int leftBoolVal;
- int rightBoolVal;
- ILExpression newExpr;
- // a ? true:false is equivalent to a
- // a ? false:true is equivalent to !a
- // a ? true : b is equivalent to a || b
- // a ? b : true is equivalent to !a || b
- // a ? b : false is equivalent to a && b
- // a ? false : b is equivalent to !a && b
- if (retType == typeSystem.Boolean &&
- trueExpr.Match(ILCode.Ldc_I4, out leftBoolVal) &&
- falseExpr.Match(ILCode.Ldc_I4, out rightBoolVal) &&
- ((leftBoolVal != 0 && rightBoolVal == 0) || (leftBoolVal == 0 && rightBoolVal != 0))
- )
- {
- // It can be expressed as trivilal expression
- if (leftBoolVal != 0) {
- newExpr = condExpr;
- } else {
- newExpr = new ILExpression(ILCode.LogicNot, null, condExpr);
- }
- } else if (retType == typeSystem.Boolean && trueExpr.Match(ILCode.Ldc_I4, out leftBoolVal)) {
- // It can be expressed as logical expression
- if (leftBoolVal != 0) {
- newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicOr, condExpr, falseExpr);
- } else {
- newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicAnd, new ILExpression(ILCode.LogicNot, null, condExpr), falseExpr);
- }
- } else if (retType == typeSystem.Boolean && falseExpr.Match(ILCode.Ldc_I4, out rightBoolVal)) {
- // It can be expressed as logical expression
- if (rightBoolVal != 0) {
- newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicOr, new ILExpression(ILCode.LogicNot, null, condExpr), trueExpr);
- } else {
- newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicAnd, condExpr, trueExpr);
- }
- } else {
- // Ternary operator tends to create long complicated return statements
- if (opCode == ILCode.Ret)
- return false;
-
- // Create ternary expression
- newExpr = new ILExpression(ILCode.TernaryOp, null, condExpr, trueExpr, falseExpr);
- }
-
- head.Body[head.Body.Count - 1] = new ILExpression(opCode, trueLocVar, newExpr);
- head.FallthoughGoto = trueFall != null ? new ILExpression(ILCode.Br, trueFall) : null;
-
- // Remove the old basic blocks
- foreach(ILLabel deleteLabel in new [] { trueLabel, falseLabel }) {
- body.RemoveOrThrow(labelToBasicBlock[deleteLabel]);
- labelGlobalRefCount.RemoveOrThrow(deleteLabel);
- labelToBasicBlock.RemoveOrThrow(deleteLabel);
- }
-
- return true;
- }
- return false;
- }
-
- bool SimplifyNullCoalescing(List body, ILBasicBlock head, int pos)
- {
- // ...
- // v = ldloc(leftVar)
- // brtrue(endBBLabel, ldloc(leftVar))
- // br(rightBBLabel)
- //
- // rightBBLabel:
- // v = rightExpr
- // br(endBBLabel)
- // ...
- // =>
- // ...
- // v = NullCoalescing(ldloc(leftVar), rightExpr)
- // br(endBBLabel)
-
- ILVariable v, v2;
- ILExpression leftExpr, leftExpr2;
- ILVariable leftVar;
- ILLabel endBBLabel, endBBLabel2;
- ILLabel rightBBLabel;
- ILBasicBlock rightBB;
- ILExpression rightExpr;
- if (head.Body.Count >= 2 &&
- head.Body[head.Body.Count - 2].Match(ILCode.Stloc, out v, out leftExpr) &&
- leftExpr.Match(ILCode.Ldloc, out leftVar) &&
- head.MatchLast(ILCode.Brtrue, out endBBLabel, out leftExpr2, out rightBBLabel) &&
- leftExpr2.Match(ILCode.Ldloc, leftVar) &&
- labelToBasicBlock.TryGetValue(rightBBLabel, out rightBB) &&
- rightBB.MatchSingle(ILCode.Stloc, out v2, out rightExpr, out endBBLabel2) &&
- v == v2 &&
- endBBLabel == endBBLabel2 &&
- labelGlobalRefCount.GetOrDefault(rightBBLabel) == 1 &&
- body.Contains(rightBB)
- )
- {
- 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);
-
- body.RemoveOrThrow(labelToBasicBlock[rightBBLabel]);
- labelGlobalRefCount.RemoveOrThrow(rightBBLabel);
- labelToBasicBlock.RemoveOrThrow(rightBBLabel);
- return true;
- }
- return false;
- }
-
- bool SimplifyShortCircuit(List body, ILBasicBlock head, int pos)
- {
- Debug.Assert(body.Contains(head));
-
- ILExpression condExpr;
- ILLabel trueLabel;
- ILLabel falseLabel;
- if(head.MatchLast(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) {
- for (int pass = 0; pass < 2; pass++) {
-
- // On the second pass, swap labels and negate expression of the first branch
- // It is slightly ugly, but much better then copy-pasting this whole block
- ILLabel nextLabel = (pass == 0) ? trueLabel : falseLabel;
- ILLabel otherLablel = (pass == 0) ? falseLabel : trueLabel;
- bool negate = (pass == 1);
-
- ILBasicBlock nextBasicBlock = labelToBasicBlock[nextLabel];
- ILExpression nextCondExpr;
- ILLabel nextTrueLablel;
- ILLabel nextFalseLabel;
- if (body.Contains(nextBasicBlock) &&
- nextBasicBlock != head &&
- labelGlobalRefCount[nextBasicBlock.EntryLabel] == 1 &&
- nextBasicBlock.MatchSingle(ILCode.Brtrue, out nextTrueLablel, out nextCondExpr, out nextFalseLabel) &&
- (otherLablel == nextFalseLabel || otherLablel == nextTrueLablel))
- {
- // Create short cicuit branch
- ILExpression logicExpr;
- if (otherLablel == nextFalseLabel) {
- logicExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicAnd, negate ? new ILExpression(ILCode.LogicNot, null, condExpr) : condExpr, nextCondExpr);
- } else {
- logicExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicOr, negate ? condExpr : new ILExpression(ILCode.LogicNot, null, condExpr), nextCondExpr);
- }
- head.Body[head.Body.Count - 1] = new ILExpression(ILCode.Brtrue, nextTrueLablel, logicExpr);
- head.FallthoughGoto = new ILExpression(ILCode.Br, nextFalseLabel);
-
- // Remove the inlined branch from scope
- labelGlobalRefCount.RemoveOrThrow(nextBasicBlock.EntryLabel);
- labelToBasicBlock.RemoveOrThrow(nextBasicBlock.EntryLabel);
- body.RemoveOrThrow(nextBasicBlock);
-
- return true;
- }
- }
- }
- return false;
- }
-
- ILExpression MakeLeftAssociativeShortCircuit(ILCode code, ILExpression left, ILExpression right)
- {
- // Assuming that the inputs are already left associative
- if (right.Match(code)) {
- // Find the leftmost logical expression
- ILExpression current = right;
- while(current.Arguments[0].Match(code))
- current = current.Arguments[0];
- current.Arguments[0] = new ILExpression(code, null, left, current.Arguments[0]);
- return right;
- } else {
- return new ILExpression(code, null, left, right);
- }
- }
-
void DuplicateReturnStatements(ILBlock method)
{
Dictionary nextSibling = new Dictionary();
diff --git a/ICSharpCode.Decompiler/ILAst/SimpleControlFlow.cs b/ICSharpCode.Decompiler/ILAst/SimpleControlFlow.cs
new file mode 100644
index 000000000..456d06025
--- /dev/null
+++ b/ICSharpCode.Decompiler/ILAst/SimpleControlFlow.cs
@@ -0,0 +1,233 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+
+using Mono.Cecil;
+
+namespace ICSharpCode.Decompiler.ILAst
+{
+ public class SimpleControlFlow
+ {
+ Dictionary labelGlobalRefCount = new Dictionary();
+ Dictionary labelToBasicBlock = new Dictionary();
+
+ DecompilerContext context;
+ TypeSystem typeSystem;
+
+ public SimpleControlFlow(DecompilerContext context, ILBlock method)
+ {
+ this.context = context;
+ this.typeSystem = context.CurrentMethod.Module.TypeSystem;
+
+ foreach(ILLabel target in method.GetSelfAndChildrenRecursive(e => e.IsBranch()).SelectMany(e => e.GetBranchTargets())) {
+ labelGlobalRefCount[target] = labelGlobalRefCount.GetOrDefault(target) + 1;
+ }
+ foreach(ILBasicBlock bb in method.GetSelfAndChildrenRecursive()) {
+ foreach(ILLabel label in bb.GetChildren().OfType()) {
+ labelToBasicBlock[label] = bb;
+ }
+ }
+ }
+
+ public bool SimplifyTernaryOperator(List body, ILBasicBlock head, int pos)
+ {
+ Debug.Assert(body.Contains(head));
+
+ ILExpression condExpr;
+ ILLabel trueLabel;
+ ILLabel falseLabel;
+ ILVariable trueLocVar = null;
+ ILExpression trueExpr;
+ ILLabel trueFall;
+ ILVariable falseLocVar = null;
+ ILExpression falseExpr;
+ ILLabel falseFall;
+ object unused;
+
+ if (head.MatchLast(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel) &&
+ labelGlobalRefCount[trueLabel] == 1 &&
+ labelGlobalRefCount[falseLabel] == 1 &&
+ ((labelToBasicBlock[trueLabel].MatchSingle(ILCode.Stloc, out trueLocVar, out trueExpr, out trueFall) &&
+ labelToBasicBlock[falseLabel].MatchSingle(ILCode.Stloc, out falseLocVar, out falseExpr, out falseFall)) ||
+ (labelToBasicBlock[trueLabel].MatchSingle(ILCode.Ret, out unused, out trueExpr, out trueFall) &&
+ labelToBasicBlock[falseLabel].MatchSingle(ILCode.Ret, out unused, out falseExpr, out falseFall))) &&
+ trueLocVar == falseLocVar &&
+ trueFall == falseFall &&
+ body.Contains(labelToBasicBlock[trueLabel]) &&
+ body.Contains(labelToBasicBlock[falseLabel])
+ )
+ {
+ ILCode opCode = trueLocVar != null ? ILCode.Stloc : ILCode.Ret;
+ TypeReference retType = trueLocVar != null ? trueLocVar.Type : this.context.CurrentMethod.ReturnType;
+ int leftBoolVal;
+ int rightBoolVal;
+ ILExpression newExpr;
+ // a ? true:false is equivalent to a
+ // a ? false:true is equivalent to !a
+ // a ? true : b is equivalent to a || b
+ // a ? b : true is equivalent to !a || b
+ // a ? b : false is equivalent to a && b
+ // a ? false : b is equivalent to !a && b
+ if (retType == typeSystem.Boolean &&
+ trueExpr.Match(ILCode.Ldc_I4, out leftBoolVal) &&
+ falseExpr.Match(ILCode.Ldc_I4, out rightBoolVal) &&
+ ((leftBoolVal != 0 && rightBoolVal == 0) || (leftBoolVal == 0 && rightBoolVal != 0))
+ )
+ {
+ // It can be expressed as trivilal expression
+ if (leftBoolVal != 0) {
+ newExpr = condExpr;
+ } else {
+ newExpr = new ILExpression(ILCode.LogicNot, null, condExpr);
+ }
+ } else if (retType == typeSystem.Boolean && trueExpr.Match(ILCode.Ldc_I4, out leftBoolVal)) {
+ // It can be expressed as logical expression
+ if (leftBoolVal != 0) {
+ newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicOr, condExpr, falseExpr);
+ } else {
+ newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicAnd, new ILExpression(ILCode.LogicNot, null, condExpr), falseExpr);
+ }
+ } else if (retType == typeSystem.Boolean && falseExpr.Match(ILCode.Ldc_I4, out rightBoolVal)) {
+ // It can be expressed as logical expression
+ if (rightBoolVal != 0) {
+ newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicOr, new ILExpression(ILCode.LogicNot, null, condExpr), trueExpr);
+ } else {
+ newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicAnd, condExpr, trueExpr);
+ }
+ } else {
+ // Ternary operator tends to create long complicated return statements
+ if (opCode == ILCode.Ret)
+ return false;
+
+ // Only simplify generated variables
+ if (opCode == ILCode.Stloc && !trueLocVar.IsGenerated)
+ return false;
+
+ // Create ternary expression
+ newExpr = new ILExpression(ILCode.TernaryOp, null, condExpr, trueExpr, falseExpr);
+ }
+
+ head.Body[head.Body.Count - 1] = new ILExpression(opCode, trueLocVar, newExpr);
+ head.FallthoughGoto = trueFall != null ? new ILExpression(ILCode.Br, trueFall) : null;
+
+ // Remove the old basic blocks
+ body.RemoveOrThrow(labelToBasicBlock[trueLabel]);
+ body.RemoveOrThrow(labelToBasicBlock[falseLabel]);
+
+ return true;
+ }
+ return false;
+ }
+
+ public bool SimplifyNullCoalescing(List body, ILBasicBlock head, int pos)
+ {
+ // ...
+ // v = ldloc(leftVar)
+ // brtrue(endBBLabel, ldloc(leftVar))
+ // br(rightBBLabel)
+ //
+ // rightBBLabel:
+ // v = rightExpr
+ // br(endBBLabel)
+ // ...
+ // =>
+ // ...
+ // v = NullCoalescing(ldloc(leftVar), rightExpr)
+ // br(endBBLabel)
+
+ ILVariable v, v2;
+ ILExpression leftExpr, leftExpr2;
+ ILVariable leftVar;
+ ILLabel endBBLabel, endBBLabel2;
+ ILLabel rightBBLabel;
+ ILBasicBlock rightBB;
+ ILExpression rightExpr;
+ if (head.Body.Count >= 2 &&
+ head.Body[head.Body.Count - 2].Match(ILCode.Stloc, out v, out leftExpr) &&
+ leftExpr.Match(ILCode.Ldloc, out leftVar) &&
+ head.MatchLast(ILCode.Brtrue, out endBBLabel, out leftExpr2, out rightBBLabel) &&
+ leftExpr2.Match(ILCode.Ldloc, leftVar) &&
+ labelToBasicBlock.TryGetValue(rightBBLabel, out rightBB) &&
+ rightBB.MatchSingle(ILCode.Stloc, out v2, out rightExpr, out endBBLabel2) &&
+ v == v2 &&
+ endBBLabel == endBBLabel2 &&
+ labelGlobalRefCount.GetOrDefault(rightBBLabel) == 1 &&
+ body.Contains(rightBB)
+ )
+ {
+ 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);
+
+ body.RemoveOrThrow(labelToBasicBlock[rightBBLabel]);
+ return true;
+ }
+ return false;
+ }
+
+ public bool SimplifyShortCircuit(List body, ILBasicBlock head, int pos)
+ {
+ Debug.Assert(body.Contains(head));
+
+ ILExpression condExpr;
+ ILLabel trueLabel;
+ ILLabel falseLabel;
+ if(head.MatchLast(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) {
+ for (int pass = 0; pass < 2; pass++) {
+
+ // On the second pass, swap labels and negate expression of the first branch
+ // It is slightly ugly, but much better then copy-pasting this whole block
+ ILLabel nextLabel = (pass == 0) ? trueLabel : falseLabel;
+ ILLabel otherLablel = (pass == 0) ? falseLabel : trueLabel;
+ bool negate = (pass == 1);
+
+ ILBasicBlock nextBasicBlock = labelToBasicBlock[nextLabel];
+ ILExpression nextCondExpr;
+ ILLabel nextTrueLablel;
+ ILLabel nextFalseLabel;
+ if (body.Contains(nextBasicBlock) &&
+ nextBasicBlock != head &&
+ labelGlobalRefCount[nextBasicBlock.EntryLabel] == 1 &&
+ nextBasicBlock.MatchSingle(ILCode.Brtrue, out nextTrueLablel, out nextCondExpr, out nextFalseLabel) &&
+ (otherLablel == nextFalseLabel || otherLablel == nextTrueLablel))
+ {
+ // Create short cicuit branch
+ ILExpression logicExpr;
+ if (otherLablel == nextFalseLabel) {
+ logicExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicAnd, negate ? new ILExpression(ILCode.LogicNot, null, condExpr) : condExpr, nextCondExpr);
+ } else {
+ logicExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicOr, negate ? condExpr : new ILExpression(ILCode.LogicNot, null, condExpr), nextCondExpr);
+ }
+ head.Body[head.Body.Count - 1] = new ILExpression(ILCode.Brtrue, nextTrueLablel, logicExpr);
+ head.FallthoughGoto = new ILExpression(ILCode.Br, nextFalseLabel);
+
+ // Remove the inlined branch from scope
+ body.RemoveOrThrow(nextBasicBlock);
+
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ ILExpression MakeLeftAssociativeShortCircuit(ILCode code, ILExpression left, ILExpression right)
+ {
+ // Assuming that the inputs are already left associative
+ if (right.Match(code)) {
+ // Find the leftmost logical expression
+ ILExpression current = right;
+ while(current.Arguments[0].Match(code))
+ current = current.Arguments[0];
+ current.Arguments[0] = new ILExpression(code, null, left, current.Arguments[0]);
+ return right;
+ } else {
+ return new ILExpression(code, null, left, right);
+ }
+ }
+ }
+}