Browse Source

Fix #1194: empty if statements are decompiled into goto statements

pull/1030/head
Daniel Grunwald 7 years ago
parent
commit
bafb6d1d49
  1. 17
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/ShortCircuit.cs
  2. 112
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/ShortCircuit.il
  3. 106
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/ShortCircuit.roslyn.il
  4. 4
      ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs
  5. 18
      ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs

17
ICSharpCode.Decompiler.Tests/TestCases/Pretty/ShortCircuit.cs

@ -251,5 +251,22 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -251,5 +251,22 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
return 2;
}
#if !OPT
public void EmptyIf()
{
if (F(0)) {
}
if (!F(1)) {
}
if (F(2) && F(3)) {
}
if (F(4) || F(5)) {
}
if (F(0) && F(1) && !F(2) && (F(3) || F(4))) {
}
E();
}
#endif
}
}

112
ICSharpCode.Decompiler.Tests/TestCases/Pretty/ShortCircuit.il

@ -1257,6 +1257,118 @@ @@ -1257,6 +1257,118 @@
IL_003d: ret
} // end of method ShortCircuit::StmtComplex6
.method public hidebysig instance void
EmptyIf() cil managed
{
// Code size 156 (0x9c)
.maxstack 2
.locals init (bool V_0)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldc.i4.0
IL_0003: callvirt instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::F(int32)
IL_0008: ldc.i4.0
IL_0009: ceq
IL_000b: stloc.0
IL_000c: ldloc.0
IL_000d: brtrue.s IL_0011
IL_000f: nop
IL_0010: nop
IL_0011: ldarg.0
IL_0012: ldc.i4.1
IL_0013: callvirt instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::F(int32)
IL_0018: stloc.0
IL_0019: ldloc.0
IL_001a: brtrue.s IL_001e
IL_001c: nop
IL_001d: nop
IL_001e: ldarg.0
IL_001f: ldc.i4.2
IL_0020: callvirt instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::F(int32)
IL_0025: brfalse.s IL_0033
IL_0027: ldarg.0
IL_0028: ldc.i4.3
IL_0029: callvirt instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::F(int32)
IL_002e: ldc.i4.0
IL_002f: ceq
IL_0031: br.s IL_0034
IL_0033: ldc.i4.1
IL_0034: nop
IL_0035: stloc.0
IL_0036: ldloc.0
IL_0037: brtrue.s IL_003b
IL_0039: nop
IL_003a: nop
IL_003b: ldarg.0
IL_003c: ldc.i4.4
IL_003d: callvirt instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::F(int32)
IL_0042: brtrue.s IL_0050
IL_0044: ldarg.0
IL_0045: ldc.i4.5
IL_0046: callvirt instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::F(int32)
IL_004b: ldc.i4.0
IL_004c: ceq
IL_004e: br.s IL_0051
IL_0050: ldc.i4.0
IL_0051: nop
IL_0052: stloc.0
IL_0053: ldloc.0
IL_0054: brtrue.s IL_0058
IL_0056: nop
IL_0057: nop
IL_0058: ldarg.0
IL_0059: ldc.i4.0
IL_005a: callvirt instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::F(int32)
IL_005f: brfalse.s IL_008c
IL_0061: ldarg.0
IL_0062: ldc.i4.1
IL_0063: callvirt instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::F(int32)
IL_0068: brfalse.s IL_008c
IL_006a: ldarg.0
IL_006b: ldc.i4.2
IL_006c: callvirt instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::F(int32)
IL_0071: brtrue.s IL_008c
IL_0073: ldarg.0
IL_0074: ldc.i4.3
IL_0075: callvirt instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::F(int32)
IL_007a: brtrue.s IL_0088
IL_007c: ldarg.0
IL_007d: ldc.i4.4
IL_007e: callvirt instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::F(int32)
IL_0083: ldc.i4.0
IL_0084: ceq
IL_0086: br.s IL_0089
IL_0088: ldc.i4.0
IL_0089: nop
IL_008a: br.s IL_008d
IL_008c: ldc.i4.1
IL_008d: nop
IL_008e: stloc.0
IL_008f: ldloc.0
IL_0090: brtrue.s IL_0094
IL_0092: nop
IL_0093: nop
IL_0094: ldarg.0
IL_0095: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::E()
IL_009a: nop
IL_009b: ret
} // end of method ShortCircuit::EmptyIf
.method family hidebysig specialname rtspecialname
instance void .ctor() cil managed
{

106
ICSharpCode.Decompiler.Tests/TestCases/Pretty/ShortCircuit.roslyn.il

@ -1193,6 +1193,112 @@ @@ -1193,6 +1193,112 @@
IL_0036: ret
} // end of method ShortCircuit::StmtComplex6
.method public hidebysig instance void
EmptyIf() cil managed
{
// Code size 145 (0x91)
.maxstack 2
.locals init (bool V_0,
bool V_1,
bool V_2,
bool V_3,
bool V_4)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldc.i4.0
IL_0003: callvirt instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::F(int32)
IL_0008: stloc.0
IL_0009: ldloc.0
IL_000a: brfalse.s IL_000e
IL_000c: nop
IL_000d: nop
IL_000e: ldarg.0
IL_000f: ldc.i4.1
IL_0010: callvirt instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::F(int32)
IL_0015: ldc.i4.0
IL_0016: ceq
IL_0018: stloc.1
IL_0019: ldloc.1
IL_001a: brfalse.s IL_001e
IL_001c: nop
IL_001d: nop
IL_001e: ldarg.0
IL_001f: ldc.i4.2
IL_0020: callvirt instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::F(int32)
IL_0025: brfalse.s IL_0030
IL_0027: ldarg.0
IL_0028: ldc.i4.3
IL_0029: callvirt instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::F(int32)
IL_002e: br.s IL_0031
IL_0030: ldc.i4.0
IL_0031: stloc.2
IL_0032: ldloc.2
IL_0033: brfalse.s IL_0037
IL_0035: nop
IL_0036: nop
IL_0037: ldarg.0
IL_0038: ldc.i4.4
IL_0039: callvirt instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::F(int32)
IL_003e: brtrue.s IL_0049
IL_0040: ldarg.0
IL_0041: ldc.i4.5
IL_0042: callvirt instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::F(int32)
IL_0047: br.s IL_004a
IL_0049: ldc.i4.1
IL_004a: stloc.3
IL_004b: ldloc.3
IL_004c: brfalse.s IL_0050
IL_004e: nop
IL_004f: nop
IL_0050: ldarg.0
IL_0051: ldc.i4.0
IL_0052: callvirt instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::F(int32)
IL_0057: brfalse.s IL_0080
IL_0059: ldarg.0
IL_005a: ldc.i4.1
IL_005b: callvirt instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::F(int32)
IL_0060: brfalse.s IL_0080
IL_0062: ldarg.0
IL_0063: ldc.i4.2
IL_0064: callvirt instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::F(int32)
IL_0069: brtrue.s IL_0080
IL_006b: ldarg.0
IL_006c: ldc.i4.3
IL_006d: callvirt instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::F(int32)
IL_0072: brtrue.s IL_007d
IL_0074: ldarg.0
IL_0075: ldc.i4.4
IL_0076: callvirt instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::F(int32)
IL_007b: br.s IL_007e
IL_007d: ldc.i4.1
IL_007e: br.s IL_0081
IL_0080: ldc.i4.0
IL_0081: stloc.s V_4
IL_0083: ldloc.s V_4
IL_0085: brfalse.s IL_0089
IL_0087: nop
IL_0088: nop
IL_0089: ldarg.0
IL_008a: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.ShortCircuit::E()
IL_008f: nop
IL_0090: ret
} // end of method ShortCircuit::EmptyIf
.method family hidebysig specialname rtspecialname
instance void .ctor() cil managed
{

4
ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs

@ -87,9 +87,13 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -87,9 +87,13 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
if (!(statement is BlockStatement)) {
var b = new BlockStatement();
statement.ReplaceWith(b);
if (statement is EmptyStatement && !statement.Children.Any()) {
b.CopyAnnotationsFrom(statement);
} else {
b.Add(statement);
}
}
}
bool IsAllowedAsEmbeddedStatement(Statement statement, Statement parent)
{

18
ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs

@ -88,7 +88,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -88,7 +88,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// </summary>
private void HandleIfInstruction(Block block, IfInstruction ifInst)
{
while (InlineTrueBranch(ifInst) || InlineExitBranch(block)) {
while (InlineTrueBranch(block, ifInst) || InlineExitBranch(block)) {
PickBetterBlockExit(block, ifInst);
MergeCommonBranches(block, ifInst);
SwapEmptyThen(ifInst);
@ -105,10 +105,22 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -105,10 +105,22 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
///
/// Only inlines branches that are strictly dominated by this block (incoming edge count == 1)
/// </summary>
private bool InlineTrueBranch(IfInstruction ifInst)
private bool InlineTrueBranch(Block block, IfInstruction ifInst)
{
if (!CanInline(ifInst.TrueInst))
if (!CanInline(ifInst.TrueInst)) {
if (block.Instructions.SecondToLastOrDefault() == ifInst && ifInst.FalseInst.MatchNop()) {
var exitInst = block.Instructions.Last();
if (DetectExitPoints.CompatibleExitInstruction(ifInst.TrueInst, exitInst)) {
// if (...) exitInst; exitInst;
context.Step("Use empty block as then-branch", ifInst.TrueInst);
ifInst.TrueInst = new Nop() { ILRange = ifInst.TrueInst.ILRange };
// false, because we didn't inline a real block
// this will cause HandleIfInstruction() to attempt to inline the exitInst.
return false;
}
}
return false;
}
context.Step("Inline block as then-branch", ifInst.TrueInst);
// The targetBlock was already processed, and is ready to embed

Loading…
Cancel
Save