Browse Source

Restore HighLevelLoopTransform pattern match for loop contents within if body

pull/1258/head
Chicken-Bones 7 years ago
parent
commit
52a279f861
  1. 30
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs
  2. 102
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il
  3. 61
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il
  4. 61
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il
  5. 105
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il
  6. 4
      ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs
  7. 42
      ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs

30
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs

@ -813,6 +813,25 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -813,6 +813,25 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
} while (++i < 10);
}
// double break from switch to loop exit requires additional pattern matching in HighLevelLoopTransform
public static void SwitchWithContinue7()
{
for (int num = 0; num >= 0; num--) {
Console.WriteLine("loop-head");
switch (num) {
default:
Console.WriteLine("default");
break;
case 0:
continue;
case 1:
break;
}
break;
}
Console.WriteLine("end");
}
public static void SwitchLoopNesting()
{
for (int i = 0; i < 10; i++) {
@ -968,6 +987,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -968,6 +987,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
return false;
}
public static bool Loop8(char c, bool b, Func<char> getChar)
{
if (b) {
while (c == ' ' || c == '\t') {
c = getChar();
}
}
return true;
}
#endregion
// Ensure correctness of SwitchDetection.UseCSharpSwitch control flow heuristics

102
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il

@ -2620,6 +2620,60 @@ @@ -2620,6 +2620,60 @@
IL_008d: ret
} // end of method Switch::SwitchWithContinue6
.method public hidebysig static void SwitchWithContinue7() cil managed
{
// Code size 81 (0x51)
.maxstack 2
.locals init (int32 V_0,
int32 V_1,
bool V_2)
IL_0000: nop
IL_0001: ldc.i4.0
IL_0002: stloc.0
IL_0003: br.s IL_003a
IL_0005: nop
IL_0006: ldstr "loop-head"
IL_000b: call void [mscorlib]System.Console::WriteLine(string)
IL_0010: nop
IL_0011: ldloc.0
IL_0012: stloc.1
IL_0013: ldloc.1
IL_0014: switch (
IL_0030,
IL_0032)
IL_0021: br.s IL_0023
IL_0023: ldstr "default"
IL_0028: call void [mscorlib]System.Console::WriteLine(string)
IL_002d: nop
IL_002e: br.s IL_0034
IL_0030: br.s IL_0036
IL_0032: br.s IL_0034
IL_0034: br.s IL_0045
IL_0036: ldloc.0
IL_0037: ldc.i4.1
IL_0038: sub
IL_0039: stloc.0
IL_003a: ldloc.0
IL_003b: ldc.i4.0
IL_003c: clt
IL_003e: ldc.i4.0
IL_003f: ceq
IL_0041: stloc.2
IL_0042: ldloc.2
IL_0043: brtrue.s IL_0005
IL_0045: ldstr "end"
IL_004a: call void [mscorlib]System.Console::WriteLine(string)
IL_004f: nop
IL_0050: ret
} // end of method Switch::SwitchWithContinue7
.method public hidebysig static void SwitchLoopNesting() cil managed
{
// Code size 153 (0x99)
@ -3291,6 +3345,54 @@ @@ -3291,6 +3345,54 @@
IL_004f: ret
} // end of method Switch::SwitchlikeIf2
.method public hidebysig static bool Loop8(char c,
bool b,
class [mscorlib]System.Func`1<char> getChar) cil managed
{
// Code size 47 (0x2f)
.maxstack 2
.locals init (bool V_0,
bool V_1)
IL_0000: nop
IL_0001: ldarg.1
IL_0002: ldc.i4.0
IL_0003: ceq
IL_0005: stloc.1
IL_0006: ldloc.1
IL_0007: brtrue.s IL_0029
IL_0009: nop
IL_000a: br.s IL_0016
IL_000c: nop
IL_000d: ldarg.2
IL_000e: callvirt instance !0 class [mscorlib]System.Func`1<char>::Invoke()
IL_0013: starg.s c
IL_0015: nop
IL_0016: ldarg.0
IL_0017: ldc.i4.s 32
IL_0019: beq.s IL_0022
IL_001b: ldarg.0
IL_001c: ldc.i4.s 9
IL_001e: ceq
IL_0020: br.s IL_0023
IL_0022: ldc.i4.1
IL_0023: nop
IL_0024: stloc.1
IL_0025: ldloc.1
IL_0026: brtrue.s IL_000c
IL_0028: nop
IL_0029: ldc.i4.1
IL_002a: stloc.0
IL_002b: br.s IL_002d
IL_002d: ldloc.0
IL_002e: ret
} // end of method Switch::Loop8
.method public hidebysig static void SwitchWithBreakCase(int32 i,
bool b) cil managed
{

61
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il

@ -2111,6 +2111,41 @@ @@ -2111,6 +2111,41 @@
IL_006d: ret
} // end of method Switch::SwitchWithContinue6
.method public hidebysig static void SwitchWithContinue7() cil managed
{
// Code size 61 (0x3d)
.maxstack 2
.locals init (int32 V_0,
int32 V_1)
IL_0000: ldc.i4.0
IL_0001: stloc.0
IL_0002: br.s IL_002e
IL_0004: ldstr "loop-head"
IL_0009: call void [mscorlib]System.Console::WriteLine(string)
IL_000e: ldloc.0
IL_000f: stloc.1
IL_0010: ldloc.1
IL_0011: switch (
IL_002a,
IL_0032)
IL_001e: ldstr "default"
IL_0023: call void [mscorlib]System.Console::WriteLine(string)
IL_0028: br.s IL_0032
IL_002a: ldloc.0
IL_002b: ldc.i4.1
IL_002c: sub
IL_002d: stloc.0
IL_002e: ldloc.0
IL_002f: ldc.i4.0
IL_0030: bge.s IL_0004
IL_0032: ldstr "end"
IL_0037: call void [mscorlib]System.Console::WriteLine(string)
IL_003c: ret
} // end of method Switch::SwitchWithContinue7
.method public hidebysig static void SwitchLoopNesting() cil managed
{
// Code size 101 (0x65)
@ -2495,6 +2530,32 @@ @@ -2495,6 +2530,32 @@
IL_0024: ret
} // end of method Switch::SwitchlikeIf2
.method public hidebysig static bool Loop8(char c,
bool b,
class [mscorlib]System.Func`1<char> getChar) cil managed
{
// Code size 25 (0x19)
.maxstack 8
IL_0000: ldarg.1
IL_0001: brfalse.s IL_0017
IL_0003: br.s IL_000d
IL_0005: ldarg.2
IL_0006: callvirt instance !0 class [mscorlib]System.Func`1<char>::Invoke()
IL_000b: starg.s c
IL_000d: ldarg.0
IL_000e: ldc.i4.s 32
IL_0010: beq.s IL_0005
IL_0012: ldarg.0
IL_0013: ldc.i4.s 9
IL_0015: beq.s IL_0005
IL_0017: ldc.i4.1
IL_0018: ret
} // end of method Switch::Loop8
.method public hidebysig static void SwitchWithBreakCase(int32 i,
bool b) cil managed
{

61
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il

@ -2270,6 +2270,41 @@ @@ -2270,6 +2270,41 @@
IL_006b: ret
} // end of method Switch::SwitchWithContinue6
.method public hidebysig static void SwitchWithContinue7() cil managed
{
// Code size 52 (0x34)
.maxstack 2
.locals init (int32 V_0)
IL_0000: ldc.i4.0
IL_0001: stloc.0
IL_0002: br.s IL_0025
IL_0004: ldstr "loop-head"
IL_0009: call void [mscorlib]System.Console::WriteLine(string)
IL_000e: ldloc.0
IL_000f: brfalse.s IL_0021
IL_0011: ldloc.0
IL_0012: ldc.i4.1
IL_0013: beq.s IL_0029
IL_0015: ldstr "default"
IL_001a: call void [mscorlib]System.Console::WriteLine(string)
IL_001f: br.s IL_0029
IL_0021: ldloc.0
IL_0022: ldc.i4.1
IL_0023: sub
IL_0024: stloc.0
IL_0025: ldloc.0
IL_0026: ldc.i4.0
IL_0027: bge.s IL_0004
IL_0029: ldstr "end"
IL_002e: call void [mscorlib]System.Console::WriteLine(string)
IL_0033: ret
} // end of method Switch::SwitchWithContinue7
.method public hidebysig static void SwitchLoopNesting() cil managed
{
// Code size 92 (0x5c)
@ -2656,6 +2691,32 @@ @@ -2656,6 +2691,32 @@
IL_0024: ret
} // end of method Switch::SwitchlikeIf2
.method public hidebysig static bool Loop8(char c,
bool b,
class [mscorlib]System.Func`1<char> getChar) cil managed
{
// Code size 25 (0x19)
.maxstack 8
IL_0000: ldarg.1
IL_0001: brfalse.s IL_0017
IL_0003: br.s IL_000d
IL_0005: ldarg.2
IL_0006: callvirt instance !0 class [mscorlib]System.Func`1<char>::Invoke()
IL_000b: starg.s c
IL_000d: ldarg.0
IL_000e: ldc.i4.s 32
IL_0010: beq.s IL_0005
IL_0012: ldarg.0
IL_0013: ldc.i4.s 9
IL_0015: beq.s IL_0005
IL_0017: ldc.i4.1
IL_0018: ret
} // end of method Switch::Loop8
.method public hidebysig static void SwitchWithBreakCase(int32 i,
bool b) cil managed
{

105
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il

@ -2960,6 +2960,65 @@ @@ -2960,6 +2960,65 @@
IL_008d: ret
} // end of method Switch::SwitchWithContinue6
.method public hidebysig static void SwitchWithContinue7() cil managed
{
// Code size 76 (0x4c)
.maxstack 2
.locals init (int32 V_0,
int32 V_1,
bool V_2)
IL_0000: nop
IL_0001: ldc.i4.0
IL_0002: stloc.0
IL_0003: br.s IL_0035
IL_0005: nop
IL_0006: ldstr "loop-head"
IL_000b: call void [mscorlib]System.Console::WriteLine(string)
IL_0010: nop
IL_0011: ldloc.0
IL_0012: stloc.1
IL_0013: ldloc.1
IL_0014: brfalse.s IL_002b
IL_0016: br.s IL_0018
IL_0018: ldloc.1
IL_0019: ldc.i4.1
IL_001a: beq.s IL_002d
IL_001c: br.s IL_001e
IL_001e: ldstr "default"
IL_0023: call void [mscorlib]System.Console::WriteLine(string)
IL_0028: nop
IL_0029: br.s IL_002f
IL_002b: br.s IL_0031
IL_002d: br.s IL_002f
IL_002f: br.s IL_0040
IL_0031: ldloc.0
IL_0032: ldc.i4.1
IL_0033: sub
IL_0034: stloc.0
IL_0035: ldloc.0
IL_0036: ldc.i4.0
IL_0037: clt
IL_0039: ldc.i4.0
IL_003a: ceq
IL_003c: stloc.2
IL_003d: ldloc.2
IL_003e: brtrue.s IL_0005
IL_0040: ldstr "end"
IL_0045: call void [mscorlib]System.Console::WriteLine(string)
IL_004a: nop
IL_004b: ret
} // end of method Switch::SwitchWithContinue7
.method public hidebysig static void SwitchLoopNesting() cil managed
{
// Code size 140 (0x8c)
@ -3635,6 +3694,52 @@ @@ -3635,6 +3694,52 @@
IL_0049: ret
} // end of method Switch::SwitchlikeIf2
.method public hidebysig static bool Loop8(char c,
bool b,
class [mscorlib]System.Func`1<char> getChar) cil managed
{
// Code size 43 (0x2b)
.maxstack 2
.locals init (bool V_0,
bool V_1,
bool V_2)
IL_0000: nop
IL_0001: ldarg.1
IL_0002: stloc.0
IL_0003: ldloc.0
IL_0004: brfalse.s IL_0025
IL_0006: nop
IL_0007: br.s IL_0013
IL_0009: nop
IL_000a: ldarg.2
IL_000b: callvirt instance !0 class [mscorlib]System.Func`1<char>::Invoke()
IL_0010: starg.s c
IL_0012: nop
IL_0013: ldarg.0
IL_0014: ldc.i4.s 32
IL_0016: beq.s IL_001f
IL_0018: ldarg.0
IL_0019: ldc.i4.s 9
IL_001b: ceq
IL_001d: br.s IL_0020
IL_001f: ldc.i4.1
IL_0020: stloc.1
IL_0021: ldloc.1
IL_0022: brtrue.s IL_0009
IL_0024: nop
IL_0025: ldc.i4.1
IL_0026: stloc.2
IL_0027: br.s IL_0029
IL_0029: ldloc.2
IL_002a: ret
} // end of method Switch::Loop8
.method public hidebysig static void SwitchWithBreakCase(int32 i,
bool b) cil managed
{

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

@ -335,6 +335,8 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -335,6 +335,8 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
&& ThenInstIsSingleExit(elseIfInst);
}
private void InvertIf(Block block, IfInstruction ifInst) => InvertIf(block, ifInst, context);
/// <summary>
/// if (cond) { then... }
/// else...;
@ -345,7 +347,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -345,7 +347,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
///
/// Assumes ifInst does not have an else block
/// </summary>
private void InvertIf(Block block, IfInstruction ifInst)
internal static void InvertIf(Block block, IfInstruction ifInst, ILTransformContext context)
{
Debug.Assert(ifInst.Parent == block);

42
ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs

@ -57,17 +57,30 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -57,17 +57,30 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// if (!loop-condition) leave loop-container
// ...
condition = null;
loopBody = null;
if (!(loop.EntryPoint.Instructions[0] is IfInstruction ifInstruction))
loopBody = loop.EntryPoint;
if (!(loopBody.Instructions[0] is IfInstruction ifInstruction))
return false;
if (!ifInstruction.FalseInst.MatchNop())
return false;
if (UsesVariableCapturedInLoop(loop, ifInstruction.Condition))
return false;
condition = ifInstruction;
if (!ifInstruction.TrueInst.MatchLeave(loop))
return false;
if (!ifInstruction.TrueInst.MatchLeave(loop)) {
// sometimes the loop-body is nested within the if
// if (loop-condition) { loop-body }
// leave loop-container
if (loopBody.Instructions.Count != 2 || !loop.EntryPoint.Instructions.Last().MatchLeave(loop))
return false;
if (!ifInstruction.TrueInst.HasFlag(InstructionFlags.EndPointUnreachable))
((Block)ifInstruction.TrueInst).Instructions.Add(new Leave(loop));
ConditionDetection.InvertIf(loopBody, ifInstruction, context);
}
context.Step("Transform to while (condition) loop", loop);
loop.Kind = ContainerKind.While;
@ -95,26 +108,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -95,26 +108,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
ExpressionTransforms.RunOnSingleStatment(inst, context);
}
}*/
// Invert condition and unwrap nested block, if loop ends in a break or return statement preceeded by an IfInstruction.
/*while (loopBody.Instructions.Last() is Leave leave && loopBody.Instructions.SecondToLastOrDefault() is IfInstruction nestedIf && nestedIf.FalseInst.MatchNop()) {
switch (nestedIf.TrueInst) {
case Block nestedBlock:
loopBody.Instructions.RemoveAt(leave.ChildIndex);
loopBody.Instructions.AddRange(nestedBlock.Instructions);
break;
case Branch br:
leave.ReplaceWith(nestedIf.TrueInst);
break;
default:
return true;
}
nestedIf.Condition = Comp.LogicNot(nestedIf.Condition);
nestedIf.TrueInst = leave;
ExpressionTransforms.RunOnSingleStatment(nestedIf, context);
if (!loopBody.HasFlag(InstructionFlags.EndPointUnreachable))
loopBody.Instructions.Add(new Leave(loop));
}*/
return true;
}

Loading…
Cancel
Save