Import Tint changes from Dawn

Changes:
  - 26dc5f6989a35758ee466e8b2df5cebc983058ab msl ast_writer: Prevent emitted loops from erasure during... by David Neto <dneto@google.com>
  - 2be279ee795b144ba43171c943c09707eda4480e Fix the comparator used to sort output attributes by David Neto <dneto@google.com>
  - ece88f74027f2554bc6c98d0194eca9f4de4eb46 spir-v ast writer: Fix order of dpdx and dpdy operands by David Neto <dneto@google.com>
GitOrigin-RevId: 26dc5f6989a35758ee466e8b2df5cebc983058ab
Change-Id: Iea9f5e89f2f48cf98db233add45846845aa6b03c
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/166980
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: Copybara Prod <copybara-worker@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/lang/msl/writer/ast_printer/ast_printer.cc b/src/tint/lang/msl/writer/ast_printer/ast_printer.cc
index 8787d09..4f90706 100644
--- a/src/tint/lang/msl/writer/ast_printer/ast_printer.cc
+++ b/src/tint/lang/msl/writer/ast_printer/ast_printer.cc
@@ -2123,6 +2123,7 @@
 
     TINT_SCOPED_ASSIGNMENT(emit_continuing_, emit_continuing);
     Line() << "while (true) {";
