HLSL-IR: Fix default-only-switch workaround for FXC

Existing implementation on the IR path would add a "case 0" along with
the default case, but did doesn't fix the FXC code gen bug. This patch
implements the same fix we did on the AST path: that is, to replace the
switch with a single-iteration loop.

Also opportunistically renamed FxcPolyfill to ReplaceDefaultOnlySwitch
as this was the only thing it handles, and we've decided to keep each
FXC-specific transform separate.

Bug: 368660972
Bug: 42250197
Change-Id: I9ad11fb089d792c3e6c9ce7be5eccae7fc52f2d2
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/213014
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Reviewed-by: James Price <jrprice@google.com>
Auto-Submit: Antonio Maiorano <amaiorano@google.com>
Commit-Queue: James Price <jrprice@google.com>
diff --git a/src/tint/lang/hlsl/writer/raise/BUILD.bazel b/src/tint/lang/hlsl/writer/raise/BUILD.bazel
index 77966f3..cf9a60d 100644
--- a/src/tint/lang/hlsl/writer/raise/BUILD.bazel
+++ b/src/tint/lang/hlsl/writer/raise/BUILD.bazel
@@ -43,11 +43,11 @@
     "builtin_polyfill.cc",
     "decompose_storage_access.cc",
     "decompose_uniform_access.cc",
-    "fxc_polyfill.cc",
     "localize_struct_array_assignment.cc",
     "pixel_local.cc",
     "promote_initializers.cc",
     "raise.cc",
+    "replace_default_only_switch.cc",
     "replace_non_indexable_mat_vec_stores.cc",
     "shader_io.cc",
   ],
@@ -56,11 +56,11 @@
     "builtin_polyfill.h",
     "decompose_storage_access.h",
     "decompose_uniform_access.h",
-    "fxc_polyfill.h",
     "localize_struct_array_assignment.h",
     "pixel_local.h",
     "promote_initializers.h",
     "raise.h",
+    "replace_default_only_switch.h",
     "replace_non_indexable_mat_vec_stores.h",
     "shader_io.h",
   ],
@@ -104,10 +104,10 @@
     "builtin_polyfill_test.cc",
     "decompose_storage_access_test.cc",
     "decompose_uniform_access_test.cc",
-    "fxc_polyfill_test.cc",
     "localize_struct_array_assignment_test.cc",
     "pixel_local_test.cc",
     "promote_initializers_test.cc",
+    "replace_default_only_switch_test.cc",
     "replace_non_indexable_mat_vec_stores_test.cc",
     "shader_io_test.cc",
   ],
diff --git a/src/tint/lang/hlsl/writer/raise/BUILD.cmake b/src/tint/lang/hlsl/writer/raise/BUILD.cmake
index 7688276..a21b203 100644
--- a/src/tint/lang/hlsl/writer/raise/BUILD.cmake
+++ b/src/tint/lang/hlsl/writer/raise/BUILD.cmake
@@ -47,8 +47,6 @@
   lang/hlsl/writer/raise/decompose_storage_access.h
   lang/hlsl/writer/raise/decompose_uniform_access.cc
   lang/hlsl/writer/raise/decompose_uniform_access.h
-  lang/hlsl/writer/raise/fxc_polyfill.cc
-  lang/hlsl/writer/raise/fxc_polyfill.h
   lang/hlsl/writer/raise/localize_struct_array_assignment.cc
   lang/hlsl/writer/raise/localize_struct_array_assignment.h
   lang/hlsl/writer/raise/pixel_local.cc
@@ -57,6 +55,8 @@
   lang/hlsl/writer/raise/promote_initializers.h
   lang/hlsl/writer/raise/raise.cc
   lang/hlsl/writer/raise/raise.h
+  lang/hlsl/writer/raise/replace_default_only_switch.cc
+  lang/hlsl/writer/raise/replace_default_only_switch.h
   lang/hlsl/writer/raise/replace_non_indexable_mat_vec_stores.cc
   lang/hlsl/writer/raise/replace_non_indexable_mat_vec_stores.h
   lang/hlsl/writer/raise/shader_io.cc
