writer/msl: Only use [[invariant]] on MSL 2.1+

This is not supported on older versions.
This behavior was agreed by the WGSL core group:
https://github.com/gpuweb/gpuweb/issues/893#issuecomment-745537465

Bug: tint:1327
Change-Id: I1a0459068c438e625ae9d99fe4a044a96f2db57e
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/71684
Reviewed-by: David Neto <dneto@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/writer/msl/generator_impl.cc b/src/writer/msl/generator_impl.cc
index ddb3f17..4982639 100644
--- a/src/writer/msl/generator_impl.cc
+++ b/src/writer/msl/generator_impl.cc
@@ -240,6 +240,18 @@
     line();
   }
 
+  if (!invariant_define_name_.empty()) {
+    // '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(&helpers_) << "#if __METAL_VERSION__ >= 210";
+    line(&helpers_) << "#define " << invariant_define_name_ << " [[invariant]]";
+    line(&helpers_) << "#else";
+    line(&helpers_) << "#define " << invariant_define_name_;
+    line(&helpers_) << "#endif";
+    line(&helpers_);
+  }
+
   if (!helpers_.lines.empty()) {
     current_buffer_->Insert("", helpers_insertion_point++, 0);
     current_buffer_->Insert(helpers_, helpers_insertion_point++, 0);
@@ -2504,8 +2516,10 @@
           }
           out << " [[" << attr << "]]";
         } else if (deco->Is<ast::InvariantDecoration>()) {
-          out << " [[invariant]]";
-          has_invariant_ = true;
+          if (invariant_define_name_.empty()) {
+            invariant_define_name_ = UniqueIdentifier("TINT_INVARIANT");
+          }
+          out << " " << invariant_define_name_;
         } else if (!deco->IsAnyOf<ast::StructMemberOffsetDecoration,
                                   ast::StructMemberAlignDecoration,
                                   ast::StructMemberSizeDecoration>()) {
diff --git a/src/writer/msl/generator_impl.h b/src/writer/msl/generator_impl.h
index 710b8e2..da99716 100644
--- a/src/writer/msl/generator_impl.h
+++ b/src/writer/msl/generator_impl.h
@@ -98,7 +98,7 @@
   bool Generate();
 
   /// @returns true if an invariant attribute was generated
-  bool HasInvariant() { return has_invariant_; }
+  bool HasInvariant() { return !invariant_define_name_.empty(); }
 
   /// @returns a map from entry point to list of required workgroup allocations
   const std::unordered_map<std::string, std::vector<uint32_t>>&
@@ -410,8 +410,9 @@
   /// class.
   StorageClassToString atomicCompareExchangeWeak_;
 
-  /// True if an invariant attribute has been generated.
-  bool has_invariant_ = false;
+  /// Unique name of the 'TINT_INVARIANT' preprocessor define. Non-empty only if
+  /// an invariant attribute has been generated.
+  std::string invariant_define_name_;
 
   /// True if matrix-packed_vector operator overloads have been generated.
   bool matrix_packed_vector_overloads_ = false;
diff --git a/src/writer/msl/generator_impl_test.cc b/src/writer/msl/generator_impl_test.cc
index 121321c..8aa2891 100644
--- a/src/writer/msl/generator_impl_test.cc
+++ b/src/writer/msl/generator_impl_test.cc
@@ -95,8 +95,15 @@
   EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
 
 using namespace metal;
+
+#if __METAL_VERSION__ >= 210
+#define TINT_INVARIANT [[invariant]]
+#else
+#define TINT_INVARIANT
+#endif
+
 struct Out {
-  float4 pos [[position]] [[invariant]];
+  float4 pos [[position]] TINT_INVARIANT;
 };
 
 vertex Out vert_main() {
diff --git a/test/shader_io/invariant.wgsl.expected.msl b/test/shader_io/invariant.wgsl.expected.msl
index c9eb97e..5982216 100644
--- a/test/shader_io/invariant.wgsl.expected.msl
+++ b/test/shader_io/invariant.wgsl.expected.msl
@@ -1,8 +1,15 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+#if __METAL_VERSION__ >= 210
+#define TINT_INVARIANT [[invariant]]
+#else
+#define TINT_INVARIANT
+#endif
+
 struct tint_symbol_1 {
-  float4 value [[position]] [[invariant]];
+  float4 value [[position]] TINT_INVARIANT;
 };
 
 float4 tint_symbol_inner() {
diff --git a/test/shader_io/invariant_struct_member.wgsl.expected.msl b/test/shader_io/invariant_struct_member.wgsl.expected.msl
index a2c44a7..c85f9a1 100644
--- a/test/shader_io/invariant_struct_member.wgsl.expected.msl
+++ b/test/shader_io/invariant_struct_member.wgsl.expected.msl
@@ -1,11 +1,18 @@
 #include <metal_stdlib>
 
 using namespace metal;
+
+#if __METAL_VERSION__ >= 210
+#define TINT_INVARIANT [[invariant]]
+#else
+#define TINT_INVARIANT
+#endif
+
 struct Out {
   float4 pos;
 };
 struct tint_symbol_1 {
-  float4 pos [[position]] [[invariant]];
+  float4 pos [[position]] TINT_INVARIANT;
 };
 
 Out tint_symbol_inner() {