tint/uniformity: Fix issues with for-loops
Variables declared in for-loop initializers were not being tracked properly across iterations as a check was wrongly determining them to be declared inside the loop body. Also fixes an issue where variables declared in for-loop initializers were still considered to be in scope after loop exit. Change-Id: I2ce3a519be45c8daba31bf00e8b2614f0bd6a2de Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/114364 Reviewed-by: Antonio Maiorano <amaiorano@google.com> Auto-Submit: James Price <jrprice@google.com> Kokoro: Kokoro <noreply+kokoro@google.com> Commit-Queue: James Price <jrprice@google.com>
This commit is contained in:
parent
056618541f
commit
efc9df4695
|
@ -591,20 +591,11 @@ class UniformityGraph {
|
||||||
auto& info = current_function_->LoopSwitchInfoFor(parent);
|
auto& info = current_function_->LoopSwitchInfoFor(parent);
|
||||||
|
|
||||||
// Propagate assignments to the loop input nodes.
|
// Propagate assignments to the loop input nodes.
|
||||||
for (auto* var : current_function_->local_var_decls) {
|
for (auto v : info.var_in_nodes) {
|
||||||
// Skip variables that were declared inside this loop.
|
auto* in_node = v.value;
|
||||||
if (auto* lv = var->As<sem::LocalVariable>();
|
auto* out_node = current_function_->variables.Get(v.key);
|
||||||
lv &&
|
if (out_node != in_node) {
|
||||||
lv->Statement()->FindFirstParent([&](auto* s) { return s == parent; })) {
|
in_node->AddEdge(out_node);
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add an edge from the variable's loop input node to its value at this point.
|
|
||||||
auto in_node = info.var_in_nodes.Find(var);
|
|
||||||
TINT_ASSERT(Resolver, in_node != nullptr);
|
|
||||||
auto* out_node = current_function_->variables.Get(var);
|
|
||||||
if (out_node != *in_node) {
|
|
||||||
(*in_node)->AddEdge(out_node);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return cf;
|
return cf;
|
||||||
|
@ -677,6 +668,13 @@ class UniformityGraph {
|
||||||
current_function_->variables.Set(v.key, v.value);
|
current_function_->variables.Set(v.key, v.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (f->initializer) {
|
||||||
|
// Remove variables declared in the for-loop initializer from the current scope.
|
||||||
|
if (auto* decl = f->initializer->As<ast::VariableDeclStatement>()) {
|
||||||
|
current_function_->local_var_decls.Remove(sem_.Get(decl->variable));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
current_function_->RemoveLoopSwitchInfoFor(sem_loop);
|
current_function_->RemoveLoopSwitchInfoFor(sem_loop);
|
||||||
|
|
||||||
if (sem_loop->Behaviors() == sem::Behaviors{sem::Behavior::kNext}) {
|
if (sem_loop->Behaviors() == sem::Behaviors{sem::Behavior::kNext}) {
|
||||||
|
|
|
@ -1939,6 +1939,43 @@ test:12:9 note: reading from read_write storage buffer 'non_uniform' may result
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(UniformityAnalysisTest,
|
||||||
|
ForLoop_InitializerVarBecomesNonUniformBeforeConditionalContinue_BarrierAtStart) {
|
||||||
|
// Use a variable declared in a for-loop initializer for a conditional barrier in a loop, assign
|
||||||
|
// a non-uniform value to that variable later in that loop and then execute a continue.
|
||||||
|
// Tests that variables declared in the for-loop initializer are properly tracked.
|
||||||
|
std::string src = R"(
|
||||||
|
@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
|
||||||
|
|
||||||
|
fn foo() {
|
||||||
|
for (var i = 0; i < 10; i++) {
|
||||||
|
if (i < 5) {
|
||||||
|
workgroupBarrier();
|
||||||
|
}
|
||||||
|
if (true) {
|
||||||
|
i = non_uniform;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
RunTest(src, false);
|
||||||
|
EXPECT_EQ(error_,
|
||||||
|
R"(test:7:7 warning: 'workgroupBarrier' must only be called from uniform control flow
|
||||||
|
workgroupBarrier();
|
||||||
|
^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
test:5:3 note: control flow depends on non-uniform value
|
||||||
|
for (var i = 0; i < 10; i++) {
|
||||||
|
^^^
|
||||||
|
|
||||||
|
test:10:11 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
|
||||||
|
i = non_uniform;
|
||||||
|
^^^^^^^^^^^
|
||||||
|
)");
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(UniformityAnalysisTest, ForLoop_NonUniformCondition_Reconverge) {
|
TEST_F(UniformityAnalysisTest, ForLoop_NonUniformCondition_Reconverge) {
|
||||||
// Loops reconverge at exit, so test that we can call workgroupBarrier() after a loop that has a
|
// Loops reconverge at exit, so test that we can call workgroupBarrier() after a loop that has a
|
||||||
// non-uniform condition.
|
// non-uniform condition.
|
||||||
|
@ -1955,6 +1992,41 @@ fn foo() {
|
||||||
RunTest(src, true);
|
RunTest(src, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(UniformityAnalysisTest, ForLoop_VarDeclaredInBody) {
|
||||||
|
// Make sure that we can declare a variable inside the loop body without causing issues for
|
||||||
|
// tracking local variables across iterations.
|
||||||
|
std::string src = R"(
|
||||||
|
@group(0) @binding(0) var<storage, read_write> n : i32;
|
||||||
|
|
||||||
|
fn foo() {
|
||||||
|
var outer : i32;
|
||||||
|
for (var i = 0; i < n; i = i + 1) {
|
||||||
|
var inner : i32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
RunTest(src, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(UniformityAnalysisTest, ForLoop_InitializerScope) {
|
||||||
|
// Make sure that variables declared in a for-loop initializer are properly removed from the
|
||||||
|
// local variable list, otherwise a parent control-flow statement will try to add edges to nodes
|
||||||
|
// that no longer exist.
|
||||||
|
std::string src = R"(
|
||||||
|
@group(0) @binding(0) var<storage, read_write> n : i32;
|
||||||
|
|
||||||
|
fn foo() {
|
||||||
|
if (n == 5) {
|
||||||
|
for (var i = 0; i < n; i = i + 1) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
RunTest(src, true);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(UniformityAnalysisTest, While_CallInside_UniformCondition) {
|
TEST_F(UniformityAnalysisTest, While_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;
|
||||||
|
|
Loading…
Reference in New Issue