[hlsl] Add `discard`

This CL add support for emitting `discard` and `TerminateInvocation`
instructions.

Bug: 42251045
Change-Id: Iab7971da453ae3aa52166e8999fe94b52179084b
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/195474
Reviewed-by: James Price <jrprice@google.com>
Commit-Queue: dan sinclair <dsinclair@chromium.org>
diff --git a/src/tint/lang/hlsl/writer/function_test.cc b/src/tint/lang/hlsl/writer/function_test.cc
index 436116a..e2b8660 100644
--- a/src/tint/lang/hlsl/writer/function_test.cc
+++ b/src/tint/lang/hlsl/writer/function_test.cc
@@ -822,7 +822,7 @@
 )");
 }
 
-TEST_F(HlslWriterTest, DISABLED_FunctionWithDiscardAndVoidReturn) {
+TEST_F(HlslWriterTest, FunctionWithDiscardAndVoidReturn) {
     // fn my_func(a: i32) {
     //   if (a == 0) {
     //     discard;
@@ -835,16 +835,19 @@
 
     b.Append(func->Block(), [&] {
         auto* i = b.If(b.Equal(ty.bool_(), p, 0_i));
-        b.Append(i->True(), [&] { b.Discard(); });
+        b.Append(i->True(), [&] {
+            b.Discard();
+            b.ExitIf(i);
+        });
         b.Return(func);
     });
 
     ASSERT_TRUE(Generate()) << err_ << output_.hlsl;
-    EXPECT_EQ(output_.hlsl, R"(void my_func(int a) {
+    EXPECT_EQ(output_.hlsl, R"(
+void my_func(int a) {
   if ((a == 0)) {
     discard;
   }
-  return;
 }
 
 [numthreads(1, 1, 1)]
@@ -854,6 +857,7 @@
 )");
 }
 
+// TODO(dsinclair): Needs transform to handle discard properly
 TEST_F(HlslWriterTest, DISABLED_FunctionWithDiscardAndNonVoidReturn) {
     // fn my_func(a: i32) -> i32 {
     //   if (a == 0) {
@@ -863,17 +867,21 @@
     // }
 
     auto* func = b.Function("my_func", ty.i32());
-    auto* p = b.FunctionParam("a", ty.i32());
-    func->SetParams({p});
+    auto* a = b.FunctionParam("a", ty.i32());
+    func->SetParams({a});
 
     b.Append(func->Block(), [&] {
-        auto* i = b.If(b.Equal(ty.bool_(), p, 0_i));
-        b.Append(i->True(), [&] { b.Discard(); });
+        auto* i = b.If(b.Equal(ty.bool_(), a, 0_i));
+        b.Append(i->True(), [&] {
+            b.Discard();
+            b.ExitIf(i);
+        });
         b.Return(func, 42_i);
     });
 
     ASSERT_TRUE(Generate()) << err_ << output_.hlsl;
-    EXPECT_EQ(output_.hlsl, R"(int my_func(int a) {
+    EXPECT_EQ(output_.hlsl, R"(
+int my_func(int a) {
   if (true) {
     if ((a == 0)) {
       discard;
@@ -939,7 +947,8 @@
     }
 
     ASSERT_TRUE(Generate()) << err_ << output_.hlsl;
-    EXPECT_EQ(output_.hlsl, R"(RWByteAddressBuffer data : register(u0);
+    EXPECT_EQ(output_.hlsl, R"(
+RWByteAddressBuffer data : register(u0);
 
 [numthreads(1, 1, 1)]
 void a() {
diff --git a/src/tint/lang/hlsl/writer/printer/printer.cc b/src/tint/lang/hlsl/writer/printer/printer.cc
index bbbb103..6b3fa75 100644
--- a/src/tint/lang/hlsl/writer/printer/printer.cc
+++ b/src/tint/lang/hlsl/writer/printer/printer.cc
@@ -57,6 +57,7 @@
 #include "src/tint/lang/core/ir/core_binary.h"
 #include "src/tint/lang/core/ir/core_builtin_call.h"
 #include "src/tint/lang/core/ir/core_unary.h"
+#include "src/tint/lang/core/ir/discard.h"
 #include "src/tint/lang/core/ir/exit_if.h"
 #include "src/tint/lang/core/ir/exit_loop.h"
 #include "src/tint/lang/core/ir/exit_switch.h"
@@ -74,6 +75,7 @@
 #include "src/tint/lang/core/ir/store_vector_element.h"
 #include "src/tint/lang/core/ir/switch.h"
 #include "src/tint/lang/core/ir/swizzle.h"
+#include "src/tint/lang/core/ir/terminate_invocation.h"
 #include "src/tint/lang/core/ir/unreachable.h"
 #include "src/tint/lang/core/ir/user_call.h"
 #include "src/tint/lang/core/ir/validator.h"
@@ -282,7 +284,11 @@
 
         for (auto* inst : *block) {
             Switch(
-                inst,                                                                       //
+                inst,
+                // Discard and TerminateInvocation must come before Call.
+                [&](const core::ir::Discard*) { EmitDiscard(); },              //
+                [&](const core::ir::TerminateInvocation*) { EmitDiscard(); },  //
+
                 [&](const core::ir::BreakIf* i) { EmitBreakIf(i); },                        //
                 [&](const core::ir::Call* i) { EmitCallStmt(i); },                          //
                 [&](const core::ir::Continue*) { EmitContinue(); },                         //
@@ -313,6 +319,8 @@
         }
     }
 
+    void EmitDiscard() { Line() << "discard;"; }
+
     void EmitStoreVectorElement(const core::ir::StoreVectorElement* l) {
         auto out = Line();