[spirv-reader] Emit break-if as needed.

This CL updates the SPIRV-Reader to emit `break-if` nodes instead of
`if-break` statements.

Bug: tint:1724
Change-Id: I8cd568f5e90a950acc5a42a470345273a5f1e6bc
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/111103
Commit-Queue: Dan Sinclair <dsinclair@chromium.org>
Reviewed-by: David Neto <dneto@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
This commit is contained in:
dan sinclair 2022-11-23 01:51:24 +00:00 committed by Dawn LUCI CQ
parent b119d3b15f
commit a8bc296259
4 changed files with 152 additions and 64 deletions

View File

@ -3210,8 +3210,8 @@ bool FunctionEmitter::EmitNormalTerminator(const BlockInfo& block_info) {
return false;
}
AddStatement(create<ast::ReturnStatement>(Source{}, value.expr));
}
return true;
}
case spv::Op::OpKill:
// For now, assume SPIR-V OpKill has same semantics as WGSL discard.
// TODO(dneto): https://github.com/gpuweb/gpuweb/issues/676
@ -3266,21 +3266,58 @@ bool FunctionEmitter::EmitNormalTerminator(const BlockInfo& block_info) {
return Fail() << "Fallthrough not supported in WGSL";
}
// In the case of a continuing block a `break-if` needs to be emitted for either an
// if-break or an if-else-break statement. This only happens inside the continue block.
// It's possible for a continue block to also be the loop block, so checks are needed
// that this is a continue construct and the header construct will cause a continuing
// construct to be emitted. (i.e. the header is not `continue is entire loop`.
bool needs_break_if = false;
if ((true_kind == EdgeKind::kLoopBreak || false_kind == EdgeKind::kLoopBreak) &&
block_info.construct && block_info.construct->kind == Construct::Kind::kContinue) {
auto* header = GetBlockInfo(block_info.construct->begin_id);
TINT_ASSERT(Reader, header->construct &&
header->construct->kind == Construct::Kind::kContinue);
if (!header->is_continue_entire_loop) {
needs_break_if = true;
}
}
// At this point, at most one edge is kForward or kIfBreak.
// Emit an 'if' statement to express the *other* branch as a conditional
// break or continue. Either or both of these could be nullptr.
// (A nullptr is generated for kIfBreak, kForward, or kBack.)
// Also if one of the branches is an if-break out of an if-selection
// requiring a flow guard, then get that flow guard name too. It will
// come from at most one of these two branches.
std::string flow_guard;
auto* true_branch = MakeBranchDetailed(block_info, *true_info, &flow_guard);
auto* false_branch = MakeBranchDetailed(block_info, *false_info, &flow_guard);
// If this is a continuing block and a `break` is to be emitted, then this needs to be
// converted to a `break-if`. This may involve inverting the condition if this was a
// `break-unless`.
if (needs_break_if) {
if (true_kind == EdgeKind::kLoopBreak && false_kind == EdgeKind::kLoopBreak) {
// Both branches break ... ?
return Fail() << "Both branches of if inside continuing break.";
}
AddStatement(MakeSimpleIf(cond, true_branch, false_branch));
if (!flow_guard.empty()) {
PushGuard(flow_guard, statements_stack_.Back().GetEndId());
if (true_kind == EdgeKind::kLoopBreak) {
AddStatement(create<ast::BreakIfStatement>(Source{}, cond));
} else {
AddStatement(create<ast::BreakIfStatement>(
Source{},
create<ast::UnaryOpExpression>(Source{}, ast::UnaryOp::kNot, cond)));
}
return true;
} else {
// Emit an 'if' statement to express the *other* branch as a conditional
// break or continue. Either or both of these could be nullptr.
// (A nullptr is generated for kIfBreak, kForward, or kBack.)
// Also if one of the branches is an if-break out of an if-selection
// requiring a flow guard, then get that flow guard name too. It will
// come from at most one of these two branches.
std::string flow_guard;
auto* true_branch = MakeBranchDetailed(block_info, *true_info, &flow_guard);
auto* false_branch = MakeBranchDetailed(block_info, *false_info, &flow_guard);
AddStatement(MakeSimpleIf(cond, true_branch, false_branch));
if (!flow_guard.empty()) {
PushGuard(flow_guard, statements_stack_.Back().GetEndId());
}
}
return true;
}

View File

