[tint][msl] Check module + options before generating

Move the `CanRun()` function from the writer fuzzer into the writer as
a helper function. This can then be used by both the fuzzer and the
Tint executable to check whether the backend can actually handle the
module+options.

This yields much more user friendly error messages from the Tint
executable when trying to generate backend code for a module that
contains features that are not supported by that backend.

Bug: 376572262
Change-Id: Ib451250b4898c3f46edaa4ba2e36d6d39e625d91
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/222095
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Commit-Queue: James Price <jrprice@google.com>
diff --git a/src/tint/cmd/tint/main.cc b/src/tint/cmd/tint/main.cc
index 846c90b..adcefd4 100644
--- a/src/tint/cmd/tint/main.cc
+++ b/src/tint/cmd/tint/main.cc
@@ -951,6 +951,14 @@
             std::cerr << "Failed to generate IR: " << ir << "\n";
             return false;
         }
+
+        // Check that the module and options are supported by the backend.
+        auto check = tint::msl::writer::CanGenerate(ir.Get(), gen_options);
+        if (check != tint::Success) {
+            std::cerr << check.Failure() << "\n";
+            return false;
+        }
+
         result = tint::msl::writer::Generate(ir.Get(), gen_options);
     } else {
         result = tint::msl::writer::Generate(input_program, gen_options);
diff --git a/src/tint/lang/msl/writer/BUILD.bazel b/src/tint/lang/msl/writer/BUILD.bazel
index 489aadd..c36364d 100644
--- a/src/tint/lang/msl/writer/BUILD.bazel
+++ b/src/tint/lang/msl/writer/BUILD.bazel
@@ -49,6 +49,7 @@
     "//src/tint/lang/core",
     "//src/tint/lang/core/common",
     "//src/tint/lang/core/constant",
+    "//src/tint/lang/core/ir",
     "//src/tint/lang/core/type",
     "//src/tint/lang/wgsl",
     "//src/tint/lang/wgsl/ast",
diff --git a/src/tint/lang/msl/writer/BUILD.cmake b/src/tint/lang/msl/writer/BUILD.cmake
index 536378a..71f367ea 100644
--- a/src/tint/lang/msl/writer/BUILD.cmake
+++ b/src/tint/lang/msl/writer/BUILD.cmake
@@ -57,6 +57,7 @@
   tint_lang_core
   tint_lang_core_common
   tint_lang_core_constant
+  tint_lang_core_ir
   tint_lang_core_type
   tint_lang_wgsl
   tint_lang_wgsl_ast
diff --git a/src/tint/lang/msl/writer/BUILD.gn b/src/tint/lang/msl/writer/BUILD.gn
index 9a2501bd..f003138 100644
--- a/src/tint/lang/msl/writer/BUILD.gn
+++ b/src/tint/lang/msl/writer/BUILD.gn
@@ -54,6 +54,7 @@
       "${tint_src_dir}/lang/core",
       "${tint_src_dir}/lang/core/common",
       "${tint_src_dir}/lang/core/constant",
+      "${tint_src_dir}/lang/core/ir",
       "${tint_src_dir}/lang/core/type",
       "${tint_src_dir}/lang/wgsl",
       "${tint_src_dir}/lang/wgsl/ast",
diff --git a/src/tint/lang/msl/writer/writer.cc b/src/tint/lang/msl/writer/writer.cc
index ad36b55..c623e4b 100644
--- a/src/tint/lang/msl/writer/writer.cc
+++ b/src/tint/lang/msl/writer/writer.cc
@@ -30,6 +30,9 @@
 #include <memory>
 #include <utility>
 
+#include "src/tint/lang/core/ir/module.h"
+#include "src/tint/lang/core/ir/var.h"
+#include "src/tint/lang/core/type/input_attachment.h"
 #include "src/tint/lang/msl/writer/ast_printer/ast_printer.h"
 #include "src/tint/lang/msl/writer/common/option_helpers.h"
 #include "src/tint/lang/msl/writer/printer/printer.h"
@@ -37,6 +40,24 @@
 
 namespace tint::msl::writer {
 
+Result<SuccessType> CanGenerate(const core::ir::Module& ir, const Options&) {
+    // Check for unsupported module-scope variable address spaces and types.
+    for (auto* inst : *ir.root_block) {
+        auto* var = inst->As<core::ir::Var>();
+        auto* ptr = var->Result(0)->Type()->As<core::type::Pointer>();
+        if (ptr->AddressSpace() == core::AddressSpace::kPushConstant) {
+            return Failure("push constants are not supported by the MSL backend");
+        }
+        if (ptr->AddressSpace() == core::AddressSpace::kPixelLocal) {
+            return Failure("pixel_local address space is not supported by the MSL backend");
+        }
+        if (ptr->StoreType()->Is<core::type::InputAttachment>()) {
+            return Failure("input attachments are not supported by the MSL backend");
+        }
+    }
+    return Success;
+}
+
 Result<Output> Generate(core::ir::Module& ir, const Options& options) {
     {
         auto res = ValidateBindingOptions(options);
diff --git a/src/tint/lang/msl/writer/writer.h b/src/tint/lang/msl/writer/writer.h
index cdfdb2c..93f6505 100644
--- a/src/tint/lang/msl/writer/writer.h
+++ b/src/tint/lang/msl/writer/writer.h
@@ -45,6 +45,12 @@
 
 namespace tint::msl::writer {
 
+/// Check if the module @p ir is supported by the MSL backend with @p options.
+/// @param ir the module
+/// @param options the writer options
+/// @returns Success or a failure message indicating why MSL generation would fail
+Result<SuccessType> CanGenerate(const core::ir::Module& ir, const Options& options);
+
 /// Generate MSL for a program, according to a set of configuration options.
 /// The result will contain the MSL and supplementary information, or failure.
 /// @param ir the IR module to translate to MSL
diff --git a/src/tint/lang/msl/writer/writer_fuzz.cc b/src/tint/lang/msl/writer/writer_fuzz.cc
index ba4dced..1be3150 100644
--- a/src/tint/lang/msl/writer/writer_fuzz.cc
+++ b/src/tint/lang/msl/writer/writer_fuzz.cc
@@ -30,7 +30,6 @@
 #include "src/tint/cmd/fuzz/ir/fuzz.h"
 #include "src/tint/lang/core/ir/module.h"
 #include "src/tint/lang/core/ir/var.h"
-#include "src/tint/lang/core/type/input_attachment.h"
 #include "src/tint/lang/core/type/pointer.h"
 #include "src/tint/lang/msl/writer/helpers/generate_bindings.h"
 #include "src/tint/lang/msl/writer/writer.h"
@@ -38,31 +37,9 @@
 namespace tint::msl::writer {
 namespace {
 
-bool CanRun(const core::ir::Module& module, const Options&) {
-    // Check for unsupported module-scope variable address spaces and types.
-    for (auto* inst : *module.root_block) {
-        auto* var = inst->As<core::ir::Var>();
-        auto* ptr = var->Result(0)->Type()->As<core::type::Pointer>();
-        if (ptr->AddressSpace() == core::AddressSpace::kPushConstant) {
-            return false;
-        }
-        if (ptr->AddressSpace() == core::AddressSpace::kPixelLocal) {
-            return false;
-        }
-        if (ptr->StoreType()->Is<core::type::InputAttachment>()) {
-            return false;
-        }
-    }
-    return true;
-}
-
 Result<SuccessType> IRFuzzer(core::ir::Module& module,
                              const fuzz::ir::Context& context,
                              Options options) {
-    if (!CanRun(module, options)) {
-        return Failure{"Cannot run module"};
-    }
-
     options.bindings = GenerateBindings(module);
     options.array_length_from_uniform.ubo_binding = 30;
 
@@ -80,6 +57,11 @@
         }
     }
 
+    auto check = CanGenerate(module, options);
+    if (check != Success) {
+        return check.Failure();
+    }
+
     auto output = Generate(module, options);
 
     if (output == Success && context.options.dump) {