[spirv-reader] Emit loop and continuing
Bug: tint:3 Change-Id: Iaced5ee41f6b27ab350432fc1c2cdff6042ba191 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/22423 Reviewed-by: dan sinclair <dsinclair@google.com>
This commit is contained in:
parent
468c26b233
commit
d8613596e2
|
@ -1556,7 +1556,9 @@ bool FunctionEmitter::EmitBasicBlock(const BlockInfo& block_info) {
|
|||
auto outer_kind = entering_constructs[1]->kind;
|
||||
if (outer_kind != Construct::kContinue) {
|
||||
return Fail() << "internal error: bad construct nesting. Only Continue "
|
||||
"construct can be outer construct on same block";
|
||||
"construct can be outer construct on same block. Got "
|
||||
"outer kind "
|
||||
<< int(outer_kind) << " inner kind " << int(inner_kind);
|
||||
}
|
||||
if (inner_kind == Construct::kContinue) {
|
||||
return Fail() << "internal error: unsupported construct nesting: "
|
||||
|
@ -1588,10 +1590,28 @@ bool FunctionEmitter::EmitBasicBlock(const BlockInfo& block_info) {
|
|||
return Fail() << "internal error: nested function construct";
|
||||
|
||||
case Construct::kLoop:
|
||||
return Fail() << "unhandled: loop construct";
|
||||
if (!EmitLoopStart(construct)) {
|
||||
return false;
|
||||
}
|
||||
if (!EmitStatementsInBasicBlock(block_info, &emitted)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case Construct::kContinue:
|
||||
return Fail() << "unhandled: continue construct";
|
||||
if (block_info.is_single_block_loop) {
|
||||
if (!EmitLoopStart(construct)) {
|
||||
return false;
|
||||
}
|
||||
if (!EmitStatementsInBasicBlock(block_info, &emitted)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!EmitContinuingStart(construct)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Construct::kIfSelection:
|
||||
if (!EmitStatementsInBasicBlock(block_info, &emitted)) {
|
||||
|
@ -1709,6 +1729,30 @@ bool FunctionEmitter::EmitIfStart(const BlockInfo& block_info) {
|
|||
return success();
|
||||
}
|
||||
|
||||
bool FunctionEmitter::EmitLoopStart(const Construct* construct) {
|
||||
auto* loop = AddStatement(std::make_unique<ast::LoopStatement>())->AsLoop();
|
||||
PushNewStatementBlock(
|
||||
construct, construct->end_id,
|
||||
[loop](StatementBlock* s) { loop->set_body(std::move(s->statements)); });
|
||||
return success();
|
||||
}
|
||||
|
||||
bool FunctionEmitter::EmitContinuingStart(const Construct* construct) {
|
||||
// A continue construct has the same depth as its associated loop
|
||||
// construct. Start a continue construct.
|
||||
auto* loop_candidate = LastStatement();
|
||||
if (!loop_candidate->IsLoop()) {
|
||||
return Fail() << "internal error: starting continue construct, "
|
||||
"expected loop on top of stack";
|
||||
}
|
||||
auto* loop = loop_candidate->AsLoop();
|
||||
PushNewStatementBlock(construct, construct->end_id,
|
||||
[loop](StatementBlock* s) {
|
||||
loop->set_continuing(std::move(s->statements));
|
||||
});
|
||||
return success();
|
||||
}
|
||||
|
||||
bool FunctionEmitter::EmitNormalTerminator(const BlockInfo&) {
|
||||
// TODO(dneto): emit fallthrough, break, continue, return, kill
|
||||
return true;
|
||||
|
|
|
@ -297,6 +297,20 @@ class FunctionEmitter {
|
|||
/// @returns false if emission failed.
|
||||
bool EmitIfStart(const BlockInfo& block_info);
|
||||
|
||||
/// Emits a LoopStatement, and pushes a new StatementBlock to accumulate
|
||||
/// the remaining instructions in the current block and subsequent blocks
|
||||
/// in the loop.
|
||||
/// @param construct the loop construct
|
||||
/// @returns false if emission failed.
|
||||
bool EmitLoopStart(const Construct* construct);
|
||||
|
||||
/// Emits a ContinuingStatement, and pushes a new StatementBlock to accumulate
|
||||
/// the remaining instructions in the current block and subsequent blocks
|
||||
/// in the continue construct.
|
||||
/// @param construct the continue construct
|
||||
/// @returns false if emission failed.
|
||||
bool EmitContinuingStart(const Construct* construct);
|
||||
|
||||
/// Emits the non-control-flow parts of a basic block, but only once.
|
||||
/// The |already_emitted| parameter indicates whether the code has already
|
||||
/// been emitted, and is used to signal that this invocation actually emitted
|
||||
|
|
|
@ -7226,6 +7226,406 @@ Assignment{
|
|||
)"));
|
||||
}
|
||||
|
||||
TEST_F(SpvParserTest, EmitBody_Loop_SingleBlock_TrueBackedge) {
|
||||
// TODO(dneto): emit conditional break
|
||||
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
||||
%100 = OpFunction %void None %voidfn
|
||||
|
||||
%10 = OpLabel
|
||||
OpStore %var %uint_0
|
||||
OpBranch %20
|
||||
|
||||
%20 = OpLabel
|
||||
OpStore %var %uint_1
|
||||
OpLoopMerge %99 %20 None
|
||||
OpBranchConditional %cond %20 %99
|
||||
|
||||
%99 = OpLabel
|
||||
OpStore %var %999
|
||||
OpReturn
|
||||
|
||||
OpFunctionEnd
|
||||
)"));
|
||||
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
||||
FunctionEmitter fe(p, *spirv_function(100));
|
||||
EXPECT_TRUE(fe.EmitBody()) << p->error();
|
||||
|
||||
EXPECT_THAT(ToString(fe.ast_body()), Eq(R"(Assignment{
|
||||
Identifier{var}
|
||||
ScalarConstructor{0}
|
||||
}
|
||||
Loop{
|
||||
Assignment{
|
||||
Identifier{var}
|
||||
ScalarConstructor{1}
|
||||
}
|
||||
}
|
||||
Assignment{
|
||||
Identifier{var}
|
||||
ScalarConstructor{999}
|
||||
}
|
||||
)"));
|
||||
}
|
||||
|
||||
TEST_F(SpvParserTest, DISABLED_EmitBody_Loop_SingleBlock_FalseBackedge) {
|
||||
// TODO(dneto): emit conditional break
|
||||
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
||||
%100 = OpFunction %void None %voidfn
|
||||
|
||||
%10 = OpLabel
|
||||
OpStore %var %uint_0
|
||||
OpBranch %20
|
||||
|
||||
%20 = OpLabel
|
||||
OpStore %var %uint_1
|
||||
OpLoopMerge %99 %20 None
|
||||
OpBranchConditional %cond %99 %20
|
||||
|
||||
%99 = OpLabel
|
||||
OpStore %var %999
|
||||
OpReturn
|
||||
|
||||
OpFunctionEnd
|
||||
)"));
|
||||
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
||||
FunctionEmitter fe(p, *spirv_function(100));
|
||||
EXPECT_TRUE(fe.EmitBody()) << p->error();
|
||||
|
||||
EXPECT_THAT(ToString(fe.ast_body()), Eq(R"(Assignment{
|
||||
Identifier{var}
|
||||
ScalarConstructor{0}
|
||||
}
|
||||
Loop{
|
||||
Assignment{
|
||||
Identifier{var}
|
||||
ScalarConstructor{1}
|
||||
}
|
||||
}
|
||||
Assignment{
|
||||
Identifier{var}
|
||||
ScalarConstructor{999}
|
||||
}
|
||||
)"));
|
||||
}
|
||||
|
||||
TEST_F(SpvParserTest, EmitBody_Loop_SingleBlock_BothBackedge) {
|
||||
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
||||
%100 = OpFunction %void None %voidfn
|
||||
|
||||
%10 = OpLabel
|
||||
OpStore %var %uint_0
|
||||
OpBranch %20
|
||||
|
||||
%20 = OpLabel
|
||||
OpStore %var %uint_1
|
||||
OpLoopMerge %99 %20 None
|
||||
OpBranchConditional %cond %20 %20
|
||||
|
||||
%99 = OpLabel
|
||||
OpStore %var %999
|
||||
OpReturn
|
||||
|
||||
OpFunctionEnd
|
||||
)"));
|
||||
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
||||
FunctionEmitter fe(p, *spirv_function(100));
|
||||
EXPECT_TRUE(fe.EmitBody()) << p->error();
|
||||
|
||||
EXPECT_THAT(ToString(fe.ast_body()), Eq(R"(Assignment{
|
||||
Identifier{var}
|
||||
ScalarConstructor{0}
|
||||
}
|
||||
Loop{
|
||||
Assignment{
|
||||
Identifier{var}
|
||||
ScalarConstructor{1}
|
||||
}
|
||||
}
|
||||
Assignment{
|
||||
Identifier{var}
|
||||
ScalarConstructor{999}
|
||||
}
|
||||
)"));
|
||||
}
|
||||
|
||||
TEST_F(SpvParserTest, EmitBody_Loop_SingleBlock_UnconditionalBackege) {
|
||||
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
||||
%100 = OpFunction %void None %voidfn
|
||||
|
||||
%10 = OpLabel
|
||||
OpStore %var %uint_0
|
||||
OpBranch %20
|
||||
|
||||
%20 = OpLabel
|
||||
OpStore %var %uint_1
|
||||
OpLoopMerge %99 %20 None
|
||||
OpBranch %20
|
||||
|
||||
%99 = OpLabel
|
||||
OpStore %var %999
|
||||
OpReturn
|
||||
|
||||
OpFunctionEnd
|
||||
)"));
|
||||
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
||||
FunctionEmitter fe(p, *spirv_function(100));
|
||||
EXPECT_TRUE(fe.EmitBody()) << p->error();
|
||||
|
||||
EXPECT_THAT(ToString(fe.ast_body()), Eq(R"(Assignment{
|
||||
Identifier{var}
|
||||
ScalarConstructor{0}
|
||||
}
|
||||
Loop{
|
||||
Assignment{
|
||||
Identifier{var}
|
||||
ScalarConstructor{1}
|
||||
}
|
||||
}
|
||||
Assignment{
|
||||
Identifier{var}
|
||||
ScalarConstructor{999}
|
||||
}
|
||||
)"));
|
||||
}
|
||||
|
||||
TEST_F(SpvParserTest, EmitBody_Loop_Unconditional_Body_SingleBlockContinue) {
|
||||
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
||||
%100 = OpFunction %void None %voidfn
|
||||
|
||||
%10 = OpLabel
|
||||
OpStore %var %uint_0
|
||||
OpBranch %20
|
||||
|
||||
%20 = OpLabel
|
||||
OpStore %var %uint_1
|
||||
OpLoopMerge %99 %50 None
|
||||
OpBranch %30
|
||||
|
||||
%30 = OpLabel
|
||||
OpStore %var %uint_2
|
||||
OpBranch %50
|
||||
|
||||
%50 = OpLabel
|
||||
OpStore %var %uint_3
|
||||
OpBranch %20
|
||||
|
||||
%99 = OpLabel
|
||||
OpStore %var %999
|
||||
OpReturn
|
||||
|
||||
OpFunctionEnd
|
||||
)"));
|
||||
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
||||
FunctionEmitter fe(p, *spirv_function(100));
|
||||
EXPECT_TRUE(fe.EmitBody()) << p->error();
|
||||
|
||||
EXPECT_THAT(ToString(fe.ast_body()), Eq(R"(Assignment{
|
||||
Identifier{var}
|
||||
ScalarConstructor{0}
|
||||
}
|
||||
Loop{
|
||||
Assignment{
|
||||
Identifier{var}
|
||||
ScalarConstructor{1}
|
||||
}
|
||||
Assignment{
|
||||
Identifier{var}
|
||||
ScalarConstructor{2}
|
||||
}
|
||||
continuing {
|
||||
Assignment{
|
||||
Identifier{var}
|
||||
ScalarConstructor{3}
|
||||
}
|
||||
}
|
||||
}
|
||||
Assignment{
|
||||
Identifier{var}
|
||||
ScalarConstructor{999}
|
||||
}
|
||||
)")) << ToString(fe.ast_body());
|
||||
}
|
||||
|
||||
TEST_F(SpvParserTest, EmitBody_Loop_Unconditional_Body_MultiBlockContinue) {
|
||||
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
||||
%100 = OpFunction %void None %voidfn
|
||||
|
||||
%10 = OpLabel
|
||||
OpStore %var %uint_0
|
||||
OpBranch %20
|
||||
|
||||
%20 = OpLabel
|
||||
OpStore %var %uint_1
|
||||
OpLoopMerge %99 %50 None
|
||||
OpBranch %30
|
||||
|
||||
%30 = OpLabel
|
||||
OpStore %var %uint_2
|
||||
OpBranch %50
|
||||
|
||||
%50 = OpLabel
|
||||
OpStore %var %uint_3
|
||||
OpBranch %60
|
||||
|
||||
%60 = OpLabel
|
||||
OpStore %var %uint_4
|
||||
OpBranch %20
|
||||
|
||||
%99 = OpLabel
|
||||
OpStore %var %999
|
||||
OpReturn
|
||||
|
||||
OpFunctionEnd
|
||||
)"));
|
||||
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
||||
FunctionEmitter fe(p, *spirv_function(100));
|
||||
EXPECT_TRUE(fe.EmitBody()) << p->error();
|
||||
|
||||
EXPECT_THAT(ToString(fe.ast_body()), Eq(R"(Assignment{
|
||||
Identifier{var}
|
||||
ScalarConstructor{0}
|
||||
}
|
||||
Loop{
|
||||
Assignment{
|
||||
Identifier{var}
|
||||
ScalarConstructor{1}
|
||||
}
|
||||
Assignment{
|
||||
Identifier{var}
|
||||
ScalarConstructor{2}
|
||||
}
|
||||
continuing {
|
||||
Assignment{
|
||||
Identifier{var}
|
||||
ScalarConstructor{3}
|
||||
}
|
||||
Assignment{
|
||||
Identifier{var}
|
||||
ScalarConstructor{4}
|
||||
}
|
||||
}
|
||||
}
|
||||
Assignment{
|
||||
Identifier{var}
|
||||
ScalarConstructor{999}
|
||||
}
|
||||
)")) << ToString(fe.ast_body());
|
||||
}
|
||||
|
||||
TEST_F(SpvParserTest, EmitBody_Loop_Unconditional_Body_ContinueNestIf) {
|
||||
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
||||
%100 = OpFunction %void None %voidfn
|
||||
|
||||
%10 = OpLabel
|
||||
OpStore %var %uint_0
|
||||
OpBranch %20
|
||||
|
||||
%20 = OpLabel
|
||||
OpStore %var %uint_1
|
||||
OpLoopMerge %99 %50 None
|
||||
OpBranch %30
|
||||
|
||||
%30 = OpLabel
|
||||
OpStore %var %uint_2
|
||||
OpBranch %50
|
||||
|
||||
%50 = OpLabel ; continue target; also if-header
|
||||
OpStore %var %uint_3
|
||||
OpSelectionMerge %80 None
|
||||
OpBranchConditional %cond2 %60 %80
|
||||
|
||||
%60 = OpLabel
|
||||
OpStore %var %uint_4
|
||||
OpBranch %80
|
||||
|
||||
%80 = OpLabel
|
||||
OpStore %var %uint_5
|
||||
OpBranch %20
|
||||
|
||||
%99 = OpLabel
|
||||
OpStore %var %999
|
||||
OpReturn
|
||||
|
||||
OpFunctionEnd
|
||||
)"));
|
||||
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
||||
FunctionEmitter fe(p, *spirv_function(100));
|
||||
EXPECT_TRUE(fe.EmitBody()) << p->error();
|
||||
|
||||
EXPECT_THAT(ToString(fe.ast_body()), Eq(R"(Assignment{
|
||||
Identifier{var}
|
||||
ScalarConstructor{0}
|
||||
}
|
||||
Loop{
|
||||
Assignment{
|
||||
Identifier{var}
|
||||
ScalarConstructor{1}
|
||||
}
|
||||
Assignment{
|
||||
Identifier{var}
|
||||
ScalarConstructor{2}
|
||||
}
|
||||
continuing {
|
||||
Assignment{
|
||||
Identifier{var}
|
||||
ScalarConstructor{3}
|
||||
}
|
||||
If{
|
||||
(
|
||||
ScalarConstructor{true}
|
||||
)
|
||||
{
|
||||
Assignment{
|
||||
Identifier{var}
|
||||
ScalarConstructor{4}
|
||||
}
|
||||
}
|
||||
}
|
||||
Else{
|
||||
{
|
||||
}
|
||||
}
|
||||
Assignment{
|
||||
Identifier{var}
|
||||
ScalarConstructor{5}
|
||||
}
|
||||
}
|
||||
}
|
||||
Assignment{
|
||||
Identifier{var}
|
||||
ScalarConstructor{999}
|
||||
}
|
||||
)")) << ToString(fe.ast_body());
|
||||
}
|
||||
|
||||
TEST_F(SpvParserTest, DISABLED_EmitBody_Loop_Never) {
|
||||
// Test case where both branches exit. e.g both go to merge.
|
||||
}
|
||||
|
||||
TEST_F(SpvParserTest, DISABLED_EmitBody_Loop_HeaderBreakAndContinue) {
|
||||
// Header block branches to merge, and to an outer continue.
|
||||
}
|
||||
|
||||
TEST_F(SpvParserTest, DISABLED_EmitBody_Loop_TrueToBody) {
|
||||
// TODO(dneto): Needs break-unless
|
||||
}
|
||||
|
||||
TEST_F(SpvParserTest, DISABLED_EmitBody_Loop_FalseToBody) {
|
||||
// TODO(dneto): Needs break-if
|
||||
}
|
||||
|
||||
TEST_F(SpvParserTest, DISABLED_EmitBody_Loop_NestedIfContinue) {
|
||||
// TODO(dneto): Needs "continue" terminator support
|
||||
}
|
||||
|
||||
TEST_F(SpvParserTest, DISABLED_EmitBody_Loop_BodyAlwaysBreaks) {
|
||||
// TODO(dneto): Needs "continue" terminator support
|
||||
}
|
||||
|
||||
TEST_F(SpvParserTest, DISABLED_EmitBody_Loop_BodyConditionallyBreaks) {
|
||||
// TODO(dneto): Needs "break" support
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace spirv
|
||||
} // namespace reader
|
||||
|
|
Loading…
Reference in New Issue