[ir] Add an IR option into the ICE machinery.

This CL adds a `TINT_IR_ICE` which takes the IR module and dumps the
disassembly before the ICE error message.

Bug: tint:1718
Change-Id: Ibcfc5acb97704acec36ae5c537af4869832c428a
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/161760
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/lang/core/ir/BUILD.bazel b/src/tint/lang/core/ir/BUILD.bazel
index a07a52e..3c62fe9 100644
--- a/src/tint/lang/core/ir/BUILD.bazel
+++ b/src/tint/lang/core/ir/BUILD.bazel
@@ -113,6 +113,7 @@
     "exit_switch.h",
     "function.h",
     "function_param.h",
+    "ice.h",
     "if.h",
     "instruction.h",
     "instruction_result.h",
diff --git a/src/tint/lang/core/ir/BUILD.cmake b/src/tint/lang/core/ir/BUILD.cmake
index 028790d..19c6a8a 100644
--- a/src/tint/lang/core/ir/BUILD.cmake
+++ b/src/tint/lang/core/ir/BUILD.cmake
@@ -89,6 +89,7 @@
   lang/core/ir/function.h
   lang/core/ir/function_param.cc
   lang/core/ir/function_param.h
+  lang/core/ir/ice.h
   lang/core/ir/if.cc
   lang/core/ir/if.h
   lang/core/ir/instruction.cc
diff --git a/src/tint/lang/core/ir/BUILD.gn b/src/tint/lang/core/ir/BUILD.gn
index 0e76537..9bcb139 100644
--- a/src/tint/lang/core/ir/BUILD.gn
+++ b/src/tint/lang/core/ir/BUILD.gn
@@ -92,6 +92,7 @@
     "function.h",
     "function_param.cc",
     "function_param.h",
+    "ice.h",
     "if.cc",
     "if.h",
     "instruction.cc",
diff --git a/src/tint/lang/core/ir/ice.h b/src/tint/lang/core/ir/ice.h
new file mode 100644
index 0000000..40ef1dd
--- /dev/null
+++ b/src/tint/lang/core/ir/ice.h
@@ -0,0 +1,36 @@
+// Copyright 2023 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// 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_CORE_IR_ICE_H_
+#define SRC_TINT_LANG_CORE_IR_ICE_H_
+
+#include "src/tint/lang/core/ir/disassembler.h"
+
+/// Emit an ICE message with the disassembly of `mod` attached.
+#define TINT_IR_ICE(mod) TINT_ICE() << tint::core::ir::Disassembler{mod}.Disassemble() << "\n"
+
+#endif  // SRC_TINT_LANG_CORE_IR_ICE_H_
diff --git a/src/tint/lang/msl/writer/printer/printer.cc b/src/tint/lang/msl/writer/printer/printer.cc
index 8ac6017..c003776 100644
--- a/src/tint/lang/msl/writer/printer/printer.cc
+++ b/src/tint/lang/msl/writer/printer/printer.cc
@@ -38,6 +38,7 @@
 #include "src/tint/lang/core/ir/constant.h"
 #include "src/tint/lang/core/ir/discard.h"
 #include "src/tint/lang/core/ir/exit_if.h"
+#include "src/tint/lang/core/ir/ice.h"
 #include "src/tint/lang/core/ir/if.h"
 #include "src/tint/lang/core/ir/let.h"
 #include "src/tint/lang/core/ir/load.h"
@@ -332,7 +333,7 @@
                 out << "threadgroup ";
                 break;
             default:
-                TINT_ICE() << "unhandled variable address space";
+                TINT_IR_ICE(ir_) << "unhandled variable address space";
                 return;
         }
 
@@ -451,7 +452,7 @@
                 out << "constant";
                 break;
             default:
-                TINT_ICE() << "unhandled address space: " << sc;
+                TINT_IR_ICE(ir_) << "unhandled address space: " << sc;
                 break;
         }
     }
@@ -524,7 +525,7 @@
         } else {
             auto count = arr->ConstantCount();
             if (!count) {
-                TINT_ICE() << core::type::Array::kErrExpectedConstantCount;
+                TINT_IR_ICE(ir_) << core::type::Array::kErrExpectedConstantCount;
                 return;
             }
             out << count.value();
@@ -556,7 +557,7 @@
     /// @param tex the texture to emit
     void EmitTextureType(StringStream& out, const core::type::Texture* tex) {
         if (TINT_UNLIKELY(tex->Is<core::type::ExternalTexture>())) {
-            TINT_ICE() << "Multiplanar external texture transform was not run.";
+            TINT_IR_ICE(ir_) << "Multiplanar external texture transform was not run.";
             return;
         }
 
@@ -586,7 +587,7 @@
                 out << "cube_array";
                 break;
             default:
-                TINT_ICE() << "invalid texture dimensions";
+                TINT_IR_ICE(ir_) << "invalid texture dimensions";
                 return;
         }
         if (tex->IsAnyOf<core::type::MultisampledTexture, core::type::DepthMultisampledTexture>()) {
@@ -609,7 +610,7 @@
                 } else if (storage->access() == core::Access::kWrite) {
                     out << "access::write";
                 } else {
-                    TINT_ICE() << "invalid access control for storage texture";
+                    TINT_IR_ICE(ir_) << "invalid access control for storage texture";
                     return;
                 }
             },
