[tint] Require uniformity for subgroupShuffle* delta values

Allow the severity of this diagnostic to be overridden with the
`subgroup_uniformity` diagnostic rule.

Bug: 369426560
Change-Id: Id222d95cd2f0b2b6212dce50d4da15bffb518de7
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/214896
Reviewed-by: David Neto <dneto@google.com>
Commit-Queue: James Price <jrprice@google.com>
diff --git a/src/tint/lang/wgsl/resolver/uniformity.cc b/src/tint/lang/wgsl/resolver/uniformity.cc
index be0ad09..484d83c 100644
--- a/src/tint/lang/wgsl/resolver/uniformity.cc
+++ b/src/tint/lang/wgsl/resolver/uniformity.cc
@@ -1750,6 +1750,19 @@
                 if (builtin && builtin->Fn() == wgsl::BuiltinFn::kWorkgroupUniformLoad) {
                     // The workgroupUniformLoad builtin requires its parameter to be uniform.
                     current_function_->RequiredToBeUniform(default_severity)->AddEdge(args[i]);
+                } else if (builtin &&
+                           (builtin->Fn() == wgsl::BuiltinFn::kSubgroupShuffleDown ||
+                            builtin->Fn() == wgsl::BuiltinFn::kSubgroupShuffleUp ||
+                            builtin->Fn() == wgsl::BuiltinFn::kSubgroupShuffleXor) &&
+                           i == 1) {
+                    // The subgroupShuffle{Down,Up,Xor} builtins require their `delta` parameters to
+                    // be uniform.
+                    // Get the severity of subgroup uniformity violations in this context.
+                    auto severity = sem_.DiagnosticSeverity(
+                        call->args[i], wgsl::CoreDiagnosticRule::kSubgroupUniformity);
+                    if (severity != wgsl::DiagnosticSeverity::kOff) {
+                        current_function_->RequiredToBeUniform(severity)->AddEdge(args[i]);
+                    }
                 } else {
                     // All other builtin function parameters are RequiredToBeUniformForReturnValue,
                     // as are parameters for value constructors and value conversions.
diff --git a/src/tint/lang/wgsl/resolver/uniformity_test.cc b/src/tint/lang/wgsl/resolver/uniformity_test.cc
index c77c643..576ff69 100644
--- a/src/tint/lang/wgsl/resolver/uniformity_test.cc
+++ b/src/tint/lang/wgsl/resolver/uniformity_test.cc
@@ -9092,6 +9092,75 @@
 )");
 }
 
+TEST_F(UniformityAnalysisTest, SubgroupShuffleDown_Delta) {
+    std::string src = R"(
+enable subgroups;
+
+var<private> delta: u32;
+
+fn foo() {
+  _ = subgroupShuffleDown(1.0, delta);
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:7:32 error: 'subgroupShuffleDown' requires argument 1 to be uniform
+  _ = subgroupShuffleDown(1.0, delta);
+                               ^^^^^
+
+test:7:32 note: reading from module-scope private variable 'delta' may result in a non-uniform value
+  _ = subgroupShuffleDown(1.0, delta);
+                               ^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, SubgroupShuffleUp_Delta) {
+    std::string src = R"(
+enable subgroups;
+
+var<private> delta: u32;
+
+fn foo() {
+  _ = subgroupShuffleUp(1.0, delta);
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:7:30 error: 'subgroupShuffleUp' requires argument 1 to be uniform
+  _ = subgroupShuffleUp(1.0, delta);
+                             ^^^^^
+
+test:7:30 note: reading from module-scope private variable 'delta' may result in a non-uniform value
+  _ = subgroupShuffleUp(1.0, delta);
+                             ^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisTest, SubgroupShuffleXor_Delta) {
+    std::string src = R"(
+enable subgroups;
+
+var<private> delta: u32;
+
+fn foo() {
+  _ = subgroupShuffleXor(1.0, delta);
+}
+)";
+
+    RunTest(src, false);
+    EXPECT_EQ(error_,
+              R"(test:7:31 error: 'subgroupShuffleXor' requires argument 1 to be uniform
+  _ = subgroupShuffleXor(1.0, delta);
+                              ^^^^^
+
+test:7:31 note: reading from module-scope private variable 'delta' may result in a non-uniform value
+  _ = subgroupShuffleXor(1.0, delta);
+                              ^^^^^
+)");
+}
+
 TEST_F(UniformityAnalysisTest, WorkgroupAtomics) {
     std::string src = R"(
 var<workgroup> a : atomic<i32>;
@@ -9288,7 +9357,7 @@
     }
 }
 
-TEST_P(UniformityAnalysisDiagnosticFilterTest, Directive_SubgroupUniformity) {
+TEST_P(UniformityAnalysisDiagnosticFilterTest, Directive_SubgroupUniformity_Callsite) {
     auto& param = GetParam();
     StringStream ss;
     ss << "enable subgroups;\n"
@@ -9313,6 +9382,29 @@
     }
 }
 
+TEST_P(UniformityAnalysisDiagnosticFilterTest, Directive_SubgroupUniformity_ShuffleDelta) {
+    auto& param = GetParam();
+    StringStream ss;
+    ss << "enable subgroups;\n"
+       << "diagnostic(" << param << ", subgroup_uniformity);" << R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : u32;
+
+fn foo() {
+  _ = subgroupShuffleUp(1.0, non_uniform);
+}
+)";
+
+    RunTest(ss.str(), param != wgsl::DiagnosticSeverity::kError);
+
+    if (param == wgsl::DiagnosticSeverity::kOff) {
+        EXPECT_TRUE(error_.empty());
+    } else {
+        StringStream err;
+        err << ToStr(param) << ": 'subgroupShuffleUp' requires argument 1 to be uniform";
+        EXPECT_THAT(error_, ::testing::HasSubstr(err.str()));
+    }
+}
+
 TEST_P(UniformityAnalysisDiagnosticFilterTest, AttributeOnFunction_DerivativeUniformity) {
     auto& param = GetParam();
     StringStream ss;
@@ -9339,7 +9431,7 @@
     }
 }
 
-TEST_P(UniformityAnalysisDiagnosticFilterTest, AttributeOnFunction_SubgroupUniformity) {
+TEST_P(UniformityAnalysisDiagnosticFilterTest, AttributeOnFunction_SubgroupUniformity_Callsite) {
     auto& param = GetParam();
     StringStream ss;
     ss << R"(
@@ -9364,6 +9456,31 @@
     }
 }
 
+TEST_P(UniformityAnalysisDiagnosticFilterTest,
+       AttributeOnFunction_SubgroupUniformity_ShuffleDelta) {
+    auto& param = GetParam();
+    StringStream ss;
+    ss << "enable subgroups;\n"
+       << "diagnostic(" << param << ", subgroup_uniformity);" << R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : u32;
+)" << "@diagnostic("
+       << param << ", subgroup_uniformity)" <<
+        R"(fn foo() {
+  _ = subgroupShuffleUp(1.0, non_uniform);
+}
+)";
+
+    RunTest(ss.str(), param != wgsl::DiagnosticSeverity::kError);
+
+    if (param == wgsl::DiagnosticSeverity::kOff) {
+        EXPECT_TRUE(error_.empty());
+    } else {
+        StringStream err;
+        err << ToStr(param) << ": 'subgroupShuffleUp' requires argument 1 to be uniform";
+        EXPECT_THAT(error_, ::testing::HasSubstr(err.str()));
+    }
+}
+
 // Test the various places that the attributes can be used against just one diagnostic rule, to
 // avoid over-parameterizing the tests.