[hlsl] Validate unittests with DXC if available.

This CL the ability to run DXC against the generated code in the HLSL IR
unit tests. This requires the `dxcompiler` library to be available on
the users `PATH` in order to find DXC.

Bug: 42251045
Change-Id: I10828be589576763101da7d43e4cf29f08e05d93
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/196844
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Reviewed-by: James Price <jrprice@google.com>
Auto-Submit: dan sinclair <dsinclair@chromium.org>
Commit-Queue: James Price <jrprice@google.com>
diff --git a/src/tint/lang/hlsl/writer/BUILD.bazel b/src/tint/lang/hlsl/writer/BUILD.bazel
index f1743b9..bcb889a 100644
--- a/src/tint/lang/hlsl/writer/BUILD.bazel
+++ b/src/tint/lang/hlsl/writer/BUILD.bazel
@@ -113,6 +113,7 @@
     "//src/tint/lang/core/type",
     "//src/tint/lang/hlsl/writer/common",
     "//src/tint/lang/wgsl/ast",
+    "//src/tint/utils/command",
     "//src/tint/utils/containers",
     "//src/tint/utils/diagnostic",
     "//src/tint/utils/ice",
@@ -129,6 +130,7 @@
     "@gtest",
   ] + select({
     ":tint_build_hlsl_writer": [
+      "//src/tint/lang/hlsl/validate",
       "//src/tint/lang/hlsl/writer",
     ],
     "//conditions:default": [],
diff --git a/src/tint/lang/hlsl/writer/BUILD.cmake b/src/tint/lang/hlsl/writer/BUILD.cmake
index 2ea842e..cb0fdde 100644
--- a/src/tint/lang/hlsl/writer/BUILD.cmake
+++ b/src/tint/lang/hlsl/writer/BUILD.cmake
@@ -125,6 +125,7 @@
   tint_lang_core_type
   tint_lang_hlsl_writer_common
   tint_lang_wgsl_ast
+  tint_utils_command
   tint_utils_containers
   tint_utils_diagnostic
   tint_utils_ice
@@ -146,6 +147,7 @@
 
 if(TINT_BUILD_HLSL_WRITER)
   tint_target_add_dependencies(tint_lang_hlsl_writer_test test
+    tint_lang_hlsl_validate
     tint_lang_hlsl_writer
   )
 endif(TINT_BUILD_HLSL_WRITER)
diff --git a/src/tint/lang/hlsl/writer/BUILD.gn b/src/tint/lang/hlsl/writer/BUILD.gn
index 36e2f40..b4ff72d 100644
--- a/src/tint/lang/hlsl/writer/BUILD.gn
+++ b/src/tint/lang/hlsl/writer/BUILD.gn
@@ -117,6 +117,7 @@
         "${tint_src_dir}/lang/core/type",
         "${tint_src_dir}/lang/hlsl/writer/common",
         "${tint_src_dir}/lang/wgsl/ast",
+        "${tint_src_dir}/utils/command",
         "${tint_src_dir}/utils/containers",
         "${tint_src_dir}/utils/diagnostic",
         "${tint_src_dir}/utils/ice",
@@ -133,7 +134,10 @@
       ]
 
       if (tint_build_hlsl_writer) {
-        deps += [ "${tint_src_dir}/lang/hlsl/writer" ]
+        deps += [
+          "${tint_src_dir}/lang/hlsl/validate",
+          "${tint_src_dir}/lang/hlsl/writer",
+        ]
       }
     }
   }
diff --git a/src/tint/lang/hlsl/writer/access_test.cc b/src/tint/lang/hlsl/writer/access_test.cc
index 80964b2..d522c87 100644
--- a/src/tint/lang/hlsl/writer/access_test.cc
+++ b/src/tint/lang/hlsl/writer/access_test.cc
@@ -298,7 +298,13 @@
 )");
 }
 
-TEST_F(HlslWriterTest, AccessStorageVectorF16) {
+// TODO(dsinclair): Fails DXC validation
+// hlsl.hlsl:4:55: warning: use of right-shift operator ('>>') in template argument will require
+// parentheses in C++11 [-Wc++11-compat]
+//   vector<float16_t, 4> a = v.Load4<vector<float16_t, 4>>(0u);
+//                                                       ^
+//                                                      (      )
+TEST_F(HlslWriterTest, DISABLED_AccessStorageVectorF16) {
     auto* var = b.Var<storage, vec4<f16>, core::Access::kRead>("v");
     var->SetBindingPoint(0, 0);
 
@@ -575,7 +581,26 @@
 )");
 }
 
