Import Tint changes from Dawn

Changes:
  - daca5564356a92bb0981c17833c97502b26241e7 [tint][resolver]: Require @blend_src on all or none of th... by Corentin Wallez <cwallez@chromium.org>
  - b57b92d4bf6a402f874ae64149892a934c6834b6 Enable dp4a: set to shipped-with-kill-switch by David Neto <dneto@google.com>
  - eb5304ca62ed17e2dd473c4f4ffdc24fae16388e Fix missing member in reflection of tint::spirv::writer::... by Corentin Wallez <cwallez@chromium.org>
  - 0a49383188b7bb1baa7f0c932070ce2130711553 [tint][utils] Optimize ScopeStack by Ben Clayton <bclayton@google.com>
GitOrigin-RevId: daca5564356a92bb0981c17833c97502b26241e7
Change-Id: Ic291b1670c2d3a676eeecc1096d19c57a288fd67
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/172328
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/lang/spirv/writer/common/options.h b/src/tint/lang/spirv/writer/common/options.h
index 79ec0e7..494369c 100644
--- a/src/tint/lang/spirv/writer/common/options.h
+++ b/src/tint/lang/spirv/writer/common/options.h
@@ -160,6 +160,7 @@
                  use_zero_initialize_workgroup_memory_extension,
                  emit_vertex_point_size,
                  clamp_frag_depth,
+                 pass_matrix_by_pointer,
                  experimental_require_subgroup_uniform_control_flow,
                  polyfill_dot_4x8_packed,
                  disable_polyfill_integer_div_mod,
diff --git a/src/tint/lang/wgsl/features/status.cc b/src/tint/lang/wgsl/features/status.cc
index 334c1b6..2f592a1 100644
--- a/src/tint/lang/wgsl/features/status.cc
+++ b/src/tint/lang/wgsl/features/status.cc
@@ -36,7 +36,6 @@
             ////////////////////////////////////////////////////////////////////
             // Experimental features
             ///////////////////////////////////////////////////////////////////
-        case LanguageFeature::kPacked4X8IntegerDotProduct:
         case LanguageFeature::kPointerCompositeAccess:
         case LanguageFeature::kReadonlyAndReadwriteStorageTextures:
             return FeatureStatus::kExperimental;
@@ -44,6 +43,7 @@
             ////////////////////////////////////////////////////////////////////
             // Enabled features
             ////////////////////////////////////////////////////////////////////
+        case LanguageFeature::kPacked4X8IntegerDotProduct:
         case LanguageFeature::kUnrestrictedPointerParameters:
             return FeatureStatus::kShippedWithKillswitch;
 
diff --git a/src/tint/lang/wgsl/resolver/dual_source_blending_extension_test.cc b/src/tint/lang/wgsl/resolver/dual_source_blending_extension_test.cc
index 67a3aab..ab8677e 100644
--- a/src/tint/lang/wgsl/resolver/dual_source_blending_extension_test.cc
+++ b/src/tint/lang/wgsl/resolver/dual_source_blending_extension_test.cc
@@ -215,6 +215,49 @@
     EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
 
