[ir][spirv-writer] Handle shader IO

Add a ShaderIO transform that will handle mutating shader IO for each
backend. The common functionality is implemented in the PIMPL State
class, while backend-specific parts are handled in a backend-specific
state objects defined in separate files.

This uses const_cast to directly remove attributes from existing
structure members, which will be cleaned up in the future.

Use this new transform in the SPIR-V backend, and add codegen for
variables in the `in` and `out` address spaces.

Bug: tint:1906
Change-Id: I78392a055d62f363e39d78f50d6efc4f65739a2f
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/137461
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/ir/builder.h b/src/tint/ir/builder.h
index edb5509..72482b6 100644
--- a/src/tint/ir/builder.h
+++ b/src/tint/ir/builder.h
@@ -574,6 +574,11 @@
     /// @returns the instruction
     template <typename ARG>
     ir::Return* Return(ir::Function* func, ARG&& value) {
+        if constexpr (std::is_same_v<std::decay_t<ARG>, ir::Value*>) {
+            if (value == nullptr) {
+                return Append(ir.instructions.Create<ir::Return>(func));
+            }
+        }
         return Append(ir.instructions.Create<ir::Return>(func, Value(std::forward<ARG>(value))));
     }