@@ -105,10 +105,10 @@
   lang/hlsl/writer/raise/builtin_polyfill_test.cc
   lang/hlsl/writer/raise/decompose_storage_access_test.cc
   lang/hlsl/writer/raise/decompose_uniform_access_test.cc
-  lang/hlsl/writer/raise/fxc_polyfill_test.cc
   lang/hlsl/writer/raise/localize_struct_array_assignment_test.cc
   lang/hlsl/writer/raise/pixel_local_test.cc
   lang/hlsl/writer/raise/promote_initializers_test.cc
+  lang/hlsl/writer/raise/replace_default_only_switch_test.cc
   lang/hlsl/writer/raise/replace_non_indexable_mat_vec_stores_test.cc
   lang/hlsl/writer/raise/shader_io_test.cc
 )
diff --git a/src/tint/lang/hlsl/writer/raise/BUILD.gn b/src/tint/lang/hlsl/writer/raise/BUILD.gn
index 6eef83a..ecc27a1 100644
--- a/src/tint/lang/hlsl/writer/raise/BUILD.gn
+++ b/src/tint/lang/hlsl/writer/raise/BUILD.gn
@@ -53,8 +53,6 @@
     "decompose_storage_access.h",
     "decompose_uniform_access.cc",
     "decompose_uniform_access.h",
-    "fxc_polyfill.cc",
-    "fxc_polyfill.h",
     "localize_struct_array_assignment.cc",
     "localize_struct_array_assignment.h",
     "pixel_local.cc",
@@ -63,6 +61,8 @@
     "promote_initializers.h",
     "raise.cc",
     "raise.h",
+    "replace_default_only_switch.cc",
+    "replace_default_only_switch.h",
     "replace_non_indexable_mat_vec_stores.cc",
     "replace_non_indexable_mat_vec_stores.h",
     "shader_io.cc",
@@ -105,10 +105,10 @@
       "builtin_polyfill_test.cc",
       "decompose_storage_access_test.cc",
       "decompose_uniform_access_test.cc",
-      "fxc_polyfill_test.cc",
       "localize_struct_array_assignment_test.cc",
       "pixel_local_test.cc",
       "promote_initializers_test.cc",
+      "replace_default_only_switch_test.cc",
       "replace_non_indexable_mat_vec_stores_test.cc",
       "shader_io_test.cc",
     ]
diff --git a/src/tint/lang/hlsl/writer/raise/raise.cc b/src/tint/lang/hlsl/writer/raise/raise.cc
index 3abedb7..f526849 100644
--- a/src/tint/lang/hlsl/writer/raise/raise.cc
+++ b/src/tint/lang/hlsl/writer/raise/raise.cc
@@ -52,10 +52,10 @@
 #include "src/tint/lang/hlsl/writer/raise/builtin_polyfill.h"
 #include "src/tint/lang/hlsl/writer/raise/decompose_storage_access.h"
 #include "src/tint/lang/hlsl/writer/raise/decompose_uniform_access.h"
-#include "src/tint/lang/hlsl/writer/raise/fxc_polyfill.h"
 #include "src/tint/lang/hlsl/writer/raise/localize_struct_array_assignment.h"
 #include "src/tint/lang/hlsl/writer/raise/pixel_local.h"
 #include "src/tint/lang/hlsl/writer/raise/promote_initializers.h"
+#include "src/tint/lang/hlsl/writer/raise/replace_default_only_switch.h"
 #include "src/tint/lang/hlsl/writer/raise/replace_non_indexable_mat_vec_stores.h"
 #include "src/tint/lang/hlsl/writer/raise/shader_io.h"
 #include "src/tint/utils/result/result.h"
@@ -128,7 +128,7 @@
     RUN_TRANSFORM(core::ir::transform::AddEmptyEntryPoint, module);
 
     if (options.compiler == Options::Compiler::kFXC) {
-        RUN_TRANSFORM(raise::FxcPolyfill, module);
+        RUN_TRANSFORM(raise::ReplaceDefaultOnlySwitch, module);
         RUN_TRANSFORM(raise::LocalizeStructArrayAssignment, module);
         RUN_TRANSFORM(raise::ReplaceNonIndexableMatVecStores, module);
     }
