[msl] Emit TINT_INVARIANT macro

Set the has_invariant_attribute field of the MSL writer result.

Bug: 42251016
Change-Id: If5e48a217b98e2d005f2933bcdd9ac82f89b4368
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/196357
Reviewed-by: dan sinclair <dsinclair@chromium.org>
diff --git a/src/tint/lang/msl/writer/printer/printer.cc b/src/tint/lang/msl/writer/printer/printer.cc
index 9d45b9b..3a232f5 100644
--- a/src/tint/lang/msl/writer/printer/printer.cc
+++ b/src/tint/lang/msl/writer/printer/printer.cc
@@ -1379,7 +1379,22 @@
             }
 
             if (attributes.invariant) {
-                invariant_define_name_ = UniqueIdentifier("TINT_INVARIANT");
+                if (invariant_define_name_.empty()) {
+                    invariant_define_name_ = UniqueIdentifier("TINT_INVARIANT");
+                    result_.has_invariant_attribute = true;
+
+                    // 'invariant' attribute requires MSL 2.1 or higher.
+                    // WGSL can ignore the invariant attribute on pre MSL 2.1 devices.
+                    // See: https://github.com/gpuweb/gpuweb/issues/893#issuecomment-745537465
+                    Line(&preamble_buffer_);
+                    Line(&preamble_buffer_) << "#if __METAL_VERSION__ >= 210";
+                    Line(&preamble_buffer_)
+                        << "#define " << invariant_define_name_ << " [[invariant]]";
+                    Line(&preamble_buffer_) << "#else";
+                    Line(&preamble_buffer_) << "#define " << invariant_define_name_;
+                    Line(&preamble_buffer_) << "#endif";
+                    Line(&preamble_buffer_);
+                }
                 out << " " << invariant_define_name_;
             }
 
diff --git a/src/tint/lang/msl/writer/printer/printer.h b/src/tint/lang/msl/writer/printer/printer.h
index bba5c8c..0448671 100644
--- a/src/tint/lang/msl/writer/printer/printer.h
+++ b/src/tint/lang/msl/writer/printer/printer.h
@@ -59,6 +59,9 @@
     /// The generated MSL.
     std::string msl = "";
 
+    /// `true` if an invariant attribute was generated.
+    bool has_invariant_attribute = false;
+
     /// A map from entry point name to a list of dynamic workgroup allocations.
     /// Each element of the vector is the size of the workgroup allocation that should be created
     /// for that index.
diff --git a/src/tint/lang/msl/writer/writer.cc b/src/tint/lang/msl/writer/writer.cc
index e9ae23c..6d05098 100644
--- a/src/tint/lang/msl/writer/writer.cc
+++ b/src/tint/lang/msl/writer/writer.cc
@@ -61,7 +61,7 @@
     output.msl = result->msl;
     output.workgroup_allocations = std::move(result->workgroup_allocations);
     output.needs_storage_buffer_sizes = raise_result->needs_storage_buffer_sizes;
-    // TODO(crbug.com/42251016): Set has_invariant.
+    output.has_invariant_attribute = result->has_invariant_attribute;
     // TODO(crbug.com/42251016): Set used_array_length_from_uniform_indices.
     return output;
 }
diff --git a/test/tint/types/functions/shader_io/invariant.wgsl.expected.ir.msl b/test/tint/types/functions/shader_io/invariant.wgsl.expected.ir.msl
index c9652ff..bc3ee97 100644
--- a/test/tint/types/functions/shader_io/invariant.wgsl.expected.ir.msl
+++ b/test/tint/types/functions/shader_io/invariant.wgsl.expected.ir.msl
@@ -1,8 +1,13 @@
-SKIP: FAILED
-
 #include <metal_stdlib>
 using namespace metal;
 
+#if __METAL_VERSION__ >= 210
+#define TINT_INVARIANT [[invariant]]
+#else
+#define TINT_INVARIANT
+#endif
+
+
 struct tint_symbol_outputs {
   float4 tint_symbol_1 [[position]] TINT_INVARIANT;
 };
@@ -14,8 +19,3 @@
 vertex tint_symbol_outputs tint_symbol() {
   return tint_symbol_outputs{.tint_symbol_1=tint_symbol_inner()};
 }
-program_source:5:36: error: expected ';' at end of declaration list
-  float4 tint_symbol_1 [[position]] TINT_INVARIANT;
-                                   ^
-                                   ;
-
diff --git a/test/tint/types/functions/shader_io/invariant_struct_member.wgsl.expected.ir.msl b/test/tint/types/functions/shader_io/invariant_struct_member.wgsl.expected.ir.msl
index e7922f6..021eccc 100644
--- a/test/tint/types/functions/shader_io/invariant_struct_member.wgsl.expected.ir.msl
+++ b/test/tint/types/functions/shader_io/invariant_struct_member.wgsl.expected.ir.msl
@@ -1,5 +1,3 @@
-SKIP: FAILED
-
 #include <metal_stdlib>
 using namespace metal;
 
@@ -7,6 +5,13 @@
   float4 pos;
 };
 
+#if __METAL_VERSION__ >= 210
+#define TINT_INVARIANT [[invariant]]
+#else
+#define TINT_INVARIANT
+#endif
+
+
 struct tint_symbol_outputs {
   float4 Out_pos [[position]] TINT_INVARIANT;
 };
@@ -18,8 +23,3 @@
 vertex tint_symbol_outputs tint_symbol() {
   return tint_symbol_outputs{.Out_pos=tint_symbol_inner().pos};
 }
-program_source:9:30: error: expected ';' at end of declaration list
-  float4 Out_pos [[position]] TINT_INVARIANT;
-                             ^
-                             ;
-