diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
index 647dcdaf5..355464e64 100644
--- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
+++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
@@ -83,6 +83,7 @@ namespace ICSharpCode.Decompiler.CSharp
new SplitVariables(), // split variables once again, because the stobj(ldloca V, ...) may open up new replacements
new SwitchDetection(),
new SwitchOnStringTransform(),
+ new SwitchOnNullableTransform(),
new BlockILTransform { // per-block transforms
PostOrderTransforms = {
// Even though it's a post-order block-transform as most other transforms,
diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
index 534af2cbe..7fee5ff0d 100644
--- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
+++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
@@ -277,6 +277,7 @@
+
diff --git a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnNullableTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnNullableTransform.cs
new file mode 100644
index 000000000..30005e06b
--- /dev/null
+++ b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnNullableTransform.cs
@@ -0,0 +1,117 @@
+// Copyright (c) 2017 Siegfried Pammer
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using ICSharpCode.Decompiler.IL.ControlFlow;
+using ICSharpCode.Decompiler.TypeSystem;
+using ICSharpCode.Decompiler.Util;
+
+namespace ICSharpCode.Decompiler.IL.Transforms
+{
+ ///
+ /// Detects switch-on-nullable patterns employed by the C# compiler and transforms them to an ILAst-switch-instruction.
+ ///
+ class SwitchOnNullableTransform : IILTransform
+ {
+ public void Run(ILFunction function, ILTransformContext context)
+ {
+ if (!context.Settings.LiftNullables)
+ return;
+
+ HashSet changedContainers = new HashSet();
+
+ foreach (var block in function.Descendants.OfType()) {
+ bool changed = false;
+ for (int i = block.Instructions.Count - 1; i >= 0; i--) {
+ SwitchInstruction newSwitch;
+ if (MatchSwitchOnNullable(block.Instructions, i, out newSwitch)) {
+ block.Instructions[i + 1].ReplaceWith(newSwitch);
+ block.Instructions.RemoveRange(i - 2, 3);
+ i -= 2;
+ changed = true;
+ continue;
+ }
+ }
+ if (!changed) continue;
+ SwitchDetection.SimplifySwitchInstruction(block);
+ if (block.Parent is BlockContainer container)
+ changedContainers.Add(container);
+ }
+
+ foreach (var container in changedContainers)
+ container.SortBlocks(deleteUnreachableBlocks: true);
+ }
+
+ bool MatchSwitchOnNullable(InstructionCollection instructions, int i, out SwitchInstruction newSwitch)
+ {
+ newSwitch = null;
+ // match first block:
+ // stloc tmp(ldloca switchValueVar)
+ // stloc switchVariable(call GetValueOrDefault(ldloc tmp))
+ // if (logic.not(call get_HasValue(ldloc tmp))) br nullCaseBlock
+ // br switchBlock
+ if (i < 2) return false;
+ if (!instructions[i - 2].MatchStLoc(out var tmp, out var ldloca) ||
+ !instructions[i - 1].MatchStLoc(out var switchVariable, out var getValueOrDefault) ||
+ !instructions[i].MatchIfInstruction(out var condition, out var trueInst))
+ return false;
+ if (!instructions[i + 1].MatchBranch(out var switchBlock) || !trueInst.MatchBranch(out var nullCaseBlock))
+ return false;
+ if (!ldloca.MatchLdLoca(out var switchValueVar))
+ return false;
+ if (!condition.MatchLogicNot(out var getHasValue))
+ return false;
+ if (!(getValueOrDefault is Call getValueOrDefaultCall) || getValueOrDefaultCall.Method.FullName != "System.Nullable.GetValueOrDefault" ||
+ getValueOrDefaultCall.Method.DeclaringType.TypeParameterCount != 1)
+ return false;
+ if (!(getHasValue is Call getHasValueCall) || !getHasValueCall.Method.IsAccessor || getHasValueCall.Method.FullName != "System.Nullable.get_HasValue" ||
+ getHasValueCall.Method.DeclaringType.TypeParameterCount != 1)
+ return false;
+ if (getHasValueCall.Arguments.Count != 1 || getValueOrDefaultCall.Arguments.Count != 1)
+ return false;
+ if (!getHasValueCall.Arguments[0].MatchLdLoc(tmp) || !getValueOrDefaultCall.Arguments[0].MatchLdLoc(tmp))
+ return false;
+ // match second block: switchBlock
+ // switch (ldloc swtichVariable) {
+ // case [0..1): br caseBlock1
+ // ... more cases ...
+ // case [long.MinValue..0),[1..5),[6..10),[11..long.MaxValue]: br defaultBlock
+ // }
+ if (switchBlock.Instructions.Count != 1 || switchBlock.IncomingEdgeCount != 1)
+ return false;
+ if (!(switchBlock.Instructions[0] is SwitchInstruction switchInst))
+ return false;
+ newSwitch = new SwitchInstruction(new LdLoc(switchValueVar));
+ newSwitch.IsLifted = true;
+ SwitchSection defaultSection = null;
+ foreach (var section in switchInst.Sections) {
+ if (defaultSection == null || section.Labels.Count() >= defaultSection.Labels.Count())
+ defaultSection = section;
+ newSwitch.Sections.Add(section);
+ }
+ if (defaultSection.Body.MatchBranch(out var defaultBlock) && defaultBlock == nullCaseBlock)
+ defaultSection.HasNullLabel = true;
+ else {
+ newSwitch.Sections.Add(new SwitchSection { Body = new Branch(nullCaseBlock), HasNullLabel = true });
+ }
+ return true;
+ }
+ }
+}