+TEST_F(DualSourceBlendingExtensionTests, MixedBlendSrcAndNonBlendSrcOnLocationZero_Struct) {
+    // struct S {
+    //   @location(0) a : vec4<f32>,
+    //   @location(0) @blend_src(1) b : vec4<f32>,
+    // };
+    Structure("S", Vector{
+                       Member("a", ty.vec4<f32>(),
+                              Vector{
+                                  Location(Source{{12, 34}}, 0_a),
+                              }),
+                       Member("b", ty.vec4<f32>(),
+                              Vector{Location(0_a), BlendSrc(Source{{56, 78}}, 1_a)}),
+                   });
+
+    EXPECT_TRUE(r()->Resolve());
+}
+
+TEST_F(DualSourceBlendingExtensionTests,
+       MixedBlendSrcAndNonBlendSrcOnLocationZero_StructUsedInEntryPoint) {
+    // struct S {
+    //   @location(0) a : vec4<f32>,
+    //   @location(0) @blend_src(1) b : vec4<f32>,
+    // };
+    // fn F() -> S { return S(); }
+    Structure("S", Vector{
+                       Member("a", ty.vec4<f32>(),
+                              Vector{
+                                  Location(Source{{12, 34}}, 0_a),
+                              }),
+                       Member("b", ty.vec4<f32>(),
+                              Vector{Location(0_a), BlendSrc(Source{{56, 78}}, 1_a)}),
+                   });
+    Func("F", Empty, ty("S"), Vector{Return(Call("S"))},
+         Vector{Stage(ast::PipelineStage::kFragment)});
+
+    EXPECT_FALSE(r()->Resolve());
+    EXPECT_EQ(
+        r()->error(),
+        R"(12:34 error: use of @blend_src requires all the output @location attributes of the entry point to be paired with a @blend_src attribute
+56:78 note: use of @blend_src here
+note: while analyzing entry point 'F')");
+}
+
 class DualSourceBlendingExtensionTestWithParams : public ResolverTestWithParam<int> {
   public:
     DualSourceBlendingExtensionTestWithParams() {
@@ -224,7 +267,7 @@
 
 // Rendering to multiple render targets while using dual source blending should fail.
 TEST_P(DualSourceBlendingExtensionTestWithParams,
-       MultipleRenderTargetsNotAllowed_BlendSrcThenNonZeroLocation) {
+       MultipleRenderTargetsNotAllowed_BlendSrcAndNoBlendSrc) {
     // struct S {
     //   @location(0) @blend_src(0) a : vec4<f32>,
     //   @location(0) @blend_src(1) b : vec4<f32>,
@@ -241,9 +284,10 @@
          Vector{Stage(ast::PipelineStage::kFragment)});
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              R"(1:2 error: pipeline cannot use both non-zero @blend_src and non-zero @location
-3:4 note: non-zero @location declared here
+    EXPECT_EQ(
+        r()->error(),
+        R"(3:4 error: use of @blend_src requires all the output @location attributes of the entry point to be paired with a @blend_src attribute
+1:2 note: use of @blend_src here
 note: while analyzing entry point 'F')");
 }
 
@@ -265,9 +309,10 @@
          Vector{Stage(ast::PipelineStage::kFragment)});
 
     EXPECT_FALSE(r()->Resolve());
-    EXPECT_EQ(r()->error(),
-              R"(3:4 error: pipeline cannot use both non-zero @blend_src and non-zero @location
-1:2 note: non-zero @location declared here
+    EXPECT_EQ(
+        r()->error(),
+        R"(1:2 error: use of @blend_src requires all the output @location attributes of the entry point to be paired with a @blend_src attribute
+note: use of @blend_src here
 5:6 note: while analyzing entry point 'F')");
 }
 
diff --git a/src/tint/lang/wgsl/resolver/validator.cc b/src/tint/lang/wgsl/resolver/validator.cc
index 907a3c4..0201bb4 100644
--- a/src/tint/lang/wgsl/resolver/validator.cc
+++ b/src/tint/lang/wgsl/resolver/validator.cc
@@ -1149,7 +1149,8 @@
     Hashset<core::BuiltinValue, 4> builtins;
     Hashset<std::pair<uint32_t, uint32_t>, 8> locations_and_blend_srcs;
     const ast::LocationAttribute* first_nonzero_location = nullptr;
-    const ast::BlendSrcAttribute* first_nonzero_blend_src = nullptr;
+    const ast::BlendSrcAttribute* first_blend_src = nullptr;
+    const ast::LocationAttribute* first_location_without_blend_src = nullptr;
     Hashset<uint32_t, 4> colors;
     enum class ParamOrRetType {
         kParameter,
@@ -1311,16 +1312,28 @@
                 }
             }
 