diff --git a/src/tint/lang/hlsl/writer/raise/fxc_polyfill.cc b/src/tint/lang/hlsl/writer/raise/replace_default_only_switch.cc
similarity index 64%
rename from src/tint/lang/hlsl/writer/raise/fxc_polyfill.cc
rename to src/tint/lang/hlsl/writer/raise/replace_default_only_switch.cc
index 1d332e2..10a44b1 100644
--- a/src/tint/lang/hlsl/writer/raise/fxc_polyfill.cc
+++ b/src/tint/lang/hlsl/writer/raise/replace_default_only_switch.cc
@@ -25,7 +25,7 @@
 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-#include "src/tint/lang/hlsl/writer/raise/fxc_polyfill.h"
+#include "src/tint/lang/hlsl/writer/raise/replace_default_only_switch.h"
 
 #include "src/tint/lang/core/ir/block.h"
 #include "src/tint/lang/core/ir/builder.h"
@@ -56,26 +56,51 @@
 
     /// Process the module.
     void Process() {
+        Vector<core::ir::Switch*, 4> worklist;
         for (auto* inst : ir.Instructions()) {
             if (auto* swtch = inst->As<core::ir::Switch>()) {
-                // BUG(crbug.com/tint/1188): work around default-only switches
-                //
-                // FXC fails to compile a switch with just a default case, ignoring the
-                // default case body. We work around this here by emitting the zero case as a
-                // fallthrough to the default case
                 if (swtch->Cases().Length() == 1 && swtch->Cases()[0].selectors.Length() == 1 &&
                     swtch->Cases()[0].selectors[0].IsDefault()) {
-                    swtch->Cases()[0].selectors.Push({b.Zero(swtch->Condition()->Type())});
+                    ProcessSwitch(swtch);
                 }
             }
         }
     }
+
+    void ProcessSwitch(core::ir::Switch* swtch) {
+        // Replace the switch with a one-iteration loop. We use a loop so that 'break's
+        // in switch used for control flow will continue to work.
+        // Note that we can't just add a 'case 0' fallthrough on the 'default' as FXC treates it
+        // the same as just a 'default', resulting in the same miscompilation.
+
+        auto* loop = b.Loop();
+        loop->InsertBefore(swtch);
+
+        // Replace all switch exits with loop exits. This includes nested exits.
+        auto exits = swtch->Exits().Vector();  // Copy to avoid iterator invalidation
+        for (auto& exit : exits) {
+            exit->ReplaceWith(b.ExitLoop(loop));
+            exit->Destroy();
+        }
+
+        // Move all instructions from the default case to the loop body
+        auto swtch_default_block = swtch->Cases()[0].block;
+        for (auto* inst = *swtch_default_block->begin(); inst;) {
+            // Remember next instruction as we're about to remove the current one from its block
+            auto* next = inst->next.Get();
+            TINT_DEFER(inst = next);
+            inst->Remove();
+            loop->Body()->Append(inst);
+        }
+
+        swtch->Destroy();
+    }
 };
 
 }  // namespace
 
