[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:
David Neto 2020-06-02 16:46:51 +00:00 committed by dan sinclair
parent 468c26b233
commit d8613596e2
3 changed files with 461 additions and 3 deletions

View File

@ -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;

View File

@ -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

View File

@ -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