@@ -671,8 +672,8 @@
             if (is_host_shareable) {
                 if (TINT_UNLIKELY(ir_offset < msl_offset)) {
                     // Unimplementable layout
-                    TINT_ICE() << "Structure member offset (" << ir_offset
-                               << ") is behind MSL offset (" << msl_offset << ")";
+                    TINT_IR_ICE(ir_) << "Structure member offset (" << ir_offset
+                                     << ") is behind MSL offset (" << msl_offset << ")";
                     return;
                 }
 
@@ -696,7 +697,7 @@
             if (auto builtin = attributes.builtin) {
                 auto name = BuiltinToAttribute(builtin.value());
                 if (name.empty()) {
-                    TINT_ICE() << "unknown builtin";
+                    TINT_IR_ICE(ir_) << "unknown builtin";
                     return;
                 }
                 out << " [[" << name << "]]";
@@ -705,7 +706,7 @@
             if (auto location = attributes.location) {
                 auto& pipeline_stage_uses = str->PipelineStageUses();
                 if (TINT_UNLIKELY(pipeline_stage_uses.size() != 1)) {
-                    TINT_ICE() << "invalid entry point IO struct uses";
+                    TINT_IR_ICE(ir_) << "invalid entry point IO struct uses";
                     return;
                 }
 
@@ -721,7 +722,7 @@
                                core::type::PipelineStageUsage::kFragmentOutput))) {
                     out << " [[color(" + std::to_string(location.value()) + ")]]";
                 } else {
-                    TINT_ICE() << "invalid use of location decoration";
+                    TINT_IR_ICE(ir_) << "invalid use of location decoration";
                     return;
                 }
             }
@@ -729,7 +730,7 @@
             if (auto interpolation = attributes.interpolation) {
                 auto name = InterpolationToAttribute(interpolation->type, interpolation->sampling);
                 if (name.empty()) {
-                    TINT_ICE() << "unknown interpolation attribute";
+                    TINT_IR_ICE(ir_) << "unknown interpolation attribute";
                     return;
                 }
                 out << " [[" << name << "]]";
@@ -746,9 +747,9 @@
                 // Calculate new MSL offset
                 auto size_align = MslPackedTypeSizeAndAlign(ty);
                 if (TINT_UNLIKELY(msl_offset % size_align.align)) {
-                    TINT_ICE() << "Misaligned MSL structure member " << mem_name << " : "
-                               << ty->FriendlyName() << " offset: " << msl_offset
-                               << " align: " << size_align.align;
+                    TINT_IR_ICE(ir_) << "Misaligned MSL structure member " << mem_name << " : "
+                                     << ty->FriendlyName() << " offset: " << msl_offset
+                                     << " align: " << size_align.align;
                     return;
                 }
                 msl_offset += size_align.size;
@@ -816,7 +817,7 @@
 
                 auto count = a->ConstantCount();
                 if (!count) {
-                    TINT_ICE() << core::type::Array::kErrExpectedConstantCount;
+                    TINT_IR_ICE(ir_) << core::type::Array::kErrExpectedConstantCount;
                     return;
                 }
                 emit_values(*count);
@@ -903,8 +904,8 @@
             [&](Default) -> ExprAndPtrKind {
                 auto lookup = bindings_.Find(value);
                 if (TINT_UNLIKELY(!lookup)) {
-                    TINT_ICE() << "Expr(" << (value ? value->TypeInfo().name : "null")
-                               << ") value has no expression";
+                    TINT_IR_ICE(ir_) << "Expr(" << (value ? value->TypeInfo().name : "null")
+                                     << ") value has no expression";
                     return {};
                 }
 
@@ -926,10 +927,10 @@
                         }
 
                         if constexpr (std::is_same_v<T, ConsumedValue>) {
-                            TINT_ICE() << "Expr(" << value->TypeInfo().name
-                                       << ") called twice on the same value";
+                            TINT_IR_ICE(ir_) << "Expr(" << value->TypeInfo().name
+                                             << ") called twice on the same value";
                         } else {
-                            TINT_ICE()
+                            TINT_IR_ICE(ir_)
                                 << "Expr(" << value->TypeInfo().name << ") has unhandled value";
                         }
                         return {};
@@ -999,7 +1000,7 @@
             return;
         }
 
-        TINT_ICE() << "Bind(" << value->TypeInfo().name << ") called twice for same value";
+        TINT_IR_ICE(ir_) << "Bind(" << value->TypeInfo().name << ") called twice for same value";
     }
 
     /// Associates an IR value the 'var', 'let' or parameter of the given name
@@ -1011,7 +1012,8 @@
 
         bool added = bindings_.Add(value, VariableValue{name, ptr_kind});
         if (TINT_UNLIKELY(!added)) {
-            TINT_ICE() << "Bind(" << value->TypeInfo().name << ") called twice for same value";
+            TINT_IR_ICE(ir_) << "Bind(" << value->TypeInfo().name
+                             << ") called twice for same value";
         }
     }