+            if (blend_src_attribute) {
+                first_blend_src = blend_src_attribute;
+            } else if (location_attribute) {
+                first_location_without_blend_src = location_attribute;
+            }
+
+            if (first_blend_src && first_location_without_blend_src) {
+                AddError(
+                    "use of @blend_src requires all the output @location attributes of the entry "
+                    "point to be paired with a @blend_src attribute",
+                    first_location_without_blend_src->source);
+                AddNote("use of @blend_src here", first_blend_src->source);
+                return false;
+            }
+
             if (location_attribute) {
                 if (!first_nonzero_location && location > 0u) {
                     first_nonzero_location = location_attribute;
                 }
-                if (!first_nonzero_blend_src && blend_src > 0u) {
-                    first_nonzero_blend_src = blend_src_attribute;
-                }
-                if (first_nonzero_location && first_nonzero_blend_src) {
-                    AddError("pipeline cannot use both non-zero @blend_src and non-zero @location",
-                             first_nonzero_blend_src->source);
+                if (first_nonzero_location && first_blend_src) {
+                    AddError("pipeline cannot use both a @blend_src and non-zero @location",
+                             first_blend_src->source);
                     AddNote("non-zero @location declared here", first_nonzero_location->source);
                     return false;
                 }
@@ -1416,7 +1429,8 @@
     builtins.Clear();
     locations_and_blend_srcs.Clear();
     first_nonzero_location = nullptr;
-    first_nonzero_blend_src = nullptr;
+    first_blend_src = nullptr;
+    first_location_without_blend_src = nullptr;
 
     if (!func->ReturnType()->Is<core::type::Void>()) {
         if (!validate_entry_point_attributes(decl->return_type_attributes, func->ReturnType(),
@@ -2226,7 +2240,7 @@
         return false;
     }
 
-    Hashset<std::pair<uint32_t, uint32_t>, 8> locations_and_blend_srcs;
+    Hashset<std::pair<uint32_t, std::optional<uint32_t>>, 8> locations_and_blend_srcs;
     Hashset<uint32_t, 4> colors;
     for (auto* member : str->Members()) {
         if (auto* r = member->Type()->As<sem::Array>()) {
@@ -2330,17 +2344,16 @@
             return false;
         }
 
-        // Ensure all locations and index pairs are unique
+        // Ensure all locations and optional blend_src pairs are unique
         if (location_attribute) {
             uint32_t location = member->Attributes().location.value();
-            uint32_t blend_src = member->Attributes().blend_src.value_or(0);
+            std::optional<uint32_t> blend_src = member->Attributes().blend_src;
 
-            std::pair<uint32_t, uint32_t> location_and_blend_src(location, blend_src);
-            if (!locations_and_blend_srcs.Add(location_and_blend_src)) {
+            if (!locations_and_blend_srcs.Add(std::make_pair(location, blend_src))) {
                 StringStream err;
                 err << "@location(" << location << ") ";
-                if (blend_src_attribute) {
-                    err << "@blend_src(" << blend_src << ") ";
+                if (blend_src) {
+                    err << "@blend_src(" << blend_src.value() << ") ";
                 }
                 err << "appears multiple times";
                 AddError(err.str(), location_attribute->source);
diff --git a/src/tint/utils/containers/scope_stack.h b/src/tint/utils/containers/scope_stack.h
index 35a070e..e87bff5 100644
--- a/src/tint/utils/containers/scope_stack.h
+++ b/src/tint/utils/containers/scope_stack.h
@@ -31,6 +31,7 @@
 
 #include "src/tint/utils/containers/hashmap.h"
 #include "src/tint/utils/containers/vector.h"
+#include "src/tint/utils/macros/compiler.h"
 
 namespace tint {
 
@@ -39,13 +40,22 @@
 template <class K, class V>
 class ScopeStack {
   public:
+    ScopeStack() { Push(); }
+
     /// Push a new scope on to the stack
-    void Push() { stack_.Push({}); }
+    void Push() {
+        depth_++;
+        if (TINT_LIKELY(stack_.Length() >= depth_)) {
+            Top().Clear();
+        } else {
+            stack_.Push({});
+        }
+    }
 
     /// Pop the scope off the top of the stack
     void Pop() {
-        if (stack_.Length() > 1) {
-            stack_.Pop();
+        if (depth_ > 1) {
+            depth_--;
         }
     }
 
@@ -55,12 +65,11 @@
     /// @returns the old value if there was an existing key at the top of the
     /// stack, otherwise the zero initializer for type T.
     V Set(const K& key, V val) {
-        auto& back = stack_.Back();
-        if (auto el = back.Find(key)) {
+        if (auto el = Top().Find(key)) {
             std::swap(val, *el);
             return val;
         }
-        back.Add(key, val);
+        Top().Add(key, val);
         return {};
     }
 
@@ -68,8 +77,8 @@
     /// @param key the key to look for
     /// @returns the value, or the zero initializer if the value was not found
     V Get(const K& key) const {
-        for (auto iter = stack_.rbegin(); iter != stack_.rend(); ++iter) {
-            if (auto val = iter->Find(key)) {
+        for (size_t i = depth_; i > 0; i--) {
+            if (auto val = stack_[i - 1].Find(key)) {
                 return *val;
             }
         }
@@ -79,16 +88,24 @@
 
     /// Return the top scope of the stack.
     /// @returns the top scope of the stack
-    const Hashmap<K, V, 4>& Top() const { return stack_.Back(); }
+    const Hashmap<K, V, 4>& Top() const { return stack_[depth_ - 1]; }
+
+    /// Return the top scope of the stack.
+    /// @returns the top scope of the stack
+    Hashmap<K, V, 4>& Top() { return stack_[depth_ - 1]; }
 
     /// Clear the scope stack.
     void Clear() {
-        stack_.Clear();
-        stack_.Push({});
+        depth_ = 1;
+        stack_.Resize(1);
+        stack_.Front().Clear();
     }
 
   private:
-    Vector<Hashmap<K, V, 4>, 8> stack_ = {{}};
+    Vector<Hashmap<K, V, 4>, 8> stack_;
+    /// The active count in stack. We don't push and pop the stack to avoid frequent re-allocations
+    /// of the hashmaps.
+    size_t depth_ = 0;
 };
 
 }  // namespace tint