tint: Function calls do not affect control flow uniformity
We now use the MergeReturn transform for SPIR-V, which is the only
backend that requires special handling to meet these function call
reconvergence requirements.
Fixed: tint:1627, tint:1726
Change-Id: I25f848f4b9ff0fd301b8a27a220bb09cdb2867ca
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/107364
Commit-Queue: James Price <jrprice@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/resolver/uniformity.cc b/src/tint/resolver/uniformity.cc
index eb62c0c..831e49e 100644
--- a/src/tint/resolver/uniformity.cc
+++ b/src/tint/resolver/uniformity.cc
@@ -72,7 +72,6 @@
/// FunctionTag describes a functions effects on uniformity.
enum FunctionTag {
- SubsequentControlFlowMayBeNonUniform,
ReturnValueMayBeNonUniform,
NoRestriction,
};
@@ -80,7 +79,6 @@
/// ParameterTag describes the uniformity requirements of values passed to a function parameter.
enum ParameterTag {
ParameterRequiredToBeUniform,
- ParameterRequiredToBeUniformForSubsequentControlFlow,
ParameterRequiredToBeUniformForReturnValue,
ParameterNoRestriction,
};
@@ -163,7 +161,6 @@
required_to_be_uniform = CreateNode("RequiredToBeUniform");
may_be_non_uniform = CreateNode("MayBeNonUniform");
cf_start = CreateNode("CF_start");
- cf_return = CreateNode("CF_return");
if (func->return_type) {
value_return = CreateNode("Value_return");
}
@@ -208,8 +205,6 @@
Node* may_be_non_uniform;
/// Special `CF_start` node.
Node* cf_start;
- /// Special `CF_return` node.
- Node* cf_return;
/// Special `Value_return` node.
Node* value_return;
@@ -339,8 +334,7 @@
// Process function body.
if (func->body) {
- auto* cf = ProcessStatement(current_function_->cf_start, func->body);
- current_function_->cf_return->AddEdge(cf);
+ ProcessStatement(current_function_->cf_start, func->body);
}
#if TINT_DUMP_UNIFORMITY_GRAPH
@@ -378,25 +372,6 @@
}
}
- // Look at which nodes are reachable from "CF_return"
- {
- utils::UniqueVector<Node*, 4> reachable;
- Traverse(current_function_->cf_return, &reachable);
- if (reachable.Contains(current_function_->may_be_non_uniform)) {
- current_function_->function_tag = SubsequentControlFlowMayBeNonUniform;
- }
-
- // Set the parameter tag to ParameterRequiredToBeUniformForSubsequentControlFlow for
- // each parameter node that was reachable.
- for (size_t i = 0; i < func->params.Length(); i++) {
- auto* param = func->params[i];
- if (reachable.Contains(current_function_->variables.Get(sem_.Get(param)))) {
- current_function_->parameters[i].tag =
- ParameterRequiredToBeUniformForSubsequentControlFlow;
- }
- }
- }
-
// If "Value_return" exists, look at which nodes are reachable from it
if (current_function_->value_return) {
utils::UniqueVector<Node*, 4> reachable;
@@ -918,12 +893,10 @@
Node* cf_ret;
if (r->value) {
auto [cf1, v] = ProcessExpression(cf, r->value);
- current_function_->cf_return->AddEdge(cf1);
current_function_->value_return->AddEdge(v);
cf_ret = cf1;
} else {
TINT_ASSERT(Resolver, cf != nullptr);
- current_function_->cf_return->AddEdge(cf);
cf_ret = cf;
}
@@ -1395,10 +1368,7 @@
}
cf_after->AddEdge(call_node);
- if (function_tag == SubsequentControlFlowMayBeNonUniform) {
- cf_after->AddEdge(current_function_->may_be_non_uniform);
- cf_after->affects_control_flow = true;
- } else if (function_tag == ReturnValueMayBeNonUniform) {
+ if (function_tag == ReturnValueMayBeNonUniform) {
result->AddEdge(current_function_->may_be_non_uniform);
}
@@ -1411,10 +1381,6 @@
case ParameterRequiredToBeUniform:
current_function_->required_to_be_uniform->AddEdge(args[i]);
break;
- case ParameterRequiredToBeUniformForSubsequentControlFlow:
- cf_after->AddEdge(args[i]);
- args[i]->affects_control_flow = true;
- break;
case ParameterRequiredToBeUniformForReturnValue:
result->AddEdge(args[i]);
break;
@@ -1544,26 +1510,9 @@
auto* control_flow = TraceBackAlongPathUntil(
non_uniform_source, [](Node* node) { return node->affects_control_flow; });
if (control_flow) {
- if (auto* call = control_flow->ast->As<ast::CallExpression>()) {
- if (control_flow->type == Node::kFunctionCallArgument) {
- auto idx = control_flow->arg_index;
- diagnostics_.add_note(diag::System::Resolver,
- "non-uniform function call argument causes subsequent "
- "control flow to be non-uniform",
- call->args[idx]->source);
-
- // Recurse into the target function.
- if (auto* user = SemCall(call)->Target()->As<sem::Function>()) {
- auto& callee = functions_.at(user->Declaration());
- ShowCauseOfNonUniformity(callee, callee.cf_return,
- callee.parameters[idx].init_value);
- }
- }
- } else {
- diagnostics_.add_note(diag::System::Resolver,
- "control flow depends on non-uniform value",
- control_flow->ast->source);
- }
+ diagnostics_.add_note(diag::System::Resolver,
+ "control flow depends on non-uniform value",
+ control_flow->ast->source);
// TODO(jrprice): There are cases where the function with uniformity requirements is not
// actually inside this control flow construct, for example:
// - A conditional interrupt (e.g. break), with a barrier elsewhere in the loop
@@ -1619,21 +1568,6 @@
auto target_name = builder_->Symbols().NameFor(
c->target.name->As<ast::IdentifierExpression>()->symbol);
switch (non_uniform_source->type) {
- case Node::kRegular: {
- diagnostics_.add_note(
- diag::System::Resolver,
- "calling '" + target_name +
- "' may cause subsequent control flow to be non-uniform",
- c->source);
-
- // Recurse into the target function.
- if (auto* user = SemCall(c)->Target()->As<sem::Function>()) {
- auto& callee = functions_.at(user->Declaration());
- ShowCauseOfNonUniformity(callee, callee.cf_return,
- callee.may_be_non_uniform);
- }
- break;
- }
case Node::kFunctionCallReturnValue: {
diagnostics_.add_note(
diag::System::Resolver,
diff --git a/src/tint/resolver/uniformity_test.cc b/src/tint/resolver/uniformity_test.cc
index 5cf475c..faefb13 100644
--- a/src/tint/resolver/uniformity_test.cc
+++ b/src/tint/resolver/uniformity_test.cc
@@ -338,134 +338,6 @@
/// Test specific function and parameter tags that are not tested above.
////////////////////////////////////////////////////////////////////////////////
-TEST_F(UniformityAnalysisTest, SubsequentControlFlowMayBeNonUniform_Pass) {
- // Call a function that causes subsequent control flow to be non-uniform, and then call another
- // function that doesn't require uniformity.
- std::string src = R"(
-@group(0) @binding(0) var<storage, read_write> rw : i32;
-
-var<private> p : i32;
-
-fn foo() {
- if (rw == 0) {
- p = 42;
- return;
- }
- p = 5;
- return;
-}
-
-fn bar() {
- if (p == 42) {
- p = 7;
- }
-}
-
-fn main() {
- foo();
- bar();
-}
-)";
-
- RunTest(src, true);
-}
-
-TEST_F(UniformityAnalysisTest, SubsequentControlFlowMayBeNonUniform_Fail) {
- // Call a function that causes subsequent control flow to be non-uniform, and then call another
- // function that requires uniformity.
- std::string src = R"(
-@group(0) @binding(0) var<storage, read_write> rw : i32;
-
-var<private> p : i32;
-
-fn foo() {
- if (rw == 0) {
- p = 42;
- return;
- }
- p = 5;
- return;
-}
-
-fn main() {
- foo();
- workgroupBarrier();
-}
-)";
-
- RunTest(src, false);
- EXPECT_EQ(error_,
- R"(test:17:3 warning: 'workgroupBarrier' must only be called from uniform control flow
- workgroupBarrier();
- ^^^^^^^^^^^^^^^^
-
-test:16:3 note: calling 'foo' may cause subsequent control flow to be non-uniform
- foo();
- ^^^
-
-test:7:3 note: control flow depends on non-uniform value
- if (rw == 0) {
- ^^
-
-test:7:7 note: reading from read_write storage buffer 'rw' may result in a non-uniform value
- if (rw == 0) {
- ^^
-)");
-}
-
-TEST_F(UniformityAnalysisTest, SubsequentControlFlowMayBeNonUniform_Nested_Fail) {
- // Indirectly call a function that causes subsequent control flow to be non-uniform, and then
- // call another function that requires uniformity.
- // The lack of return statement in `foo()` requires that we implicitly add an edge from
- // CF_return to that last control flow node of the function.
- std::string src = R"(
-@group(0) @binding(0) var<storage, read_write> rw : i32;
-
-var<private> p : i32;
-
-fn bar() {
- if (rw == 0) {
- p = 42;
- return;
- }
- p = 5;
- return;
-}
-
-fn foo() {
- bar();
-}
-
-fn main() {
- foo();
- workgroupBarrier();
-}
-)";
-
- RunTest(src, false);
- EXPECT_EQ(error_,
- R"(test:21:3 warning: 'workgroupBarrier' must only be called from uniform control flow
- workgroupBarrier();
- ^^^^^^^^^^^^^^^^
-
-test:20:3 note: calling 'foo' may cause subsequent control flow to be non-uniform
- foo();
- ^^^
-
-test:16:3 note: calling 'bar' may cause subsequent control flow to be non-uniform
- bar();
- ^^^
-
-test:7:3 note: control flow depends on non-uniform value
- if (rw == 0) {
- ^^
-
-test:7:7 note: reading from read_write storage buffer 'rw' may result in a non-uniform value
- if (rw == 0) {
- ^^
-)");
-}
-
TEST_F(UniformityAnalysisTest, ParameterNoRestriction_Pass) {
// Pass a non-uniform value as an argument, and then try to use the return value for
// control-flow guarding a barrier.
@@ -599,79 +471,6 @@
)");
}
-TEST_F(UniformityAnalysisTest, ParameterRequiredToBeUniformForSubsequentControlFlow_Pass) {
- // Pass a uniform value as an argument to a function that uses that parameter return early, and
- // then invoke a barrier after calling that function.
- std::string src = R"(
-@group(0) @binding(0) var<storage, read> ro : i32;
-
-var<private> p : i32;
-
-fn foo(i : i32) {
- if (i == 0) {
- p = 42;
- return;
- }
- p = 5;
- return;
-}
-
-fn bar() {
- foo(ro);
- workgroupBarrier();
-}
-)";
-
- RunTest(src, true);
-}
-
-TEST_F(UniformityAnalysisTest, ParameterRequiredToBeUniformForSubsequentControlFlow_Fail) {
- // Pass a non-uniform value as an argument to a function that uses that parameter return early,
- // and then invoke a barrier after calling that function.
- std::string src = R"(
-@group(0) @binding(0) var<storage, read_write> rw : i32;
-
-var<private> p : i32;
-
-fn foo(i : i32) {
- if (i == 0) {
- p = 42;
- return;
- }
- p = 5;
- return;
-}
-
-fn bar() {
- foo(rw);
- workgroupBarrier();
-}
-)";
-
- RunTest(src, false);
- EXPECT_EQ(error_,
- R"(test:17:3 warning: 'workgroupBarrier' must only be called from uniform control flow
- workgroupBarrier();
- ^^^^^^^^^^^^^^^^
-
-test:16:7 note: non-uniform function call argument causes subsequent control flow to be non-uniform
- foo(rw);
- ^^
-
-test:7:3 note: control flow depends on non-uniform value
- if (i == 0) {
- ^^
-
-test:7:7 note: reading from 'i' may result in a non-uniform value
- if (i == 0) {
- ^
-
-test:16:7 note: reading from read_write storage buffer 'rw' may result in a non-uniform value
- foo(rw);
- ^^
-)");
-}
-
////////////////////////////////////////////////////////////////////////////////
/// Test shader IO attributes.
////////////////////////////////////////////////////////////////////////////////
@@ -1770,72 +1569,6 @@
RunTest(src, true);
}
-TEST_F(UniformityAnalysisTest, Loop_NonUniformFunctionInBody_Reconverge) {
- // Loops reconverge at exit, so test that we can call workgroupBarrier() after a loop that
- // contains a call to a function that causes non-uniform control flow.
- std::string src = R"(
-@group(0) @binding(0) var<storage, read_write> n : i32;
-
-fn bar() {
- if (n == 42) {
- return;
- } else {
- return;
- }
-}
-
-fn foo() {
- loop {
- bar();
- break;
- }
- workgroupBarrier();
-}
-)";
-
- RunTest(src, true);
-}
-
-TEST_F(UniformityAnalysisTest, Loop_NonUniformFunctionDiscard_NoReconvergence) {
- // Loops should not reconverge after non-uniform discard statements.
- std::string src = R"(
-@group(0) @binding(0) var<storage, read_write> n : i32;
-
-fn bar() {
- if (n == 42) {
- discard;
- }
-}
-
-fn foo() {
- loop {
- bar();
- break;
- }
- workgroupBarrier();
-}
-)";
-
- RunTest(src, false);
- EXPECT_EQ(error_,
- R"(test:15:3 warning: 'workgroupBarrier' must only be called from uniform control flow
- workgroupBarrier();
- ^^^^^^^^^^^^^^^^
-
-test:12:5 note: calling 'bar' may cause subsequent control flow to be non-uniform
- bar();
- ^^^
-
-test:5:3 note: control flow depends on non-uniform value
- if (n == 42) {
- ^^
-
-test:5:7 note: reading from read_write storage buffer 'n' may result in a non-uniform value
- if (n == 42) {
- ^
-)");
-}
-
TEST_F(UniformityAnalysisTest, ForLoop_CallInside_UniformCondition) {
std::string src = R"(
@group(0) @binding(0) var<storage, read> n : i32;
@@ -1877,84 +1610,6 @@
)");
}
-TEST_F(UniformityAnalysisTest, ForLoop_CallInside_InitializerCausesNonUniformFlow) {
- std::string src = R"(
-@group(0) @binding(0) var<storage, read_write> n : i32;
-
-fn bar() -> i32 {
- if (n == 42) {
- return 1;
- } else {
- return 2;
- }
-}
-
-fn foo() {
- for (var i = bar(); i < 10; i = i + 1) {
- workgroupBarrier();
- }
-}
-)";
-
- RunTest(src, false);
- EXPECT_EQ(error_,
- R"(test:14:5 warning: 'workgroupBarrier' must only be called from uniform control flow
- workgroupBarrier();
- ^^^^^^^^^^^^^^^^
-
-test:13:16 note: calling 'bar' may cause subsequent control flow to be non-uniform
- for (var i = bar(); i < 10; i = i + 1) {
- ^^^
-
-test:5:3 note: control flow depends on non-uniform value
- if (n == 42) {
- ^^
-
-test:5:7 note: reading from read_write storage buffer 'n' may result in a non-uniform value
- if (n == 42) {
- ^
-)");
-}
-
-TEST_F(UniformityAnalysisTest, ForLoop_CallInside_ContinuingCausesNonUniformFlow) {
- std::string src = R"(
-@group(0) @binding(0) var<storage, read_write> n : i32;
-
-fn bar() -> i32 {
- if (n == 42) {
- return 1;
- } else {
- return 2;
- }
-}
-
-fn foo() {
- for (var i = 0; i < 10; i = i + bar()) {
- workgroupBarrier();
- }
-}
-)";
-
- RunTest(src, false);
- EXPECT_EQ(error_,
- R"(test:14:5 warning: 'workgroupBarrier' must only be called from uniform control flow
- workgroupBarrier();
- ^^^^^^^^^^^^^^^^
-
-test:13:35 note: calling 'bar' may cause subsequent control flow to be non-uniform
- for (var i = 0; i < 10; i = i + bar()) {
- ^^^
-
-test:5:3 note: control flow depends on non-uniform value
- if (n == 42) {
- ^^
-
-test:5:7 note: reading from read_write storage buffer 'n' may result in a non-uniform value
- if (n == 42) {
- ^
-)");
-}
-
TEST_F(UniformityAnalysisTest, ForLoop_VarBecomesNonUniformInContinuing_BarrierInLoop) {
// Use a variable for a conditional barrier in a loop, and then assign a non-uniform value to
// that variable in the continuing statement.
@@ -3832,76 +3487,6 @@
RunTest(src, true);
}
-TEST_F(UniformityAnalysisTest, Switch_NonUniformFunctionCall_Reconverge) {
- // Switch statements reconverge at exit, so test that we can call workgroupBarrier() after a
- // switch statement that contains a call to a function that causes non-uniform control flow.
- std::string src = R"(
-@group(0) @binding(0) var<storage, read_write> n : i32;
-
-fn bar() {
- if (n == 42) {
- return;
- } else {
- return;
- }
-}
-
-fn foo() {
- switch (42) {
- default: {
- bar();
- break;
- }
- }
- workgroupBarrier();
-}
-)";
-
- RunTest(src, true);
-}
-
-TEST_F(UniformityAnalysisTest, Switch_NonUniformFunctionDiscard_NoReconvergence) {
- // Switch statements should not reconverge after non-uniform discards.
- std::string src = R"(
-@group(0) @binding(0) var<storage, read_write> n : i32;
-
-fn bar() {
- if (n == 42) {
- discard;
- }
-}
-
-fn foo() {
- switch (42) {
- default: {
- bar();
- break;
- }
- }
- workgroupBarrier();
-}
-)";
-
- RunTest(src, false);
- EXPECT_EQ(error_,
- R"(test:17:3 warning: 'workgroupBarrier' must only be called from uniform control flow
- workgroupBarrier();
- ^^^^^^^^^^^^^^^^
-
-test:13:7 note: calling 'bar' may cause subsequent control flow to be non-uniform
- bar();
- ^^^
-
-test:5:3 note: control flow depends on non-uniform value
- if (n == 42) {
- ^^
-
-test:5:7 note: reading from read_write storage buffer 'n' may result in a non-uniform value
- if (n == 42) {
- ^
-)");
-}
-
////////////////////////////////////////////////////////////////////////////////
/// Pointer tests.
////////////////////////////////////////////////////////////////////////////////
@@ -7164,6 +6749,32 @@
/// Miscellaneous statement and expression tests.
////////////////////////////////////////////////////////////////////////////////
+TEST_F(UniformityAnalysisTest, FunctionReconvergesOnExit) {
+ // Call a function that has returns during non-uniform control flow, and test that the analysis
+ // reconverges when returning to the caller.
+ std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> rw : i32;
+
+var<private> p : i32;
+
+fn foo() {
+ if (rw == 0) {
+ p = 42;
+ return;
+ }
+ p = 5;
+ return;
+}
+
+fn main() {
+ foo();
+ workgroupBarrier();
+}
+)";
+
+ RunTest(src, true);
+}
+
TEST_F(UniformityAnalysisTest, FunctionRequiresUniformFlowAndCausesNonUniformFlow) {
// Test that a function that requires uniform flow and then causes non-uniform flow can be
// called without error.
@@ -7328,176 +6939,12 @@
)");
}
-TEST_F(UniformityAnalysisTest, PhonyAssignment_LhsCausesNonUniformControlFlow) {
- std::string src = R"(
-@group(0) @binding(0) var<storage, read_write> nonuniform_var : i32;
-
-fn bar() -> i32 {
- if (nonuniform_var == 42) {
- return 1;
- } else {
- return 2;
- }
-}
-
-fn foo() {
- _ = bar();
- workgroupBarrier();
-}
-)";
-
- RunTest(src, false);
- EXPECT_EQ(error_,
- R"(test:14:3 warning: 'workgroupBarrier' must only be called from uniform control flow
- workgroupBarrier();
- ^^^^^^^^^^^^^^^^
-
-test:13:7 note: calling 'bar' may cause subsequent control flow to be non-uniform
- _ = bar();
- ^^^
-
-test:5:3 note: control flow depends on non-uniform value
- if (nonuniform_var == 42) {
- ^^
-
-test:5:7 note: reading from read_write storage buffer 'nonuniform_var' may result in a non-uniform value
- if (nonuniform_var == 42) {
- ^^^^^^^^^^^^^^
-)");
-}
-
-TEST_F(UniformityAnalysisTest, ShortCircuiting_NoReconvergeLHS) {
- std::string src = R"(
-@group(0) @binding(0) var<storage, read_write> non_uniform_global : i32;
-
-var<private> p : i32;
-
-fn non_uniform_discard_func() -> bool {
- if (non_uniform_global == 42) {
- discard;
- }
- return false;
-}
-
-fn main() {
- let b = non_uniform_discard_func() && false;
- workgroupBarrier();
-}
-)";
-
- RunTest(src, false);
- EXPECT_EQ(error_,
- R"(test:15:3 warning: 'workgroupBarrier' must only be called from uniform control flow
- workgroupBarrier();
- ^^^^^^^^^^^^^^^^
-
-test:14:11 note: calling 'non_uniform_discard_func' may cause subsequent control flow to be non-uniform
- let b = non_uniform_discard_func() && false;
- ^^^^^^^^^^^^^^^^^^^^^^^^
-
-test:7:3 note: control flow depends on non-uniform value
- if (non_uniform_global == 42) {
- ^^
-
-test:7:7 note: reading from read_write storage buffer 'non_uniform_global' may result in a non-uniform value
- if (non_uniform_global == 42) {
- ^^^^^^^^^^^^^^^^^^
-)");
-}
-
-TEST_F(UniformityAnalysisTest, ShortCircuiting_NoReconvergeRHS) {
- std::string src = R"(
-@group(0) @binding(0) var<storage, read_write> non_uniform_global : i32;
-
-var<private> p : i32;
-
-fn non_uniform_discard_func() -> bool {
- if (non_uniform_global == 42) {
- discard;
- }
- return false;
-}
-
-fn main() {
- let b = false && non_uniform_discard_func();
- workgroupBarrier();
-}
-)";
-
- RunTest(src, false);
- EXPECT_EQ(error_,
- R"(test:15:3 warning: 'workgroupBarrier' must only be called from uniform control flow
- workgroupBarrier();
- ^^^^^^^^^^^^^^^^
-
-test:14:20 note: calling 'non_uniform_discard_func' may cause subsequent control flow to be non-uniform
- let b = false && non_uniform_discard_func();
- ^^^^^^^^^^^^^^^^^^^^^^^^
-
-test:7:3 note: control flow depends on non-uniform value
- if (non_uniform_global == 42) {
- ^^
-
-test:7:7 note: reading from read_write storage buffer 'non_uniform_global' may result in a non-uniform value
- if (non_uniform_global == 42) {
- ^^^^^^^^^^^^^^^^^^
-)");
-}
-
-TEST_F(UniformityAnalysisTest, ShortCircuiting_NoReconvergeBoth) {
- std::string src = R"(
-@group(0) @binding(0) var<storage, read_write> non_uniform_global : i32;
-
-var<private> p : i32;
-
-fn non_uniform_discard_func() -> bool {
- if (non_uniform_global == 42) {
- discard;
- }
- return false;
-}
-
-fn main() {
- let b = non_uniform_discard_func() && non_uniform_discard_func();
- workgroupBarrier();
-}
-)";
-
- RunTest(src, false);
- EXPECT_EQ(error_,
- R"(test:15:3 warning: 'workgroupBarrier' must only be called from uniform control flow
- workgroupBarrier();
- ^^^^^^^^^^^^^^^^
-
-test:14:41 note: calling 'non_uniform_discard_func' may cause subsequent control flow to be non-uniform
- let b = non_uniform_discard_func() && non_uniform_discard_func();
- ^^^^^^^^^^^^^^^^^^^^^^^^
-
-test:7:3 note: control flow depends on non-uniform value
- if (non_uniform_global == 42) {
- ^^
-
-test:7:7 note: reading from read_write storage buffer 'non_uniform_global' may result in a non-uniform value
- if (non_uniform_global == 42) {
- ^^^^^^^^^^^^^^^^^^
-)");
-}
-
TEST_F(UniformityAnalysisTest, ShortCircuiting_ReconvergeLHS) {
std::string src = R"(
@group(0) @binding(0) var<storage, read_write> non_uniform_global : i32;
-var<private> p : i32;
-
-fn uniform_discard_func() -> bool {
- if (true) {
- discard;
- }
- return false;
-}
-
fn main() {
- let b = uniform_discard_func() && false;
+ let b = (non_uniform_global == 0) && false;
workgroupBarrier();
}
)";
@@ -7509,17 +6956,8 @@
std::string src = R"(
@group(0) @binding(0) var<storage, read_write> non_uniform_global : i32;
-var<private> p : i32;
-
-fn uniform_discard_func() -> bool {
- if (true) {
- discard;
- }
- return false;
-}
-
fn main() {
- let b = false && uniform_discard_func();
+ let b = false && (non_uniform_global == 0);
workgroupBarrier();
}
)";
@@ -7531,17 +6969,8 @@
std::string src = R"(
@group(0) @binding(0) var<storage, read_write> non_uniform_global : i32;
-var<private> p : i32;
-
-fn uniform_discard_func() -> bool {
- if (true) {
- discard;
- }
- return false;
-}
-
fn main() {
- let b = uniform_discard_func() && uniform_discard_func();
+ let b = (non_uniform_global != 0) && (non_uniform_global != 42);
workgroupBarrier();
}
)";
@@ -7867,228 +7296,5 @@
)");
}
-TEST_F(UniformityAnalysisTest, Error_SubsequentControlFlowMayBeNonUniform) {
- // Make sure we correctly identify the function call as the source of non-uniform control flow
- // and not the if statement with the uniform condition.
- std::string src = R"(
-@group(0) @binding(0) var<uniform> uniform_value : i32;
-@group(0) @binding(1) var<storage, read_write> non_uniform_value : i32;
-
-fn foo() -> i32 {
- if (non_uniform_value == 0) {
- return 5;
- }
- return 6;
-}
-
-fn main() {
- foo();
- if (uniform_value == 42) {
- workgroupBarrier();
- }
-}
-)";
-
- RunTest(src, false);
- EXPECT_EQ(error_,
- R"(test:15:5 warning: 'workgroupBarrier' must only be called from uniform control flow
- workgroupBarrier();
- ^^^^^^^^^^^^^^^^
-
-test:13:3 note: calling 'foo' may cause subsequent control flow to be non-uniform
- foo();
- ^^^
-
-test:6:3 note: control flow depends on non-uniform value
- if (non_uniform_value == 0) {
- ^^
-
-test:6:7 note: reading from read_write storage buffer 'non_uniform_value' may result in a non-uniform value
- if (non_uniform_value == 0) {
- ^^^^^^^^^^^^^^^^^
-)");
-}
-
-TEST_F(UniformityAnalysisTest, Error_ParameterRequiredToBeUniformForSubsequentControlFlow) {
- // Make sure we correctly identify the function call as the source of non-uniform control flow
- // and not the if statement with the uniform condition.
- std::string src = R"(
-@group(0) @binding(0) var<uniform> uniform_value : i32;
-@group(0) @binding(1) var<storage, read_write> non_uniform_value : i32;
-
-fn foo(x : i32) -> i32 {
- if (x == 0) {
- return 5;
- }
- return 6;
-}
-
-fn main() {
- foo(non_uniform_value);
- if (uniform_value == 42) {
- workgroupBarrier();
- }
-}
-)";
-
- RunTest(src, false);
- EXPECT_EQ(error_,
- R"(test:15:5 warning: 'workgroupBarrier' must only be called from uniform control flow
- workgroupBarrier();
- ^^^^^^^^^^^^^^^^
-
-test:13:7 note: non-uniform function call argument causes subsequent control flow to be non-uniform
- foo(non_uniform_value);
- ^^^^^^^^^^^^^^^^^
-
-test:6:3 note: control flow depends on non-uniform value
- if (x == 0) {
- ^^
-
-test:6:7 note: reading from 'x' may result in a non-uniform value
- if (x == 0) {
- ^
-
-test:13:7 note: reading from read_write storage buffer 'non_uniform_value' may result in a non-uniform value
- foo(non_uniform_value);
- ^^^^^^^^^^^^^^^^^
-)");
-}
-
-TEST_F(UniformityAnalysisTest,
- Error_ParameterRequiredToBeUniformForSubsequentControlFlow_ViaPointer) {
- // Make sure we correctly identify the function call as the source of non-uniform control flow
- // and not the if statement with the uniform condition.
- std::string src = R"(
-@group(0) @binding(1) var<storage, read_write> non_uniform_value : vec4<f32>;
-
-fn foo(limit : ptr<function, f32>) -> f32 {
- var i : i32;
- if (f32(i) > *limit) {
- return 0.0;
- }
- return 1.0f;
-}
-
-fn main() {
- var param : f32 = non_uniform_value.y;
- let i = foo(¶m);
- let y = dpdx(vec3<f32>());
-}
-)";
-
- RunTest(src, false);
- EXPECT_EQ(error_,
- R"(test:15:11 warning: 'dpdx' must only be called from uniform control flow
- let y = dpdx(vec3<f32>());
- ^^^^
-
-test:14:15 note: non-uniform function call argument causes subsequent control flow to be non-uniform
- let i = foo(¶m);
- ^
-
-test:6:3 note: control flow depends on non-uniform value
- if (f32(i) > *limit) {
- ^^
-
-test:6:14 note: result of expression may be non-uniform
- if (f32(i) > *limit) {
- ^
-
-test:13:21 note: reading from read_write storage buffer 'non_uniform_value' may result in a non-uniform value
- var param : f32 = non_uniform_value.y;
- ^^^^^^^^^^^^^^^^^
-)");
-}
-
-TEST_F(UniformityAnalysisTest,
- Error_ParameterRequiredToBeUniformForSubsequentControlFlow_ViaPointer_InLoop) {
- // Make sure we correctly identify the function call as the source of non-uniform control flow
- // and not the if statement with the uniform condition.
- std::string src = R"(
-@group(0) @binding(1) var<storage, read_write> non_uniform_value : vec4<f32>;
-
-fn foo(limit : ptr<function, f32>) -> f32 {
- var i : i32;
- loop {
- if (f32(i) > *limit) {
- return 0.0;
- }
- }
-}
-
-fn main() {
- var param : f32 = non_uniform_value.y;
- let i = foo(¶m);
- let y = dpdx(vec3<f32>());
-}
-)";
-
- RunTest(src, false);
- EXPECT_EQ(error_,
- R"(test:16:11 warning: 'dpdx' must only be called from uniform control flow
- let y = dpdx(vec3<f32>());
- ^^^^
-
-test:15:15 note: non-uniform function call argument causes subsequent control flow to be non-uniform
- let i = foo(¶m);
- ^
-
-test:7:5 note: control flow depends on non-uniform value
- if (f32(i) > *limit) {
- ^^
-
-test:4:8 note: reading from 'limit' may result in a non-uniform value
-fn foo(limit : ptr<function, f32>) -> f32 {
- ^^^^^
-
-test:14:21 note: reading from read_write storage buffer 'non_uniform_value' may result in a non-uniform value
- var param : f32 = non_uniform_value.y;
- ^^^^^^^^^^^^^^^^^
-)");
-}
-
-TEST_F(UniformityAnalysisTest, Error_ShortCircuitingExprCausesNonUniformControlFlow) {
- // Make sure we correctly identify the short-circuit as the source of non-uniform control flow
- // and not the if statement with the uniform condition.
- std::string src = R"(
-@group(0) @binding(0) var<uniform> uniform_value : i32;
-@group(0) @binding(1) var<storage, read_write> non_uniform_value : i32;
-
-fn non_uniform_discard_func() -> bool {
- if (non_uniform_value == 42) {
- discard;
- }
- return false;
-}
-
-fn main() {
- let b = non_uniform_discard_func() && true;
- if (uniform_value == 42) {
- workgroupBarrier();
- }
-}
-)";
-
- RunTest(src, false);
- EXPECT_EQ(error_,
- R"(test:15:5 warning: 'workgroupBarrier' must only be called from uniform control flow
- workgroupBarrier();
- ^^^^^^^^^^^^^^^^
-
-test:13:11 note: calling 'non_uniform_discard_func' may cause subsequent control flow to be non-uniform
- let b = non_uniform_discard_func() && true;
- ^^^^^^^^^^^^^^^^^^^^^^^^
-
-test:6:3 note: control flow depends on non-uniform value
- if (non_uniform_value == 42) {
- ^^
-
-test:6:7 note: reading from read_write storage buffer 'non_uniform_value' may result in a non-uniform value
- if (non_uniform_value == 42) {
- ^^^^^^^^^^^^^^^^^
-)");
-}
-
} // namespace
} // namespace tint::resolver