Canonicalize index-ordering of entry point attributes

This makes the output of entry point IO canonicalization deterministic.
This fixes unit test failures in some environments.

Change-Id: I013f147bb00515b2f87eb952c00f957b1a0a4261
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/165941
Auto-Submit: David Neto <dneto@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: James Price <jrprice@google.com>
Commit-Queue: James Price <jrprice@google.com>
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 1408dcc..ad22d6c 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
@@ -552,7 +552,8 @@
 
     /// Comparison function used to reorder struct members such that all members with
     /// color attributes appear first (ordered by color slot), then location attributes (ordered by
-    /// location slot), followed by those with builtin attributes (ordered by BuiltinOrder).
+    /// location slot), then index attributes (ordered by index slot), followed by those with
+    /// builtin attributes (ordered by BuiltinOrder).
     /// @param x a struct member
     /// @param y another struct member
     /// @returns true if a comes before b
@@ -575,6 +576,15 @@
             return x.location.has_value();
         }
 
+        if (x.index.has_value() && y.index.has_value()) {
+            // Both have index attributes: smallest goes first.
+            return x.index < y.index;
+        }
+        if (x.index.has_value() != y.index.has_value()) {
+            // The member with the index goes first
+            return x.index.has_value();
+        }
+
         {
             auto* x_blt = GetAttribute<BuiltinAttribute>(x.member->attributes);
             auto* y_blt = GetAttribute<BuiltinAttribute>(y.member->attributes);
@@ -635,8 +645,8 @@
             wrapper_struct_output_members.Push({
                 /* member */ b.Member(name, outval.type, std::move(outval.attributes)),
                 /* location */ outval.location,
+                /* index */ outval.index,
                 /* color */ std::nullopt,
-                /* index */ std::nullopt,
             });
             assignments.Push(b.Assign(b.MemberAccessor(wrapper_result, name), outval.value));
         }