diff --git a/src/tint/resolver/uniformity.cc b/src/tint/resolver/uniformity.cc index eb62c0ce2c..831e49e0a3 100644 --- a/src/tint/resolver/uniformity.cc +++ b/src/tint/resolver/uniformity.cc @@ -72,7 +72,6 @@ enum CallSiteTag { /// FunctionTag describes a functions effects on uniformity. enum FunctionTag { - SubsequentControlFlowMayBeNonUniform, ReturnValueMayBeNonUniform, NoRestriction, }; @@ -80,7 +79,6 @@ enum FunctionTag { /// ParameterTag describes the uniformity requirements of values passed to a function parameter. enum ParameterTag { ParameterRequiredToBeUniform, - ParameterRequiredToBeUniformForSubsequentControlFlow, ParameterRequiredToBeUniformForReturnValue, ParameterNoRestriction, }; @@ -163,7 +161,6 @@ struct FunctionInfo { 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 @@ struct FunctionInfo { 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 @@ class UniformityGraph { // 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 @@ class UniformityGraph { } } - // Look at which nodes are reachable from "CF_return" - { - utils::UniqueVector 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 reachable; @@ -918,12 +893,10 @@ class UniformityGraph { 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 @@ class UniformityGraph { } 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 @@ class UniformityGraph { 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 @@ class UniformityGraph { auto* control_flow = TraceBackAlongPathUntil( non_uniform_source, [](Node* node) { return node->affects_control_flow; }); if (control_flow) { - if (auto* call = control_flow->ast->As()) { - 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()) { - 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 @@ class UniformityGraph { auto target_name = builder_->Symbols().NameFor( c->target.name->As()->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()) { - 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 5cf475c372..faefb13d26 100644 --- a/src/tint/resolver/uniformity_test.cc +++ b/src/tint/resolver/uniformity_test.cc @@ -338,134 +338,6 @@ INSTANTIATE_TEST_SUITE_P( /// 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 rw : i32; - -var 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 rw : i32; - -var 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 rw : i32; - -var 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: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 ro : i32; - -var 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 rw : i32; - -var 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 @@ fn foo() { 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 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 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 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 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 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 @@ fn foo() { 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 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 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 @@ fn foo() { /// 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 rw : i32; + +var 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: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 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 non_uniform_global : i32; - -var 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 non_uniform_global : i32; - -var 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 non_uniform_global : i32; - -var 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 non_uniform_global : i32; -var 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 @@ TEST_F(UniformityAnalysisTest, ShortCircuiting_ReconvergeRHS) { std::string src = R"( @group(0) @binding(0) var non_uniform_global : i32; -var 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 @@ TEST_F(UniformityAnalysisTest, ShortCircuiting_ReconvergeBoth) { std::string src = R"( @group(0) @binding(0) var non_uniform_global : i32; -var 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: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_value : i32; -@group(0) @binding(1) var 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_value : i32; -@group(0) @binding(1) var 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 non_uniform_value : vec4; - -fn foo(limit : ptr) -> 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()); -} -)"; - - RunTest(src, false); - EXPECT_EQ(error_, - R"(test:15:11 warning: 'dpdx' must only be called from uniform control flow - let y = dpdx(vec3()); - ^^^^ - -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 non_uniform_value : vec4; - -fn foo(limit : ptr) -> 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()); -} -)"; - - RunTest(src, false); - EXPECT_EQ(error_, - R"(test:16:11 warning: 'dpdx' must only be called from uniform control flow - let y = dpdx(vec3()); - ^^^^ - -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) -> 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_value : i32; -@group(0) @binding(1) var 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