-TEST_F(HlslWriterTest, AccessDirectVariable) {
+// TODO(dsinclair): Fails DXC validation
+// hlsl.hlsl:9:9: warning: implicit truncation of vector type [-Wconversion]
+//   float a = v1[1u];
+//         ^
+// hlsl.hlsl:9:13: error: array index 1 is out of bounds
+//   float a = v1[1u];
+//             ^
+// hlsl.hlsl:3:3: note: array 'v1' declared here
+//   uint4 v1[1];
+//   ^
+// hlsl.hlsl:13:9: warning: implicit truncation of vector type [-Wconversion]
+//   float a = v2[1u];
+//         ^
+// hlsl.hlsl:13:13: error: array index 1 is out of bounds
+//   float a = v2[1u];
+//             ^
+// hlsl.hlsl:6:3: note: array 'v2' declared here
+//   uint4 v2[1];
+//   ^
+TEST_F(HlslWriterTest, DISABLED_AccessDirectVariable) {
     auto* var1 = b.Var<uniform, vec4<f32>, core::Access::kRead>("v1");
     var1->SetBindingPoint(0, 0);
     b.ir.root_block->Append(var1);
diff --git a/src/tint/lang/hlsl/writer/helper_test.h b/src/tint/lang/hlsl/writer/helper_test.h
index 931032a..7e97e2a 100644
--- a/src/tint/lang/hlsl/writer/helper_test.h
+++ b/src/tint/lang/hlsl/writer/helper_test.h
@@ -33,7 +33,10 @@
 #include "gtest/gtest.h"
 #include "src/tint/lang/core/ir/builder.h"
 #include "src/tint/lang/core/ir/validator.h"
+#include "src/tint/lang/hlsl/validate/validate.h"
 #include "src/tint/lang/hlsl/writer/writer.h"
+#include "src/tint/utils/command/command.h"
+#include "src/tint/utils/text/string.h"
 
 namespace tint::hlsl::writer {
 
@@ -65,6 +68,30 @@
         }
         output_ = result.Get();
 
+        const char* dxc_path = validate::kDxcDLLName;
+        auto dxc = tint::Command::LookPath(dxc_path);
+        if (dxc.Found()) {
+            uint32_t hlsl_shader_model = 66;
+            bool require_16bit_types = true;
+
+            auto validate_res =
+                validate::ValidateUsingDXC(dxc.Path(), output_.hlsl, output_.entry_points,
+                                           require_16bit_types, hlsl_shader_model);
+            if (validate_res.failed) {
+                size_t line_num = 1;
+
+                std::stringstream err;
+                err << "DXC was expected to succeed, but failed:\n\n";
+                for (auto line : Split(output_.hlsl, "\n")) {
+                    err << line_num++ << ": " << line << "\n";
+                }
+                err << "\n\n" << validate_res.output;
+
+                err_ = err.str();
+                return false;
+            }
+        }
+
         return true;
     }
 };
diff --git a/src/tint/lang/hlsl/writer/loop_test.cc b/src/tint/lang/hlsl/writer/loop_test.cc
index dab17a6..d52fffc 100644
--- a/src/tint/lang/hlsl/writer/loop_test.cc
+++ b/src/tint/lang/hlsl/writer/loop_test.cc
@@ -86,7 +86,9 @@
 )");
 }
 
-TEST_F(HlslWriterTest, LoopBodyVarInContinue) {
+// TODO(dsinclair): DXC error
+//  `hlsl.hlsl:3: error: Loop must have break.`
+TEST_F(HlslWriterTest, DISABLED_LoopBodyVarInContinue) {
     auto* func = b.Function("a", ty.void_(), core::ir::Function::PipelineStage::kCompute);
     func->SetWorkgroupSize(1, 1, 1);
 
@@ -119,7 +121,9 @@
 )");
 }
 
-TEST_F(HlslWriterTest, LoopInitializer) {
+// TODO(dsinclair): DXC Error:
+//  `hlsl.hlsl:3: error: Loop must have break.`
+TEST_F(HlslWriterTest, DISABLED_LoopInitializer) {
     auto* func = b.Function("a", ty.void_(), core::ir::Function::PipelineStage::kCompute);
     func->SetWorkgroupSize(1, 1, 1);