diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs index 7e71ca0f5..6f1ee6f41 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs @@ -105,6 +105,24 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } + public static void SparseIntegerSwitch2(int i) + { + switch (i) { + case 4: + case 10: + case 11: + case 13: + case 21: + case 29: + case 33: + case 49: + case 50: + case 55: + Console.WriteLine(); + break; + } + } + public static string SwitchOverNullableInt(int? i) { switch (i) { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il index 476dbee82..db2ccb969 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il @@ -293,6 +293,69 @@ IL_00d0: ret } // end of method Switch::SparseIntegerSwitch + .method public hidebysig static void SparseIntegerSwitch2(int32 i) cil managed + { + // Code size 94 (0x5e) + .maxstack 2 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: ldc.i4.s 21 + IL_0006: bgt.s IL_002c + + IL_0008: ldloc.0 + IL_0009: ldc.i4.4 + IL_000a: beq.s IL_0055 + + IL_000c: ldloc.0 + IL_000d: ldc.i4.s 10 + IL_000f: sub + IL_0010: switch ( + IL_0055, + IL_0055, + IL_005d, + IL_0055) + IL_0025: ldloc.0 + IL_0026: ldc.i4.s 21 + IL_0028: beq.s IL_0055 + + IL_002a: br.s IL_005d + + IL_002c: ldloc.0 + IL_002d: ldc.i4.s 33 + IL_002f: bgt.s IL_003d + + IL_0031: ldloc.0 + IL_0032: ldc.i4.s 29 + IL_0034: beq.s IL_0055 + + IL_0036: ldloc.0 + IL_0037: ldc.i4.s 33 + IL_0039: beq.s IL_0055 + + IL_003b: br.s IL_005d + + IL_003d: ldloc.0 + IL_003e: ldc.i4.s 49 + IL_0040: sub + IL_0041: switch ( + IL_0055, + IL_0055) + IL_004e: ldloc.0 + IL_004f: ldc.i4.s 55 + IL_0051: beq.s IL_0055 + + IL_0053: br.s IL_005d + + IL_0055: call void [mscorlib]System.Console::WriteLine() + IL_005a: nop + IL_005b: br.s IL_005d + + IL_005d: ret + } // end of method Switch::SparseIntegerSwitch2 + .method public hidebysig static string SwitchOverNullableInt(valuetype [mscorlib]System.Nullable`1 i) cil managed { @@ -876,7 +939,7 @@ IL_0015: brfalse IL_00e9 IL_001a: volatile. - IL_001c: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x600000d-1' + IL_001c: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x600000e-1' IL_0021: brtrue.s IL_0084 IL_0023: ldc.i4.7 @@ -917,9 +980,9 @@ IL_0078: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, !1) IL_007d: volatile. - IL_007f: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x600000d-1' + IL_007f: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x600000e-1' IL_0084: volatile. - IL_0086: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x600000d-1' + IL_0086: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x600000e-1' IL_008b: ldloc.1 IL_008c: ldloca.s V_2 IL_008e: call instance bool class [mscorlib]System.Collections.Generic.Dictionary`2::TryGetValue(!0, @@ -991,7 +1054,7 @@ IL_0013: brfalse IL_0158 IL_0018: volatile. - IL_001a: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x600000e-1' + IL_001a: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x600000f-1' IL_001f: brtrue IL_00b8 IL_0024: ldc.i4.s 11 @@ -1052,9 +1115,9 @@ IL_00ac: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, !1) IL_00b1: volatile. - IL_00b3: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x600000e-1' + IL_00b3: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x600000f-1' IL_00b8: volatile. - IL_00ba: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x600000e-1' + IL_00ba: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x600000f-1' IL_00bf: ldloc.1 IL_00c0: ldloca.s V_2 IL_00c2: call instance bool class [mscorlib]System.Collections.Generic.Dictionary`2::TryGetValue(!0, @@ -1342,7 +1405,7 @@ IL_003b: brfalse IL_012c IL_0040: volatile. - IL_0042: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x6000013-1' + IL_0042: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x6000014-1' IL_0047: brtrue.s IL_009e IL_0049: ldc.i4.6 @@ -1378,9 +1441,9 @@ IL_0092: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, !1) IL_0097: volatile. - IL_0099: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x6000013-1' + IL_0099: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x6000014-1' IL_009e: volatile. - IL_00a0: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x6000013-1' + IL_00a0: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x6000014-1' IL_00a5: ldloc.s V_5 IL_00a7: ldloca.s V_6 IL_00a9: call instance bool class [mscorlib]System.Collections.Generic.Dictionary`2::TryGetValue(!0, @@ -1611,9 +1674,9 @@ extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) - .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x600000d-1' .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x600000e-1' - .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000013-1' + .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x600000f-1' + .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000014-1' } // end of class '' diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il index 719b11561..b5e0254f0 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il @@ -251,6 +251,65 @@ IL_00b4: ret } // end of method Switch::SparseIntegerSwitch + .method public hidebysig static void SparseIntegerSwitch2(int32 i) cil managed + { + // Code size 87 (0x57) + .maxstack 2 + .locals init (int32 V_0) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: ldc.i4.s 21 + IL_0005: bgt.s IL_002a + + IL_0007: ldloc.0 + IL_0008: ldc.i4.4 + IL_0009: beq.s IL_0051 + + IL_000b: ldloc.0 + IL_000c: ldc.i4.s 10 + IL_000e: sub + IL_000f: switch ( + IL_0051, + IL_0051, + IL_0056, + IL_0051) + IL_0024: ldloc.0 + IL_0025: ldc.i4.s 21 + IL_0027: beq.s IL_0051 + + IL_0029: ret + + IL_002a: ldloc.0 + IL_002b: ldc.i4.s 33 + IL_002d: bgt.s IL_003a + + IL_002f: ldloc.0 + IL_0030: ldc.i4.s 29 + IL_0032: beq.s IL_0051 + + IL_0034: ldloc.0 + IL_0035: ldc.i4.s 33 + IL_0037: beq.s IL_0051 + + IL_0039: ret + + IL_003a: ldloc.0 + IL_003b: ldc.i4.s 49 + IL_003d: sub + IL_003e: switch ( + IL_0051, + IL_0051) + IL_004b: ldloc.0 + IL_004c: ldc.i4.s 55 + IL_004e: beq.s IL_0051 + + IL_0050: ret + + IL_0051: call void [mscorlib]System.Console::WriteLine() + IL_0056: ret + } // end of method Switch::SparseIntegerSwitch2 + .method public hidebysig static string SwitchOverNullableInt(valuetype [mscorlib]System.Nullable`1 i) cil managed { @@ -745,7 +804,7 @@ IL_0013: brfalse IL_00db IL_0018: volatile. - IL_001a: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x600000d-1' + IL_001a: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x600000e-1' IL_001f: brtrue.s IL_0082 IL_0021: ldc.i4.7 @@ -786,9 +845,9 @@ IL_0076: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, !1) IL_007b: volatile. - IL_007d: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x600000d-1' + IL_007d: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x600000e-1' IL_0082: volatile. - IL_0084: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x600000d-1' + IL_0084: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x600000e-1' IL_0089: ldloc.0 IL_008a: ldloca.s V_1 IL_008c: call instance bool class [mscorlib]System.Collections.Generic.Dictionary`2::TryGetValue(!0, @@ -846,7 +905,7 @@ IL_0011: brfalse IL_013d IL_0016: volatile. - IL_0018: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x600000e-1' + IL_0018: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x600000f-1' IL_001d: brtrue IL_00b6 IL_0022: ldc.i4.s 11 @@ -907,9 +966,9 @@ IL_00aa: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, !1) IL_00af: volatile. - IL_00b1: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x600000e-1' + IL_00b1: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x600000f-1' IL_00b6: volatile. - IL_00b8: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x600000e-1' + IL_00b8: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x600000f-1' IL_00bd: ldloc.0 IL_00be: ldloca.s V_1 IL_00c0: call instance bool class [mscorlib]System.Collections.Generic.Dictionary`2::TryGetValue(!0, @@ -1135,7 +1194,7 @@ IL_0037: brfalse IL_011f IL_003c: volatile. - IL_003e: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x6000013-1' + IL_003e: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x6000014-1' IL_0043: brtrue.s IL_009a IL_0045: ldc.i4.6 @@ -1171,9 +1230,9 @@ IL_008e: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, !1) IL_0093: volatile. - IL_0095: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x6000013-1' + IL_0095: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x6000014-1' IL_009a: volatile. - IL_009c: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x6000013-1' + IL_009c: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x6000014-1' IL_00a1: ldloc.s V_5 IL_00a3: ldloca.s V_6 IL_00a5: call instance bool class [mscorlib]System.Collections.Generic.Dictionary`2::TryGetValue(!0, @@ -1371,9 +1430,9 @@ extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) - .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x600000d-1' .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x600000e-1' - .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000013-1' + .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x600000f-1' + .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000014-1' } // end of class '' diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il index d1fcf4714..1315c4d1b 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il @@ -269,6 +269,68 @@ IL_00b8: ret } // end of method Switch::SparseIntegerSwitch + .method public hidebysig static void SparseIntegerSwitch2(int32 i) cil managed + { + // Code size 67 (0x43) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldc.i4.s 21 + IL_0003: bgt.s IL_0021 + + IL_0005: ldarg.0 + IL_0006: ldc.i4.s 11 + IL_0008: bgt.s IL_0016 + + IL_000a: ldarg.0 + IL_000b: ldc.i4.4 + IL_000c: beq.s IL_003d + + IL_000e: ldarg.0 + IL_000f: ldc.i4.s 10 + IL_0011: sub + IL_0012: ldc.i4.1 + IL_0013: ble.un.s IL_003d + + IL_0015: ret + + IL_0016: ldarg.0 + IL_0017: ldc.i4.s 13 + IL_0019: beq.s IL_003d + + IL_001b: ldarg.0 + IL_001c: ldc.i4.s 21 + IL_001e: beq.s IL_003d + + IL_0020: ret + + IL_0021: ldarg.0 + IL_0022: ldc.i4.s 33 + IL_0024: bgt.s IL_0031 + + IL_0026: ldarg.0 + IL_0027: ldc.i4.s 29 + IL_0029: beq.s IL_003d + + IL_002b: ldarg.0 + IL_002c: ldc.i4.s 33 + IL_002e: beq.s IL_003d + + IL_0030: ret + + IL_0031: ldarg.0 + IL_0032: ldc.i4.s 49 + IL_0034: sub + IL_0035: ldc.i4.1 + IL_0036: ble.un.s IL_003d + + IL_0038: ldarg.0 + IL_0039: ldc.i4.s 55 + IL_003b: bne.un.s IL_0042 + + IL_003d: call void [mscorlib]System.Console::WriteLine() + IL_0042: ret + } // end of method Switch::SparseIntegerSwitch2 + .method public hidebysig static string SwitchOverNullableInt(valuetype [mscorlib]System.Nullable`1 i) cil managed { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il index 369af405d..cda35e3a3 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il @@ -325,6 +325,85 @@ IL_00e1: ret } // end of method Switch::SparseIntegerSwitch + .method public hidebysig static void SparseIntegerSwitch2(int32 i) cil managed + { + // Code size 86 (0x56) + .maxstack 2 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: ldc.i4.s 21 + IL_0006: bgt.s IL_002a + + IL_0008: ldloc.0 + IL_0009: ldc.i4.s 11 + IL_000b: bgt.s IL_001c + + IL_000d: ldloc.0 + IL_000e: ldc.i4.4 + IL_000f: beq.s IL_004d + + IL_0011: br.s IL_0013 + + IL_0013: ldloc.0 + IL_0014: ldc.i4.s 10 + IL_0016: sub + IL_0017: ldc.i4.1 + IL_0018: ble.un.s IL_004d + + IL_001a: br.s IL_0055 + + IL_001c: ldloc.0 + IL_001d: ldc.i4.s 13 + IL_001f: beq.s IL_004d + + IL_0021: br.s IL_0023 + + IL_0023: ldloc.0 + IL_0024: ldc.i4.s 21 + IL_0026: beq.s IL_004d + + IL_0028: br.s IL_0055 + + IL_002a: ldloc.0 + IL_002b: ldc.i4.s 33 + IL_002d: bgt.s IL_003d + + IL_002f: ldloc.0 + IL_0030: ldc.i4.s 29 + IL_0032: beq.s IL_004d + + IL_0034: br.s IL_0036 + + IL_0036: ldloc.0 + IL_0037: ldc.i4.s 33 + IL_0039: beq.s IL_004d + + IL_003b: br.s IL_0055 + + IL_003d: ldloc.0 + IL_003e: ldc.i4.s 49 + IL_0040: sub + IL_0041: ldc.i4.1 + IL_0042: ble.un.s IL_004d + + IL_0044: br.s IL_0046 + + IL_0046: ldloc.0 + IL_0047: ldc.i4.s 55 + IL_0049: beq.s IL_004d + + IL_004b: br.s IL_0055 + + IL_004d: call void [mscorlib]System.Console::WriteLine() + IL_0052: nop + IL_0053: br.s IL_0055 + + IL_0055: ret + } // end of method Switch::SparseIntegerSwitch2 + .method public hidebysig static string SwitchOverNullableInt(valuetype [mscorlib]System.Nullable`1 i) cil managed { diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/SwitchAnalysis.cs b/ICSharpCode.Decompiler/IL/ControlFlow/SwitchAnalysis.cs index 20767b136..72cab06b1 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/SwitchAnalysis.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/SwitchAnalysis.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; using System; +using System.Linq; namespace ICSharpCode.Decompiler.IL.ControlFlow { @@ -38,7 +39,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow public bool ContainsILSwitch { get; private set; } /// - /// Gets the sections that were detected by the previoous AnalyzeBlock() call. + /// Gets the sections that were detected by the previous AnalyzeBlock() call. /// public readonly List> Sections = new List>(); @@ -230,15 +231,36 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow return inst.MatchLdLoc(out switchVar); } + bool MatchSwitchVar(ILInstruction inst, out long sub) + { + if (inst.MatchBinaryNumericInstruction(BinaryNumericOperator.Sub, out var left, out var right) && right.MatchLdcI(out sub)) + return MatchSwitchVar(left); + + sub = 0; + return MatchSwitchVar(inst); + } + /// + /// Shifts a LongInterval, treating long.MinValue and long.MaxValue like float.Positive/NegativeInfinity + /// + LongInterval ShiftInterval(LongInterval interval, long offset) + { + return new LongInterval( + interval.Start == long.MinValue ? long.MinValue : interval.Start + offset, + interval.End == long.MinValue ? long.MinValue : interval.End + offset); + } + /// /// Analyzes the boolean condition, returning the set of values of the interesting /// variable for which the condition evaluates to true. /// private bool AnalyzeCondition(ILInstruction condition, out LongSet trueValues) { - if (condition is Comp comp && MatchSwitchVar(comp.Left) && comp.Right.MatchLdcI(out long val)) { + if (condition is Comp comp && MatchSwitchVar(comp.Left, out var sub) && comp.Right.MatchLdcI(out long val)) { // if (comp(V OP val)) trueValues = MakeSetWhereComparisonIsTrue(comp.Kind, val, comp.Sign); + if (sub != 0) + trueValues = new LongSet(trueValues.Intervals.Select(i => ShiftInterval(i, sub))); + return true; } else if (MatchSwitchVar(condition)) { // if (ldloc V) --> branch for all values except 0