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>
This commit is contained in:
parent
3e1bc0a6da
commit
cc85ed6dd1
|
@ -72,7 +72,6 @@ enum CallSiteTag {
|
||||||
|
|
||||||
/// FunctionTag describes a functions effects on uniformity.
|
/// FunctionTag describes a functions effects on uniformity.
|
||||||
enum FunctionTag {
|
enum FunctionTag {
|
||||||
SubsequentControlFlowMayBeNonUniform,
|
|
||||||
ReturnValueMayBeNonUniform,
|
ReturnValueMayBeNonUniform,
|
||||||
NoRestriction,
|
NoRestriction,
|
||||||
};
|
};
|
||||||
|
@ -80,7 +79,6 @@ enum FunctionTag {
|
||||||
/// ParameterTag describes the uniformity requirements of values passed to a function parameter.
|
/// ParameterTag describes the uniformity requirements of values passed to a function parameter.
|
||||||
enum ParameterTag {
|
enum ParameterTag {
|
||||||
ParameterRequiredToBeUniform,
|
ParameterRequiredToBeUniform,
|
||||||
ParameterRequiredToBeUniformForSubsequentControlFlow,
|
|
||||||
ParameterRequiredToBeUniformForReturnValue,
|
ParameterRequiredToBeUniformForReturnValue,
|
||||||
ParameterNoRestriction,
|
ParameterNoRestriction,
|
||||||
};
|
};
|
||||||
|
@ -163,7 +161,6 @@ struct FunctionInfo {
|
||||||
required_to_be_uniform = CreateNode("RequiredToBeUniform");
|
required_to_be_uniform = CreateNode("RequiredToBeUniform");
|
||||||
may_be_non_uniform = CreateNode("MayBeNonUniform");
|
may_be_non_uniform = CreateNode("MayBeNonUniform");
|
||||||
cf_start = CreateNode("CF_start");
|
cf_start = CreateNode("CF_start");
|
||||||
cf_return = CreateNode("CF_return");
|
|
||||||
if (func->return_type) {
|
if (func->return_type) {
|
||||||
value_return = CreateNode("Value_return");
|
value_return = CreateNode("Value_return");
|
||||||
}
|
}
|
||||||
|
@ -208,8 +205,6 @@ struct FunctionInfo {
|
||||||
Node* may_be_non_uniform;
|
Node* may_be_non_uniform;
|
||||||
/// Special `CF_start` node.
|
/// Special `CF_start` node.
|
||||||
Node* cf_start;
|
Node* cf_start;
|
||||||
/// Special `CF_return` node.
|
|
||||||
Node* cf_return;
|
|
||||||
/// Special `Value_return` node.
|
/// Special `Value_return` node.
|
||||||
Node* value_return;
|
Node* value_return;
|
||||||
|
|
||||||
|
@ -339,8 +334,7 @@ class UniformityGraph {
|
||||||
|
|
||||||
// Process function body.
|
// Process function body.
|
||||||
if (func->body) {
|
if (func->body) {
|
||||||
auto* cf = ProcessStatement(current_function_->cf_start, func->body);
|
ProcessStatement(current_function_->cf_start, func->body);
|
||||||
current_function_->cf_return->AddEdge(cf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if TINT_DUMP_UNIFORMITY_GRAPH
|
#if TINT_DUMP_UNIFORMITY_GRAPH
|
||||||
|
@ -378,25 +372,6 @@ class UniformityGraph {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 "Value_return" exists, look at which nodes are reachable from it
|
||||||
if (current_function_->value_return) {
|
if (current_function_->value_return) {
|
||||||
utils::UniqueVector<Node*, 4> reachable;
|
utils::UniqueVector<Node*, 4> reachable;
|
||||||
|
@ -918,12 +893,10 @@ class UniformityGraph {
|
||||||
Node* cf_ret;
|
Node* cf_ret;
|
||||||
if (r->value) {
|
if (r->value) {
|
||||||
auto [cf1, v] = ProcessExpression(cf, r->value);
|
auto [cf1, v] = ProcessExpression(cf, r->value);
|
||||||
current_function_->cf_return->AddEdge(cf1);
|
|
||||||
current_function_->value_return->AddEdge(v);
|
current_function_->value_return->AddEdge(v);
|
||||||
cf_ret = cf1;
|
cf_ret = cf1;
|
||||||
} else {
|
} else {
|
||||||
TINT_ASSERT(Resolver, cf != nullptr);
|
TINT_ASSERT(Resolver, cf != nullptr);
|
||||||
current_function_->cf_return->AddEdge(cf);
|
|
||||||
cf_ret = cf;
|
cf_ret = cf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1395,10 +1368,7 @@ class UniformityGraph {
|
||||||
}
|
}
|
||||||
cf_after->AddEdge(call_node);
|
cf_after->AddEdge(call_node);
|
||||||
|
|
||||||
if (function_tag == SubsequentControlFlowMayBeNonUniform) {
|
if (function_tag == ReturnValueMayBeNonUniform) {
|
||||||
cf_after->AddEdge(current_function_->may_be_non_uniform);
|
|
||||||
cf_after->affects_control_flow = true;
|
|
||||||
} else if (function_tag == ReturnValueMayBeNonUniform) {
|
|
||||||
result->AddEdge(current_function_->may_be_non_uniform);
|
result->AddEdge(current_function_->may_be_non_uniform);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1411,10 +1381,6 @@ class UniformityGraph {
|
||||||
case ParameterRequiredToBeUniform:
|
case ParameterRequiredToBeUniform:
|
||||||
current_function_->required_to_be_uniform->AddEdge(args[i]);
|
current_function_->required_to_be_uniform->AddEdge(args[i]);
|
||||||
break;
|
break;
|
||||||
case ParameterRequiredToBeUniformForSubsequentControlFlow:
|
|
||||||
cf_after->AddEdge(args[i]);
|
|
||||||
args[i]->affects_control_flow = true;
|
|
||||||
break;
|
|
||||||
case ParameterRequiredToBeUniformForReturnValue:
|
case ParameterRequiredToBeUniformForReturnValue:
|
||||||
result->AddEdge(args[i]);
|
result->AddEdge(args[i]);
|
||||||
break;
|
break;
|
||||||
|
@ -1544,26 +1510,9 @@ class UniformityGraph {
|
||||||
auto* control_flow = TraceBackAlongPathUntil(
|
auto* control_flow = TraceBackAlongPathUntil(
|
||||||
non_uniform_source, [](Node* node) { return node->affects_control_flow; });
|
non_uniform_source, [](Node* node) { return node->affects_control_flow; });
|
||||||
if (control_flow) {
|
if (control_flow) {
|
||||||
if (auto* call = control_flow->ast->As<ast::CallExpression>()) {
|
diagnostics_.add_note(diag::System::Resolver,
|
||||||
if (control_flow->type == Node::kFunctionCallArgument) {
|
"control flow depends on non-uniform value",
|
||||||
auto idx = control_flow->arg_index;
|
control_flow->ast->source);
|
||||||
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);
|
|
||||||
}
|
|
||||||
// TODO(jrprice): There are cases where the function with uniformity requirements is not
|
// TODO(jrprice): There are cases where the function with uniformity requirements is not
|
||||||
// actually inside this control flow construct, for example:
|
// actually inside this control flow construct, for example:
|
||||||
// - A conditional interrupt (e.g. break), with a barrier elsewhere in the loop
|
// - A conditional interrupt (e.g. break), with a barrier elsewhere in the loop
|
||||||
|
@ -1619,21 +1568,6 @@ class UniformityGraph {
|
||||||
auto target_name = builder_->Symbols().NameFor(
|
auto target_name = builder_->Symbols().NameFor(
|
||||||
c->target.name->As<ast::IdentifierExpression>()->symbol);
|
c->target.name->As<ast::IdentifierExpression>()->symbol);
|
||||||
switch (non_uniform_source->type) {
|
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: {
|
case Node::kFunctionCallReturnValue: {
|
||||||
diagnostics_.add_note(
|
diagnostics_.add_note(
|
||||||
diag::System::Resolver,
|
diag::System::Resolver,
|
||||||
|
|
|
@ -338,134 +338,6 @@ INSTANTIATE_TEST_SUITE_P(
|
||||||
/// Test specific function and parameter tags that are not tested above.
|
/// 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) {
|
TEST_F(UniformityAnalysisTest, ParameterNoRestriction_Pass) {
|
||||||
// Pass a non-uniform value as an argument, and then try to use the return value for
|
// Pass a non-uniform value as an argument, and then try to use the return value for
|
||||||
// control-flow guarding a barrier.
|
// control-flow guarding a barrier.
|
||||||
|
@ -599,79 +471,6 @@ test:9:11 note: reading from read_write storage buffer 'rw' may result in a non-
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
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.
|
/// Test shader IO attributes.
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -1770,72 +1569,6 @@ fn foo() {
|
||||||
RunTest(src, true);
|
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) {
|
TEST_F(UniformityAnalysisTest, ForLoop_CallInside_UniformCondition) {
|
||||||
std::string src = R"(
|
std::string src = R"(
|
||||||
@group(0) @binding(0) var<storage, read> n : i32;
|
@group(0) @binding(0) var<storage, read> n : i32;
|
||||||
|
@ -1877,84 +1610,6 @@ test:5:23 note: reading from read_write storage buffer 'n' may result in a non-u
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
TEST_F(UniformityAnalysisTest, ForLoop_VarBecomesNonUniformInContinuing_BarrierInLoop) {
|
||||||
// Use a variable for a conditional barrier in a loop, and then assign a non-uniform value to
|
// Use a variable for a conditional barrier in a loop, and then assign a non-uniform value to
|
||||||
// that variable in the continuing statement.
|
// that variable in the continuing statement.
|
||||||
|
@ -3832,76 +3487,6 @@ fn foo() {
|
||||||
RunTest(src, true);
|
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.
|
/// Pointer tests.
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -7164,6 +6749,32 @@ fn foo() {
|
||||||
/// Miscellaneous statement and expression tests.
|
/// 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_F(UniformityAnalysisTest, FunctionRequiresUniformFlowAndCausesNonUniformFlow) {
|
||||||
// Test that a function that requires uniform flow and then causes non-uniform flow can be
|
// Test that a function that requires uniform flow and then causes non-uniform flow can be
|
||||||
// called without error.
|
// called without error.
|
||||||
|
@ -7328,176 +6939,12 @@ test:5:11 note: reading from read_write storage buffer 'rw' may result in a non-
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
TEST_F(UniformityAnalysisTest, ShortCircuiting_ReconvergeLHS) {
|
||||||
std::string src = R"(
|
std::string src = R"(
|
||||||
@group(0) @binding(0) var<storage, read_write> non_uniform_global : i32;
|
@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() {
|
fn main() {
|
||||||
let b = uniform_discard_func() && false;
|
let b = (non_uniform_global == 0) && false;
|
||||||
workgroupBarrier();
|
workgroupBarrier();
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
@ -7509,17 +6956,8 @@ TEST_F(UniformityAnalysisTest, ShortCircuiting_ReconvergeRHS) {
|
||||||
std::string src = R"(
|
std::string src = R"(
|
||||||
@group(0) @binding(0) var<storage, read_write> non_uniform_global : i32;
|
@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() {
|
fn main() {
|
||||||
let b = false && uniform_discard_func();
|
let b = false && (non_uniform_global == 0);
|
||||||
workgroupBarrier();
|
workgroupBarrier();
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
@ -7531,17 +6969,8 @@ TEST_F(UniformityAnalysisTest, ShortCircuiting_ReconvergeBoth) {
|
||||||
std::string src = R"(
|
std::string src = R"(
|
||||||
@group(0) @binding(0) var<storage, read_write> non_uniform_global : i32;
|
@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() {
|
fn main() {
|
||||||
let b = uniform_discard_func() && uniform_discard_func();
|
let b = (non_uniform_global != 0) && (non_uniform_global != 42);
|
||||||
workgroupBarrier();
|
workgroupBarrier();
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
@ -7867,228 +7296,5 @@ test:17:7 note: return value of 'foo' may be non-uniform
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
} // namespace tint::resolver
|
} // namespace tint::resolver
|
||||||
|
|
Loading…
Reference in New Issue