+    EmitLoopPreserver();
     {
         ScopedIndent si(this);
         if (!EmitStatements(stmt->body->statements)) {
@@ -2193,6 +2194,7 @@
 
         TINT_SCOPED_ASSIGNMENT(emit_continuing_, emit_continuing);
         Line() << "while (true) {";
+        EmitLoopPreserver();
         IncrementIndent();
         TINT_DEFER({
             DecrementIndent();
@@ -2233,6 +2235,7 @@
             }
             out << " {";
         }
+        EmitLoopPreserver();
         {
             auto emit_continuing = [] { return true; };
             TINT_SCOPED_ASSIGNMENT(emit_continuing_, emit_continuing);
@@ -2266,6 +2269,7 @@
     bool emit_as_loop = cond_pre.lines.size() > 0;
     if (emit_as_loop) {
         Line() << "while (true) {";
+        EmitLoopPreserver();
         IncrementIndent();
         TINT_DEFER({
             DecrementIndent();
@@ -2288,6 +2292,7 @@
             }
             out << " {";
         }
+        EmitLoopPreserver();
         if (!EmitStatementsWithIndent(stmt->body->statements)) {
             return false;
         }
@@ -3026,6 +3031,14 @@
     return true;
 }
 
+void ASTPrinter::EmitLoopPreserver() {
+    IncrementIndent();
+    // This statement prevents the MSL compiler from erasing a loop during
+    // optimimzations.
+    Line() << R"(__asm__("");)";
+    DecrementIndent();
+}
+
 template <typename F>
 bool ASTPrinter::CallBuiltinHelper(StringStream& out,
                                    const ast::CallExpression* call,
diff --git a/src/tint/lang/msl/writer/ast_printer/ast_printer.h b/src/tint/lang/msl/writer/ast_printer/ast_printer.h
index 9c9ba7c..af251db 100644
--- a/src/tint/lang/msl/writer/ast_printer/ast_printer.h
+++ b/src/tint/lang/msl/writer/ast_printer/ast_printer.h
@@ -378,6 +378,10 @@
                               const ast::CallExpression* expr,
                               const sem::BuiltinFn* builtin);
 
+    /// Emits a code sequence that preserves a loop during
+    /// optimizations even if the loop is infinite.
+    void EmitLoopPreserver();
+
     /// Handles generating a builtin name
     /// @param builtin the semantic info for the builtin
     /// @returns the name or "" if not valid
diff --git a/src/tint/lang/msl/writer/ast_printer/ast_printer_test.cc b/src/tint/lang/msl/writer/ast_printer/ast_printer_test.cc
index b62df62..f7b869e 100644
--- a/src/tint/lang/msl/writer/ast_printer/ast_printer_test.cc
+++ b/src/tint/lang/msl/writer/ast_printer/ast_printer_test.cc
@@ -232,6 +232,7 @@
 
 void comp_main_inner(uint local_invocation_index, threadgroup tint_array<float2x2, 4>* const tint_symbol) {
   for(uint idx = local_invocation_index; (idx < 4u); idx = (idx + 1u)) {
+    __asm__("");
     uint const i = idx;
     (*(tint_symbol))[i] = float2x2(float2(0.0f), float2(0.0f));
   }
diff --git a/src/tint/lang/msl/writer/ast_printer/continue_test.cc b/src/tint/lang/msl/writer/ast_printer/continue_test.cc
index 508c69d..a79ddc3 100644
--- a/src/tint/lang/msl/writer/ast_printer/continue_test.cc
+++ b/src/tint/lang/msl/writer/ast_printer/continue_test.cc
@@ -43,6 +43,7 @@
 
     ASSERT_TRUE(gen.EmitStatement(loop)) << gen.Diagnostics();
     EXPECT_EQ(gen.Result(), R"(  while (true) {
+    __asm__("");
     if (false) {
       break;
     }
diff --git a/src/tint/lang/msl/writer/ast_printer/loop_test.cc b/src/tint/lang/msl/writer/ast_printer/loop_test.cc
index f4a6662..450592b 100644
--- a/src/tint/lang/msl/writer/ast_printer/loop_test.cc
+++ b/src/tint/lang/msl/writer/ast_printer/loop_test.cc
@@ -50,6 +50,7 @@
 
     ASSERT_TRUE(gen.EmitStatement(l)) << gen.Diagnostics();
     EXPECT_EQ(gen.Result(), R"(  while (true) {
+    __asm__("");
     break;
   }
 )");
@@ -70,6 +71,7 @@
 
     ASSERT_TRUE(gen.EmitStatement(l)) << gen.Diagnostics();
     EXPECT_EQ(gen.Result(), R"(  while (true) {
+    __asm__("");
     break;
     {
       a_statement();
@@ -93,6 +95,7 @@
 
     ASSERT_TRUE(gen.EmitStatement(l)) << gen.Diagnostics();
     EXPECT_EQ(gen.Result(), R"(  while (true) {
+    __asm__("");
     break;
     {
       a_statement();
@@ -126,7 +129,9 @@
 
     ASSERT_TRUE(gen.EmitStatement(outer)) << gen.Diagnostics();
     EXPECT_EQ(gen.Result(), R"(  while (true) {
+    __asm__("");
     while (true) {
+      __asm__("");
       break;
       {
         a_statement();
@@ -166,6 +171,7 @@
 
     ASSERT_TRUE(gen.EmitStatement(outer)) << gen.Diagnostics();
     EXPECT_EQ(gen.Result(), R"(  while (true) {
+    __asm__("");
     float lhs = 2.5f;
     float other = 0.0f;
     break;
@@ -191,6 +197,7 @@
 
     ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
     EXPECT_EQ(gen.Result(), R"(  for(; ; ) {
+    __asm__("");
     return;
   }
 )");
@@ -211,6 +218,7 @@
 
     ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
     EXPECT_EQ(gen.Result(), R"(  for(int i = 0; ; ) {
+    __asm__("");
     return;
   }
 )");
@@ -244,6 +252,7 @@
       f(2);
     }
     for(; ; ) {
+      __asm__("");
       return;
     }
   }
@@ -265,6 +274,7 @@
 
     ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
     EXPECT_EQ(gen.Result(), R"(  for(; true; ) {
+    __asm__("");
     return;
   }
 )");
@@ -287,6 +297,7 @@
     ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
     EXPECT_EQ(gen.Result(),
               R"(  for(; ; i = as_type<int>((as_type<uint>(i) + as_type<uint>(1)))) {
+    __asm__("");
     return;
   }
 )");
@@ -315,6 +326,7 @@
 
     ASSERT_TRUE(gen.EmitStatement(loop)) << gen.Diagnostics();
     EXPECT_EQ(gen.Result(), R"(  while (true) {
+    __asm__("");
     return;
     {
       f(1);
@@ -342,6 +354,7 @@
     ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
     EXPECT_EQ(gen.Result(),
               R"(  for(int i = 0; true; i = as_type<int>((as_type<uint>(i) + as_type<uint>(1)))) {
+    __asm__("");
     a_statement();
   }
 )");
@@ -376,6 +389,7 @@
       f(2);
     }
     while (true) {
+      __asm__("");
       if (!(true)) { break; }
       return;
       {
@@ -401,6 +415,7 @@
 
     ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
     EXPECT_EQ(gen.Result(), R"(  while(true) {
+    __asm__("");
     return;
   }
 )");
@@ -420,6 +435,7 @@
 
     ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
     EXPECT_EQ(gen.Result(), R"(  while(true) {
+    __asm__("");
     continue;
   }
 )");
@@ -442,6 +458,7 @@
 
     ASSERT_TRUE(gen.EmitStatement(f)) << gen.Diagnostics();
     EXPECT_EQ(gen.Result(), R"(  while((t && false)) {
+    __asm__("");
     return;
   }
 )");
diff --git a/src/tint/lang/spirv/writer/ast_printer/builder.cc b/src/tint/lang/spirv/writer/ast_printer/builder.cc
index 26c4d3a..19a5662 100644
--- a/src/tint/lang/spirv/writer/ast_printer/builder.cc
+++ b/src/tint/lang/spirv/writer/ast_printer/builder.cc
@@ -2997,8 +2997,10 @@
     }
 
     if (!image_operands.empty()) {
-        std::sort(image_operands.begin(), image_operands.end(),
-                  [](auto& a, auto& b) { return a.mask < b.mask; });
+        // Use a stable sort to preserve the order of the Grad dpdx and dpdy
+        // operands.
+        std::stable_sort(image_operands.begin(), image_operands.end(),
+                         [](auto& a, auto& b) { return a.mask < b.mask; });
         uint32_t mask = 0;
         for (auto& image_operand : image_operands) {
             mask |= image_operand.mask;
diff --git a/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io.cc b/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io.cc
index e0ec04a..9bfa463 100644
--- a/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io.cc
+++ b/src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io.cc
@@ -102,6 +102,7 @@
         default:
             break;
     }
+    TINT_UNREACHABLE();
     return 0;
 }
 
@@ -569,29 +570,26 @@
     /// @param y another struct member
     /// @returns true if a comes before b
     bool StructMemberComparator(const MemberInfo& x, const MemberInfo& y) {
-        if (x.color.has_value() && y.color.has_value()) {
+        if (x.color.has_value() && y.color.has_value() && x.color != y.color) {
             // Both have color attributes: smallest goes first.
             return x.color < y.color;
-        }
-        if (x.color.has_value() != y.color.has_value()) {
+        } else if (x.color.has_value() != y.color.has_value()) {
             // The member with the color goes first
             return x.color.has_value();
         }
 
-        if (x.location.has_value() && y.location.has_value()) {
+        if (x.location.has_value() && y.location.has_value() && x.location != y.location) {
             // Both have location attributes: smallest goes first.
             return x.location < y.location;
-        }
-        if (x.location.has_value() != y.location.has_value()) {
+        } else if (x.location.has_value() != y.location.has_value()) {
             // The member with the location goes first
             return x.location.has_value();
         }
 
-        if (x.index.has_value() && y.index.has_value()) {
+        if (x.index.has_value() && y.index.has_value() && x.index != y.index) {
             // Both have index attributes: smallest goes first.
             return x.index < y.index;
-        }
-        if (x.index.has_value() != y.index.has_value()) {
+        } else if (x.index.has_value() != y.index.has_value()) {
             // The member with the index goes first
             return x.index.has_value();
         }
@@ -603,15 +601,19 @@
                 // Both are builtins: order matters for FXC.
                 auto builtin_a = BuiltinOf(x_blt);
                 auto builtin_b = BuiltinOf(y_blt);
-                return BuiltinOrder(builtin_a) < BuiltinOrder(builtin_b);
-            }
-            if ((x_blt != nullptr) != (y_blt != nullptr)) {
+                auto order_a = BuiltinOrder(builtin_a);
+                auto order_b = BuiltinOrder(builtin_b);
+                if (order_a != order_b) {
+                    return order_a < order_b;
+                }
+            } else if ((x_blt != nullptr) != (y_blt != nullptr)) {
                 // The member with the builtin goes first
                 return x_blt != nullptr;
             }
         }
 
-        TINT_UNREACHABLE();
+        // Control flow reaches here if x is the same as y.
+        // Sort algorithms sometimes do that.
         return false;
     }