diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ShortCircuit.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ShortCircuit.cs index f89058b5f..0579d1f58 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ShortCircuit.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ShortCircuit.cs @@ -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 } } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ShortCircuit.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ShortCircuit.il index aef2f09e0..0b241cea2 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ShortCircuit.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ShortCircuit.il @@ -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 { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ShortCircuit.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ShortCircuit.roslyn.il index 0a4da1490..3504fed84 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ShortCircuit.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ShortCircuit.roslyn.il @@ -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 { diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs b/ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs index d9120755a..853a8a374 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs @@ -87,7 +87,11 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms if (!(statement is BlockStatement)) { var b = new BlockStatement(); statement.ReplaceWith(b); - b.Add(statement); + if (statement is EmptyStatement && !statement.Children.Any()) { + b.CopyAnnotationsFrom(statement); + } else { + b.Add(statement); + } } } diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs b/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs index c19f47ec1..e75313e65 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs @@ -88,7 +88,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow /// 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 /// /// Only inlines branches that are strictly dominated by this block (incoming edge count == 1) /// - 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