@ -638,9 +638,8 @@ TEST_F(SpvParserCFGTest, RegisterMerges_GoodLoopMerge_MultiBlockLoop_ContinueIsN
EXPECT_FALSE(bi99->is_continue_entire_loop);
}
TEST_F(
SpvParserCFGTest,
RegisterMerges_GoodLoopMerge_MultiBlockLoop_ContinueIsNotHeader_BranchConditional) { // NOLINT
TEST_F(SpvParserCFGTest,
RegisterMerges_GoodLoopMerge_MultiBlockLoop_ContinueIsNotHeader_BranchConditional) {
auto p = parser(test::Assemble(CommonTypes() + R"(
%100 = OpFunction %void None %voidfn
@ -2605,7 +2604,7 @@ TEST_F(SpvParserCFGTest, VerifyHeaderContinueMergeOrder_HeaderDoesNotStrictlyDom
}
TEST_F(SpvParserCFGTest,
VerifyHeaderContinueMergeOrder_HeaderDoesNotStrictlyDominateContinueTarget) { // NOLINT
VerifyHeaderContinueMergeOrder_HeaderDoesNotStrictlyDominateContinueTarget) {
auto assembly = CommonTypes() + R"(
%100 = OpFunction %void None %voidfn
@ -4451,9 +4450,8 @@ TEST_F(SpvParserCFGTest, ClassifyCFGEdges_BackEdge_MultiBlockLoop_SingleBlockCon
EXPECT_EQ(bi40->succ_edge[20], EdgeKind::kBack);
}
TEST_F(
SpvParserCFGTest,
ClassifyCFGEdges_BackEdge_MultiBlockLoop_MultiBlockContinueConstruct_ContinueIsNotHeader) { // NOLINT
TEST_F(SpvParserCFGTest,
ClassifyCFGEdges_BackEdge_MultiBlockLoop_MultiBlockContinueConstruct_ContinueIsNotHeader) {
auto assembly = CommonTypes() + R"(
%100 = OpFunction %void None %voidfn
@ -4489,9 +4487,8 @@ TEST_F(
EXPECT_EQ(bi50->succ_edge[20], EdgeKind::kBack);
}
TEST_F(
SpvParserCFGTest,
ClassifyCFGEdges_BackEdge_MultiBlockLoop_MultiBlockContinueConstruct_ContinueIsHeader) { // NOLINT
TEST_F(SpvParserCFGTest,
ClassifyCFGEdges_BackEdge_MultiBlockLoop_MultiBlockContinueConstruct_ContinueIsHeader) {
auto assembly = CommonTypes() + R"(
%100 = OpFunction %void None %voidfn
@ -6051,7 +6048,7 @@ TEST_F(SpvParserCFGTest, ClassifyCFGEdges_DomViolation_AfterContinueToContinueIn
}
TEST_F(SpvParserCFGTest,
FindSwitchCaseHeaders_DomViolation_SwitchCase_CantBeMergeForOtherConstruct) { // NOLINT
FindSwitchCaseHeaders_DomViolation_SwitchCase_CantBeMergeForOtherConstruct) {
auto assembly = CommonTypes() + R"(
%100 = OpFunction %void None %voidfn
@ -6762,8 +6759,7 @@ TEST_F(SpvParserCFGTest, ClassifyCFGEdges_IfBreak_WithForwardToPremerge) {
}
TEST_F(SpvParserCFGTest,
FindIfSelectionInternalHeaders_DomViolation_InteriorMerge_CantBeTrueHeader) { // NOLINT -
// line length
FindIfSelectionInternalHeaders_DomViolation_InteriorMerge_CantBeTrueHeader) {
auto assembly = CommonTypes() + R"(
%100 = OpFunction %void None %voidfn
@ -6795,10 +6791,8 @@ TEST_F(SpvParserCFGTest,
"merge block for header block 20 (violates dominance rule)"));
}
TEST_F(
SpvParserCFGTest,
FindIfSelectionInternalHeaders_DomViolation_InteriorMerge_CantBeFalseHeader) { // NOLINT - line
// length
TEST_F(SpvParserCFGTest,
FindIfSelectionInternalHeaders_DomViolation_InteriorMerge_CantBeFalseHeader) {
auto assembly = CommonTypes() + R"(
%100 = OpFunction %void None %voidfn
@ -9474,9 +9468,7 @@ TEST_F(SpvParserCFGTest,
}
TEST_F(SpvParserCFGTest,
EmitBody_Branch_LoopBreak_MultiBlockLoop_FromContinueConstructEnd_Unconditional) { // NOLINT
// - line
// length
EmitBody_Branch_LoopBreak_MultiBlockLoop_FromContinueConstructEnd_Unconditional) {
auto p = parser(test::Assemble(CommonTypes() + R"(
%100 = OpFunction %void None %voidfn
@ -9519,10 +9511,46 @@ return;
ASSERT_EQ(expect, got);
}
TEST_F(
SpvParserCFGTest,
EmitBody_Branch_LoopBreak_MultiBlockLoop_FromContinueConstructEnd_Conditional) { // NOLINT -
// line length
TEST_F(SpvParserCFGTest,
EmitBody_Branch_LoopBreak_MultiBlockLoop_FromContinueConstructEnd_Conditional_BreakIf) {
auto p = parser(test::Assemble(CommonTypes() + R"(
%100 = OpFunction %void None %voidfn
%10 = OpLabel
OpBranch %20
%20 = OpLabel
OpLoopMerge %99 %80 None
OpBranch %80
%80 = OpLabel ; continue target
OpStore %var %uint_1
OpBranchConditional %cond %99 %20 ; exit, and backedge
%99 = OpLabel
OpReturn
OpFunctionEnd
)"));
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
auto fe = p->function_emitter(100);
EXPECT_TRUE(fe.EmitBody()) << p->error();
auto ast_body = fe.ast_body();
auto got = test::ToString(p->program(), ast_body);
auto* expect = R"(loop {
continuing {
var_1 = 1u;
break if false;
}
}
return;
)";
ASSERT_EQ(expect, got);
}
TEST_F(SpvParserCFGTest,
EmitBody_Branch_LoopBreak_MultiBlockLoop_FromContinueConstructEnd_Conditional) {
auto p = parser(test::Assemble(CommonTypes() + R"(
%100 = OpFunction %void None %voidfn
@ -9551,10 +9579,48 @@ TEST_F(
continuing {
var_1 = 1u;
if (false) {
} else {
break;
}
break if !(false);
}
}
return;
)";
ASSERT_EQ(expect, got);
}
TEST_F(SpvParserCFGTest, EmitBody_Branch_LoopBreak_FromContinueConstructTail) {
auto p = parser(test::Assemble(CommonTypes() + R"(
%100 = OpFunction %void None %voidfn
%10 = OpLabel
OpBranch %20
%20 = OpLabel
OpLoopMerge %99 %50 None
OpBranchConditional %cond %30 %99
%30 = OpLabel
OpBranch %50
%50 = OpLabel
OpBranch %60
%60 = OpLabel
OpBranchConditional %cond %20 %99
%99 = OpLabel
OpReturn
OpFunctionEnd
)"));
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
auto fe = p->function_emitter(100);
EXPECT_TRUE(fe.EmitBody()) << p->error();
auto ast_body = fe.ast_body();
auto got = test::ToString(p->program(), ast_body);
auto* expect = R"(loop {
if (false) {
} else {
break;
}
continuing {
break if !(false);
}
}
return;
@ -10009,9 +10075,7 @@ loop {
var_1 = 1u;
continuing {
if (false) {
break;
}
break if false;
}
}
var_1 = 5u;
@ -10052,10 +10116,7 @@ loop {
var_1 = 1u;
continuing {
if (false) {
} else {
break;
}
break if !(false);
}
}
var_1 = 5u;
@ -10878,9 +10939,8 @@ return;
ASSERT_EQ(expect, got);
}
TEST_F(
SpvParserCFGTest,
EmitBody_BranchConditional_Continue_Continue_AfterHeader_Conditional_EmptyContinuing) { // NOLINT
TEST_F(SpvParserCFGTest,
EmitBody_BranchConditional_Continue_Continue_AfterHeader_Conditional_EmptyContinuing) {
// Like the previous tests, but with an empty continuing clause.
auto p = parser(test::Assemble(CommonTypes() + R"(
%100 = OpFunction %void None %voidfn

View File

@ -689,9 +689,7 @@ loop {
continuing {
x_1 = 4u;
if (false) {
break;
}
break if false;
}
}
x_1 = 5u;
@ -1610,9 +1608,7 @@ OpFunctionEnd
x_999 = false;
continuing {
if (true) {
break;
}
break if true;
}
}
}
@ -1694,9 +1690,7 @@ loop {
x_999 = false;
continuing {
if (true) {
break;
}
break if true;
}
}

View File

@ -103,10 +103,7 @@ fn insert_i1_i1_(treeIndex : ptr<function, i32>, data_1 : ptr<function, i32>) {
continuing {
let x_382 : f32 = x_27.injectionSwitch.x;
let x_384 : f32 = x_27.injectionSwitch.y;
if ((x_382 > x_384)) {
} else {
break;
}
break if !(x_382 > x_384);
}
}
continue;