-Result<SuccessType> FxcPolyfill(core::ir::Module& ir) {
-    auto result = ValidateAndDumpIfNeeded(ir, "hlsl.FxcPolyfill");
+Result<SuccessType> ReplaceDefaultOnlySwitch(core::ir::Module& ir) {
+    auto result = ValidateAndDumpIfNeeded(ir, "hlsl.ReplaceDefaultOnlySwitch");
     if (result != Success) {
         return result.Failure();
     }
diff --git a/src/tint/lang/hlsl/writer/raise/fxc_polyfill.h b/src/tint/lang/hlsl/writer/raise/replace_default_only_switch.h
similarity index 76%
rename from src/tint/lang/hlsl/writer/raise/fxc_polyfill.h
rename to src/tint/lang/hlsl/writer/raise/replace_default_only_switch.h
index 1cedaac..b11d052 100644
--- a/src/tint/lang/hlsl/writer/raise/fxc_polyfill.h
+++ b/src/tint/lang/hlsl/writer/raise/replace_default_only_switch.h
@@ -25,10 +25,8 @@
 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-#ifndef SRC_TINT_LANG_HLSL_WRITER_RAISE_FXC_POLYFILL_H_
-#define SRC_TINT_LANG_HLSL_WRITER_RAISE_FXC_POLYFILL_H_
-
-#include <string>
+#ifndef SRC_TINT_LANG_HLSL_WRITER_RAISE_REPLACE_DEFAULT_ONLY_SWITCH_H_
+#define SRC_TINT_LANG_HLSL_WRITER_RAISE_REPLACE_DEFAULT_ONLY_SWITCH_H_
 
 #include "src/tint/utils/result/result.h"
 
@@ -39,12 +37,13 @@
 
 namespace tint::hlsl::writer::raise {
 
-/// FxcPollyfill is a transform that replaces code constructs which cause FXC mis-compiles with
-/// safer constructs.
+/// ReplaceDefaultOnlySwitch is a transform that replaces switch statements with a single
+/// default case with a single-iteration loop. This is to work around an FXC bug that causes
+/// it to miscompile default-only-switch statements. See crbug.com/tint/1188.
 /// @param module the module to transform
 /// @returns success or failure
-Result<SuccessType> FxcPolyfill(core::ir::Module& module);
+Result<SuccessType> ReplaceDefaultOnlySwitch(core::ir::Module& module);
 
 }  // namespace tint::hlsl::writer::raise
 
-#endif  // SRC_TINT_LANG_HLSL_WRITER_RAISE_FXC_POLYFILL_H_
+#endif  // SRC_TINT_LANG_HLSL_WRITER_RAISE_REPLACE_DEFAULT_ONLY_SWITCH_H_
diff --git a/src/tint/lang/hlsl/writer/raise/fxc_polyfill_test.cc b/src/tint/lang/hlsl/writer/raise/replace_default_only_switch_test.cc
similarity index 68%
rename from src/tint/lang/hlsl/writer/raise/fxc_polyfill_test.cc
rename to src/tint/lang/hlsl/writer/raise/replace_default_only_switch_test.cc
index 45302a4..73640c7 100644
--- a/src/tint/lang/hlsl/writer/raise/fxc_polyfill_test.cc
+++ b/src/tint/lang/hlsl/writer/raise/replace_default_only_switch_test.cc
@@ -25,7 +25,7 @@
 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-#include "src/tint/lang/hlsl/writer/raise/fxc_polyfill.h"
+#include "src/tint/lang/hlsl/writer/raise/replace_default_only_switch.h"
 
 #include <gtest/gtest.h>
 
@@ -40,10 +40,10 @@
 namespace tint::hlsl::writer::raise {
 namespace {
 
-using HlslWriterFxcPolyfillTest = core::ir::transform::TransformTest;
+using HlslWriterReplaceDefaultOnlySwitchTest = core::ir::transform::TransformTest;
 
 // No change, no switch
-TEST_F(HlslWriterFxcPolyfillTest, NoSwitch) {
+TEST_F(HlslWriterReplaceDefaultOnlySwitchTest, NoSwitch) {
     auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
     b.Append(func->Block(), [&] { b.Return(func); });
 
@@ -57,13 +57,13 @@
     EXPECT_EQ(src, str());
 
     auto* expect = src;
-    Run(FxcPolyfill);
+    Run(ReplaceDefaultOnlySwitch);
 
     EXPECT_EQ(expect, str());
 }
 
 // No change, switch with case and default
-TEST_F(HlslWriterFxcPolyfillTest, SwitchCaseAndDefault) {
+TEST_F(HlslWriterReplaceDefaultOnlySwitchTest, SwitchCaseAndDefault) {
     auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
     b.Append(func->Block(), [&] {
         auto* s = b.Switch(1_i);
@@ -91,13 +91,13 @@
     EXPECT_EQ(src, str());
 
     auto* expect = src;
-    Run(FxcPolyfill);
+    Run(ReplaceDefaultOnlySwitch);
 
     EXPECT_EQ(expect, str());
 }
 
 // No change, switch with multi-selector default case
-TEST_F(HlslWriterFxcPolyfillTest, SwitchMultiSelectorDefault) {
+TEST_F(HlslWriterReplaceDefaultOnlySwitchTest, SwitchMultiSelectorDefault) {
     auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
     b.Append(func->Block(), [&] {
         auto* s = b.Switch(1_i);
@@ -120,13 +120,13 @@
     EXPECT_EQ(src, str());
 
     auto* expect = src;
-    Run(FxcPolyfill);
+    Run(ReplaceDefaultOnlySwitch);
 
     EXPECT_EQ(expect, str());
 }
 
 // Switch body just has a ExitSwitch
-TEST_F(HlslWriterFxcPolyfillTest, Switch) {
+TEST_F(HlslWriterReplaceDefaultOnlySwitchTest, Switch) {
     auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
     b.Append(func->Block(), [&] {
         auto* s = b.Switch(1_i);
@@ -152,9 +152,9 @@
     auto* expect = R"(
 %foo = @fragment func():void {
   $B1: {
-    switch 1i [c: (default 0i, $B2)] {  # switch_1
-      $B2: {  # case
-        exit_switch  # switch_1
+    loop [b: $B2] {  # loop_1
+      $B2: {  # body
+        exit_loop  # loop_1
       }
     }
     ret
@@ -162,13 +162,13 @@
 }
 )";
 
-    Run(FxcPolyfill);
+    Run(ReplaceDefaultOnlySwitch);
 
     EXPECT_EQ(expect, str());
 }
 
 // Switch body with assignment
-TEST_F(HlslWriterFxcPolyfillTest, SwitchWithAssignment) {
+TEST_F(HlslWriterReplaceDefaultOnlySwitchTest, SwitchWithAssignment) {
     auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
 
     auto* a = b.Var<private_>("a", b.Zero<i32>());
@@ -210,10 +210,10 @@
 
 %foo = @fragment func():void {
   $B2: {
-    switch 1i [c: (default 0i, $B3)] {  # switch_1
-      $B3: {  # case
+    loop [b: $B3] {  # loop_1
+      $B3: {  # body
         store %a, 1i
-        exit_switch  # switch_1
+        exit_loop  # loop_1
       }
     }
     ret
@@ -221,13 +221,13 @@
 }
 )";
 
-    Run(FxcPolyfill);
+    Run(ReplaceDefaultOnlySwitch);
 
     EXPECT_EQ(expect, str());
 }
 
 // Switch with if with break
-TEST_F(HlslWriterFxcPolyfillTest, SwitchWithIfBreak) {
+TEST_F(HlslWriterReplaceDefaultOnlySwitchTest, SwitchWithIfBreak) {
     auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
 
     auto* a = b.Var<private_>("a", b.Zero<i32>());
@@ -278,15 +278,15 @@
 
 %foo = @fragment func():void {
   $B2: {
-    switch 1i [c: (default 0i, $B3)] {  # switch_1
-      $B3: {  # case
+    loop [b: $B3] {  # loop_1
+      $B3: {  # body
         if true [t: $B4] {  # if_1
           $B4: {  # true
             store %a, 1i
-            exit_switch  # switch_1
+            exit_loop  # loop_1
           }
         }
-        exit_switch  # switch_1
+        exit_loop  # loop_1
       }
     }
     ret
@@ -294,13 +294,13 @@
 }
 )";
 
-    Run(FxcPolyfill);
+    Run(ReplaceDefaultOnlySwitch);
 
     EXPECT_EQ(expect, str());
 }
 
 // Switch with loop with break
-TEST_F(HlslWriterFxcPolyfillTest, SwitchWithLoopBreak) {
+TEST_F(HlslWriterReplaceDefaultOnlySwitchTest, SwitchWithLoopBreak) {
     auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
 
     auto* a = b.Var<private_>("a", b.Zero<i32>());
@@ -351,12 +351,80 @@
 
 %foo = @fragment func():void {
   $B2: {
-    switch 1i [c: (default 0i, $B3)] {  # switch_1
-      $B3: {  # case
-        loop [b: $B4] {  # loop_1
+    loop [b: $B3] {  # loop_1
+      $B3: {  # body
+        loop [b: $B4] {  # loop_2
           $B4: {  # body
             store %a, 1i
-            exit_loop  # loop_1
+            exit_loop  # loop_2
+          }
+        }
+        exit_loop  # loop_1
+      }
+    }
+    ret
+  }
+}
+)";
+
+    Run(ReplaceDefaultOnlySwitch);
+
+    EXPECT_EQ(expect, str());
+}
+
+// Switch with if with break
+TEST_F(HlslWriterReplaceDefaultOnlySwitchTest, SwitchWithNestedSwitch) {
+    auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
+
+    auto* a = b.Var<private_>("a", b.Zero<i32>());
+    b.ir.root_block->Append(a);
+
+    b.Append(func->Block(), [&] {
+        auto* s1 = b.Switch(1_i);
+        b.Append(b.DefaultCase(s1), [&] {
+            auto* if1 = b.If(true);
+            b.Append(if1->True(), [&] {
+                b.Store(a, 1_i);
+                auto* s2 = b.Switch(2_i);
+                b.Append(b.DefaultCase(s2), [&] {
+                    auto* if2 = b.If(true);
+                    b.Append(if2->True(), [&] {
+                        b.Store(a, 2_i);
+                        b.ExitSwitch(s2);
+                    });
+                    b.ExitSwitch(s2);
+                });
+                b.ExitSwitch(s1);
+            });
+            b.ExitSwitch(s1);
+        });
+        b.Return(func);
+    });
+
+    auto* src = R"(
+$B1: {  # root
+  %a:ptr<private, i32, read_write> = var, 0i
+}
+
+%foo = @fragment func():void {
+  $B2: {
+    switch 1i [c: (default, $B3)] {  # switch_1
+      $B3: {  # case
+        if true [t: $B4] {  # if_1
+          $B4: {  # true
+            store %a, 1i
+            switch 2i [c: (default, $B5)] {  # switch_2
+              $B5: {  # case
+                if true [t: $B6] {  # if_2
+                  $B6: {  # true
+                    store %a, 2i
+                    exit_switch  # switch_2
+                  }
+                }
+                exit_switch  # switch_2
+              }
+            }
+            exit_switch  # switch_1
           }
         }
         exit_switch  # switch_1
@@ -367,7 +435,43 @@
 }
 )";
 
-    Run(FxcPolyfill);
+    EXPECT_EQ(src, str());
+
+    auto* expect = R"(
+$B1: {  # root
+  %a:ptr<private, i32, read_write> = var, 0i
+}
+
+%foo = @fragment func():void {
+  $B2: {
+    loop [b: $B3] {  # loop_1
+      $B3: {  # body
+        if true [t: $B4] {  # if_1
+          $B4: {  # true
+            store %a, 1i
+            loop [b: $B5] {  # loop_2
+              $B5: {  # body
+                if true [t: $B6] {  # if_2
+                  $B6: {  # true
+                    store %a, 2i
+                    exit_loop  # loop_2
+                  }
+                }
+                exit_loop  # loop_2
+              }
+            }
+            exit_loop  # loop_1
+          }
+        }
+        exit_loop  # loop_1
+      }
+    }
+    ret
+  }
+}
+)";
+
+    Run(ReplaceDefaultOnlySwitch);
 
     EXPECT_EQ(expect, str());
 }
diff --git a/src/tint/lang/hlsl/writer/switch_test.cc b/src/tint/lang/hlsl/writer/switch_test.cc
index 72aee72..777c725 100644
--- a/src/tint/lang/hlsl/writer/switch_test.cc
+++ b/src/tint/lang/hlsl/writer/switch_test.cc
@@ -229,10 +229,8 @@
 void foo() {
   int cond = int(0);
   int a = int(0);
-  switch(cond) {
-    default:
-    case int(0):
-    {
+  {
+    while(true) {
       a = int(42);
       break;
     }
@@ -295,10 +293,9 @@
 
 [numthreads(1, 1, 1)]
 void foo() {
-  switch(bar()) {
-    default:
-    case int(0):
-    {
+  bar();
+  {
+    while(true) {
       a = int(42);
       break;
     }
diff --git a/test/tint/bug/tint/1820.wgsl.expected.ir.fxc.hlsl b/test/tint/bug/tint/1820.wgsl.expected.ir.fxc.hlsl
index 3cdb20b..e0086b9 100644
--- a/test/tint/bug/tint/1820.wgsl.expected.ir.fxc.hlsl
+++ b/test/tint/bug/tint/1820.wgsl.expected.ir.fxc.hlsl
@@ -5,10 +5,9 @@
 }
 
 void foo(float x) {
-  switch(tint_f32_to_i32(x)) {
-    default:
-    case int(0):
-    {
+  tint_f32_to_i32(x);
+  {
+    while(true) {
       break;
     }
   }
@@ -20,10 +19,9 @@
 }
 
 void bar(float x) {
-  switch(baz(tint_f32_to_i32(x))) {
-    default:
-    case int(0):
-    {
+  baz(tint_f32_to_i32(x));
+  {
+    while(true) {
       break;
     }
   }
diff --git a/test/tint/diagnostic_filtering/default_case_body_attribute.wgsl.expected.ir.fxc.hlsl b/test/tint/diagnostic_filtering/default_case_body_attribute.wgsl.expected.ir.fxc.hlsl
index b98ea35..03a056f 100644
--- a/test/tint/diagnostic_filtering/default_case_body_attribute.wgsl.expected.ir.fxc.hlsl
+++ b/test/tint/diagnostic_filtering/default_case_body_attribute.wgsl.expected.ir.fxc.hlsl
@@ -22,10 +22,9 @@
 }
 
 void main_inner(float x) {
-  switch(tint_f32_to_i32(x)) {
-    default:
-    case int(0):
-    {
+  tint_f32_to_i32(x);
+  {
+    while(true) {
       t.Sample(s, (0.0f).xx);
       break;
     }
diff --git a/test/tint/diagnostic_filtering/switch_body_attribute.wgsl.expected.ir.fxc.hlsl b/test/tint/diagnostic_filtering/switch_body_attribute.wgsl.expected.ir.fxc.hlsl
index da10aa1..98bd098 100644
--- a/test/tint/diagnostic_filtering/switch_body_attribute.wgsl.expected.ir.fxc.hlsl
+++ b/test/tint/diagnostic_filtering/switch_body_attribute.wgsl.expected.ir.fxc.hlsl
@@ -20,10 +20,9 @@
 }
 
 void main_inner(float x) {
-  switch(tint_f32_to_i32(x)) {
-    default:
-    case int(0):
-    {
+  tint_f32_to_i32(x);
+  {
+    while(true) {
       ddx(1.0f);
       break;
     }
diff --git a/test/tint/diagnostic_filtering/switch_statement_attribute.wgsl.expected.ir.fxc.hlsl b/test/tint/diagnostic_filtering/switch_statement_attribute.wgsl.expected.ir.fxc.hlsl
index a67af25..ef743be 100644
--- a/test/tint/diagnostic_filtering/switch_statement_attribute.wgsl.expected.ir.fxc.hlsl
+++ b/test/tint/diagnostic_filtering/switch_statement_attribute.wgsl.expected.ir.fxc.hlsl
@@ -24,10 +24,9 @@
   } else {
     v = false;
   }
-  switch(int(v)) {
-    default:
-    case int(0):
-    {
+  int v_1 = int(v);
+  {
+    while(true) {
       break;
     }
   }
diff --git a/test/tint/statements/switch/only_default_case.wgsl.expected.ir.fxc.hlsl b/test/tint/statements/switch/only_default_case.wgsl.expected.ir.fxc.hlsl
index 10d50ac..e84d3b9 100644
--- a/test/tint/statements/switch/only_default_case.wgsl.expected.ir.fxc.hlsl
+++ b/test/tint/statements/switch/only_default_case.wgsl.expected.ir.fxc.hlsl
@@ -3,10 +3,8 @@
 void f() {
   int i = int(0);
   int result = int(0);
-  switch(i) {
-    default:
-    case int(0):
-    {
+  {
+    while(true) {
       result = int(44);
       break;
     }
diff --git a/test/tint/switch/switch_only_default.wgsl.expected.ir.fxc.hlsl b/test/tint/switch/switch_only_default.wgsl.expected.ir.fxc.hlsl
index 5986b3a..2964637 100644
--- a/test/tint/switch/switch_only_default.wgsl.expected.ir.fxc.hlsl
+++ b/test/tint/switch/switch_only_default.wgsl.expected.ir.fxc.hlsl
@@ -1,10 +1,8 @@
 
 void a() {
   int a_1 = int(0);
-  switch(a_1) {
-    default:
-    case int(0):
-    {
+  {
+    while(true) {
       return;
     }
   }