[spirv-reader]: Support OpBranch
Bug: tint:3 Change-Id: I39d03f4fc29c7b60dc09d0bafc3afaec754671a0 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/22425 Reviewed-by: dan sinclair <dsinclair@google.com>
This commit is contained in:
parent
e76f4dac06
commit
27d42ede4e
|
@ -27,7 +27,10 @@
|
||||||
#include "src/ast/as_expression.h"
|
#include "src/ast/as_expression.h"
|
||||||
#include "src/ast/assignment_statement.h"
|
#include "src/ast/assignment_statement.h"
|
||||||
#include "src/ast/binary_expression.h"
|
#include "src/ast/binary_expression.h"
|
||||||
|
#include "src/ast/break_statement.h"
|
||||||
|
#include "src/ast/continue_statement.h"
|
||||||
#include "src/ast/else_statement.h"
|
#include "src/ast/else_statement.h"
|
||||||
|
#include "src/ast/fallthrough_statement.h"
|
||||||
#include "src/ast/identifier_expression.h"
|
#include "src/ast/identifier_expression.h"
|
||||||
#include "src/ast/if_statement.h"
|
#include "src/ast/if_statement.h"
|
||||||
#include "src/ast/kill_statement.h"
|
#include "src/ast/kill_statement.h"
|
||||||
|
@ -1786,6 +1789,39 @@ bool FunctionEmitter::EmitNormalTerminator(const BlockInfo& block_info) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
case SpvOpBranch: {
|
||||||
|
const auto dest = terminator.GetSingleWordInOperand(0);
|
||||||
|
const auto kind = block_info.succ_edge.find(dest)->second;
|
||||||
|
switch (kind) {
|
||||||
|
case EdgeKind::kBack:
|
||||||
|
// Nothing to do. The loop backedge is implicit.
|
||||||
|
return true;
|
||||||
|
case EdgeKind::kSwitchBreak:
|
||||||
|
case EdgeKind::kLoopBreak:
|
||||||
|
AddStatement(std::make_unique<ast::BreakStatement>());
|
||||||
|
return true;
|
||||||
|
case EdgeKind::kLoopContinue:
|
||||||
|
// An unconditional continue to the next block is redundant and ugly.
|
||||||
|
// Skip it in that case.
|
||||||
|
if (GetBlockInfo(dest)->pos == 1 + block_info.pos) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Otherwise, emit a regular continue statement.
|
||||||
|
AddStatement(std::make_unique<ast::ContinueStatement>());
|
||||||
|
return true;
|
||||||
|
case EdgeKind::kIfBreak:
|
||||||
|
// For an unconditional branch, the break out to an if-selection
|
||||||
|
// merge block is implicit.
|
||||||
|
return true;
|
||||||
|
case EdgeKind::kCaseFallThrough:
|
||||||
|
AddStatement(std::make_unique<ast::FallthroughStatement>());
|
||||||
|
return true;
|
||||||
|
case EdgeKind::kForward:
|
||||||
|
// Unconditional forward branch is implicit.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1842,8 +1878,8 @@ bool FunctionEmitter::EmitStatement(const spvtools::opt::Instruction& inst) {
|
||||||
auto combinatorial_expr = MaybeEmitCombinatorialValue(inst);
|
auto combinatorial_expr = MaybeEmitCombinatorialValue(inst);
|
||||||
if (combinatorial_expr.expr != nullptr) {
|
if (combinatorial_expr.expr != nullptr) {
|
||||||
if (def_use_mgr_->NumUses(&inst) == 1) {
|
if (def_use_mgr_->NumUses(&inst) == 1) {
|
||||||
// If it's used once, then defer emitting the expression until it's used.
|
// If it's used once, then defer emitting the expression until it's
|
||||||
// Any supporting statements have already been emitted.
|
// used. Any supporting statements have already been emitted.
|
||||||
singly_used_values_.insert(
|
singly_used_values_.insert(
|
||||||
std::make_pair(inst.result_id(), std::move(combinatorial_expr)));
|
std::make_pair(inst.result_id(), std::move(combinatorial_expr)));
|
||||||
return success();
|
return success();
|
||||||
|
@ -1984,10 +2020,9 @@ TypedExpression FunctionEmitter::MakeAccessChain(
|
||||||
}
|
}
|
||||||
|
|
||||||
// A SPIR-V access chain is a single instruction with multiple indices
|
// A SPIR-V access chain is a single instruction with multiple indices
|
||||||
// walking down into composites. The Tint AST represents this as ever-deeper
|
// walking down into composites. The Tint AST represents this as
|
||||||
// nested indexing expresions.
|
// ever-deeper nested indexing expresions. Start off with an expression
|
||||||
// Start off with an expression for the base, and then bury that inside
|
// for the base, and then bury that inside nested indexing expressions.
|
||||||
// nested indexing expressions.
|
|
||||||
TypedExpression current_expr(MakeOperand(inst, 0));
|
TypedExpression current_expr(MakeOperand(inst, 0));
|
||||||
|
|
||||||
const auto constants = constant_mgr_->GetOperandConstants(&inst);
|
const auto constants = constant_mgr_->GetOperandConstants(&inst);
|
||||||
|
|
|
@ -7631,12 +7631,117 @@ TEST_F(SpvParserTest, DISABLED_EmitBody_Loop_FalseToBody) {
|
||||||
// TODO(dneto): Needs break-if
|
// TODO(dneto): Needs break-if
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SpvParserTest, DISABLED_EmitBody_Loop_NestedIfContinue) {
|
TEST_F(SpvParserTest, EmitBody_Loop_NestedIfContinue) {
|
||||||
// TODO(dneto): Needs "continue" terminator support
|
// By construction, it has to come from nested code.
|
||||||
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
||||||
|
%100 = OpFunction %void None %voidfn
|
||||||
|
|
||||||
|
%10 = OpLabel
|
||||||
|
OpBranch %20
|
||||||
|
|
||||||
|
%20 = OpLabel
|
||||||
|
OpLoopMerge %99 %80 None
|
||||||
|
OpBranch %30
|
||||||
|
|
||||||
|
%30 = OpLabel
|
||||||
|
OpSelectionMerge %50 None
|
||||||
|
OpBranchConditional %cond %40 %50
|
||||||
|
|
||||||
|
%40 = OpLabel
|
||||||
|
OpStore %var %uint_1
|
||||||
|
OpBranch %80 ; continue edge
|
||||||
|
|
||||||
|
%50 = OpLabel ; inner selection merge
|
||||||
|
OpStore %var %uint_2
|
||||||
|
OpBranch %80
|
||||||
|
|
||||||
|
%80 = OpLabel ; continue target
|
||||||
|
OpStore %var %uint_3
|
||||||
|
OpBranch %20
|
||||||
|
|
||||||
|
%99 = OpLabel
|
||||||
|
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"(Loop{
|
||||||
|
If{
|
||||||
|
(
|
||||||
|
ScalarConstructor{false}
|
||||||
|
)
|
||||||
|
{
|
||||||
|
Assignment{
|
||||||
|
Identifier{var}
|
||||||
|
ScalarConstructor{1}
|
||||||
|
}
|
||||||
|
Continue{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Else{
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Assignment{
|
||||||
|
Identifier{var}
|
||||||
|
ScalarConstructor{2}
|
||||||
|
}
|
||||||
|
continuing {
|
||||||
|
Assignment{
|
||||||
|
Identifier{var}
|
||||||
|
ScalarConstructor{3}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Return{}
|
||||||
|
)")) << ToString(fe.ast_body());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SpvParserTest, DISABLED_EmitBody_Loop_BodyAlwaysBreaks) {
|
TEST_F(SpvParserTest, EmitBody_Loop_BodyAlwaysBreaks) {
|
||||||
// TODO(dneto): Needs "continue" terminator support
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
||||||
|
%100 = OpFunction %void None %voidfn
|
||||||
|
|
||||||
|
%10 = OpLabel
|
||||||
|
OpBranch %20
|
||||||
|
|
||||||
|
%20 = OpLabel
|
||||||
|
OpLoopMerge %99 %80 None
|
||||||
|
OpBranch %30
|
||||||
|
|
||||||
|
%30 = OpLabel
|
||||||
|
OpStore %var %uint_1
|
||||||
|
OpBranch %99 ; break is here
|
||||||
|
|
||||||
|
%80 = OpLabel
|
||||||
|
OpStore %var %uint_2
|
||||||
|
OpBranch %20 ; backedge
|
||||||
|
|
||||||
|
%99 = OpLabel
|
||||||
|
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"(Loop{
|
||||||
|
Assignment{
|
||||||
|
Identifier{var}
|
||||||
|
ScalarConstructor{1}
|
||||||
|
}
|
||||||
|
Break{}
|
||||||
|
continuing {
|
||||||
|
Assignment{
|
||||||
|
Identifier{var}
|
||||||
|
ScalarConstructor{2}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Return{}
|
||||||
|
)")) << ToString(fe.ast_body());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SpvParserTest, DISABLED_EmitBody_Loop_BodyConditionallyBreaks) {
|
TEST_F(SpvParserTest, DISABLED_EmitBody_Loop_BodyConditionallyBreaks) {
|
||||||
|
@ -8062,6 +8167,436 @@ TEST_F(SpvParserTest, EmitBody_Unreachable_InNonVoidFunction) {
|
||||||
)")) << ToString(fe.ast_body());
|
)")) << ToString(fe.ast_body());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvParserTest, EmitBody_Branch_BackEdge_MultiBlockLoop) {
|
||||||
|
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
|
||||||
|
OpStore %var %uint_1
|
||||||
|
OpBranch %20 ; here is one
|
||||||
|
|
||||||
|
%99 = OpLabel
|
||||||
|
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"(Loop{
|
||||||
|
continuing {
|
||||||
|
Assignment{
|
||||||
|
Identifier{var}
|
||||||
|
ScalarConstructor{1}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Return{}
|
||||||
|
)")) << ToString(fe.ast_body());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvParserTest, EmitBody_Branch_BackEdge_SingleBlockLoop) {
|
||||||
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
||||||
|
%100 = OpFunction %void None %voidfn
|
||||||
|
|
||||||
|
%10 = OpLabel
|
||||||
|
OpBranch %20
|
||||||
|
|
||||||
|
%20 = OpLabel
|
||||||
|
OpStore %var %uint_1
|
||||||
|
OpLoopMerge %99 %20 None
|
||||||
|
OpBranch %20 ; backedge in single block loop
|
||||||
|
|
||||||
|
%99 = OpLabel
|
||||||
|
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"(Loop{
|
||||||
|
Assignment{
|
||||||
|
Identifier{var}
|
||||||
|
ScalarConstructor{1}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Return{}
|
||||||
|
)")) << ToString(fe.ast_body());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvParserTest, DISABLED_EmitBody_Branch_SwitchBreak) {
|
||||||
|
// TODO(dneto): support switch first.
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvParserTest, EmitBody_Branch_LoopBreak_SingleBlockLoop) {
|
||||||
|
// This case is impossible. The loop must have a backedge,
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvParserTest, EmitBody_Branch_LoopBreak_MultiBlockLoop_FromBody) {
|
||||||
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
||||||
|
%100 = OpFunction %void None %voidfn
|
||||||
|
|
||||||
|
%10 = OpLabel
|
||||||
|
OpBranch %20
|
||||||
|
|
||||||
|
%20 = OpLabel
|
||||||
|
OpLoopMerge %99 %80 None
|
||||||
|
OpBranch %30
|
||||||
|
|
||||||
|
%30 = OpLabel
|
||||||
|
OpStore %var %uint_1
|
||||||
|
OpBranch %99 ; break is here
|
||||||
|
|
||||||
|
%80 = OpLabel
|
||||||
|
OpStore %var %uint_2
|
||||||
|
OpBranch %20 ; backedge
|
||||||
|
|
||||||
|
%99 = OpLabel
|
||||||
|
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"(Loop{
|
||||||
|
Assignment{
|
||||||
|
Identifier{var}
|
||||||
|
ScalarConstructor{1}
|
||||||
|
}
|
||||||
|
Break{}
|
||||||
|
continuing {
|
||||||
|
Assignment{
|
||||||
|
Identifier{var}
|
||||||
|
ScalarConstructor{2}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Return{}
|
||||||
|
)")) << ToString(fe.ast_body());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(
|
||||||
|
SpvParserTest,
|
||||||
|
EmitBody_Branch_LoopBreak_MultiBlockLoop_FromContinueConstructConditional) {
|
||||||
|
// This case is invalid because the backedge block doesn't post-dominate the
|
||||||
|
// continue target.
|
||||||
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
||||||
|
%100 = OpFunction %void None %voidfn
|
||||||
|
|
||||||
|
%10 = OpLabel
|
||||||
|
OpBranch %20
|
||||||
|
|
||||||
|
%20 = OpLabel
|
||||||
|
OpLoopMerge %99 %30 None
|
||||||
|
OpBranch %30
|
||||||
|
|
||||||
|
%30 = OpLabel ; continue target; also an if-header
|
||||||
|
OpSelectionMerge %80 None
|
||||||
|
OpBranchConditional %cond %40 %80
|
||||||
|
|
||||||
|
%40 = OpLabel
|
||||||
|
OpBranch %99 ; break, inside a nested if.
|
||||||
|
|
||||||
|
%80 = OpLabel
|
||||||
|
OpBranch %20 ; backedge
|
||||||
|
|
||||||
|
%99 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
|
||||||
|
OpFunctionEnd
|
||||||
|
)"));
|
||||||
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
||||||
|
FunctionEmitter fe(p, *spirv_function(100));
|
||||||
|
EXPECT_FALSE(fe.EmitBody()) << p->error();
|
||||||
|
EXPECT_THAT(p->error(),
|
||||||
|
Eq("Invalid exit (40->99) from continue construct: 40 is not the "
|
||||||
|
"last block in the continue construct starting at 30 "
|
||||||
|
"(violates post-dominance rule)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvParserTest,
|
||||||
|
EmitBody_Branch_LoopBreak_MultiBlockLoop_FromContinueConstructEnd) {
|
||||||
|
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
|
||||||
|
OpBranch %99 ; should be a backedge
|
||||||
|
|
||||||
|
%99 = OpLabel
|
||||||
|
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"(Loop{
|
||||||
|
continuing {
|
||||||
|
Assignment{
|
||||||
|
Identifier{var}
|
||||||
|
ScalarConstructor{1}
|
||||||
|
}
|
||||||
|
Break{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Return{}
|
||||||
|
)")) << ToString(fe.ast_body());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvParserTest, EmitBody_Branch_LoopContinue_LastInLoopConstruct) {
|
||||||
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
||||||
|
%100 = OpFunction %void None %voidfn
|
||||||
|
|
||||||
|
%10 = OpLabel
|
||||||
|
OpBranch %20
|
||||||
|
|
||||||
|
%20 = OpLabel
|
||||||
|
OpLoopMerge %99 %80 None
|
||||||
|
OpBranch %30
|
||||||
|
|
||||||
|
%30 = OpLabel
|
||||||
|
OpStore %var %uint_1
|
||||||
|
OpBranch %80 ; continue edge from last block before continue target
|
||||||
|
|
||||||
|
%80 = OpLabel ; continue target
|
||||||
|
OpStore %var %uint_2
|
||||||
|
OpBranch %20
|
||||||
|
|
||||||
|
%99 = OpLabel
|
||||||
|
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"(Loop{
|
||||||
|
Assignment{
|
||||||
|
Identifier{var}
|
||||||
|
ScalarConstructor{1}
|
||||||
|
}
|
||||||
|
continuing {
|
||||||
|
Assignment{
|
||||||
|
Identifier{var}
|
||||||
|
ScalarConstructor{2}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Return{}
|
||||||
|
)")) << ToString(fe.ast_body());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvParserTest, EmitBody_Branch_LoopContinue_BeforeLast) {
|
||||||
|
// By construction, it has to come from nested code.
|
||||||
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
||||||
|
%100 = OpFunction %void None %voidfn
|
||||||
|
|
||||||
|
%10 = OpLabel
|
||||||
|
OpBranch %20
|
||||||
|
|
||||||
|
%20 = OpLabel
|
||||||
|
OpLoopMerge %99 %80 None
|
||||||
|
OpBranch %30
|
||||||
|
|
||||||
|
%30 = OpLabel
|
||||||
|
OpSelectionMerge %50 None
|
||||||
|
OpBranchConditional %cond %40 %50
|
||||||
|
|
||||||
|
%40 = OpLabel
|
||||||
|
OpStore %var %uint_1
|
||||||
|
OpBranch %80 ; continue edge
|
||||||
|
|
||||||
|
%50 = OpLabel ; inner selection merge
|
||||||
|
OpStore %var %uint_2
|
||||||
|
OpBranch %80
|
||||||
|
|
||||||
|
%80 = OpLabel ; continue target
|
||||||
|
OpStore %var %uint_3
|
||||||
|
OpBranch %20
|
||||||
|
|
||||||
|
%99 = OpLabel
|
||||||
|
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"(Loop{
|
||||||
|
If{
|
||||||
|
(
|
||||||
|
ScalarConstructor{false}
|
||||||
|
)
|
||||||
|
{
|
||||||
|
Assignment{
|
||||||
|
Identifier{var}
|
||||||
|
ScalarConstructor{1}
|
||||||
|
}
|
||||||
|
Continue{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Else{
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Assignment{
|
||||||
|
Identifier{var}
|
||||||
|
ScalarConstructor{2}
|
||||||
|
}
|
||||||
|
continuing {
|
||||||
|
Assignment{
|
||||||
|
Identifier{var}
|
||||||
|
ScalarConstructor{3}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Return{}
|
||||||
|
)")) << ToString(fe.ast_body());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvParserTest, EmitBody_Branch_IfBreak_FromThen) {
|
||||||
|
// When unconditional, the if-break must be last in the then clause.
|
||||||
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
||||||
|
%100 = OpFunction %void None %voidfn
|
||||||
|
|
||||||
|
%10 = OpLabel
|
||||||
|
OpSelectionMerge %99 None
|
||||||
|
OpBranchConditional %cond %30 %99
|
||||||
|
|
||||||
|
%30 = OpLabel
|
||||||
|
OpStore %var %uint_1
|
||||||
|
OpBranch %99
|
||||||
|
|
||||||
|
%99 = OpLabel
|
||||||
|
OpStore %var %uint_2
|
||||||
|
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"(If{
|
||||||
|
(
|
||||||
|
ScalarConstructor{false}
|
||||||
|
)
|
||||||
|
{
|
||||||
|
Assignment{
|
||||||
|
Identifier{var}
|
||||||
|
ScalarConstructor{1}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Else{
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Assignment{
|
||||||
|
Identifier{var}
|
||||||
|
ScalarConstructor{2}
|
||||||
|
}
|
||||||
|
Return{}
|
||||||
|
)")) << ToString(fe.ast_body());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvParserTest, EmitBody_Branch_IfBreak_FromElse) {
|
||||||
|
// When unconditional, the if-break must be last in the else clause.
|
||||||
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
||||||
|
%100 = OpFunction %void None %voidfn
|
||||||
|
|
||||||
|
%10 = OpLabel
|
||||||
|
OpSelectionMerge %99 None
|
||||||
|
OpBranchConditional %cond %99 %30
|
||||||
|
|
||||||
|
%30 = OpLabel
|
||||||
|
OpStore %var %uint_1
|
||||||
|
OpBranch %99
|
||||||
|
|
||||||
|
%99 = OpLabel
|
||||||
|
OpStore %var %uint_2
|
||||||
|
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"(If{
|
||||||
|
(
|
||||||
|
ScalarConstructor{false}
|
||||||
|
)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Else{
|
||||||
|
{
|
||||||
|
Assignment{
|
||||||
|
Identifier{var}
|
||||||
|
ScalarConstructor{1}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Assignment{
|
||||||
|
Identifier{var}
|
||||||
|
ScalarConstructor{2}
|
||||||
|
}
|
||||||
|
Return{}
|
||||||
|
)")) << ToString(fe.ast_body());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvParserTest, DISABLED_EmitBody_Branch_CaseFallthrough) {
|
||||||
|
// TODO(dneto): support switch first.
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvParserTest, EmitBody_Branch_Forward) {
|
||||||
|
auto* p = parser(test::Assemble(CommonTypes() + R"(
|
||||||
|
%100 = OpFunction %void None %voidfn
|
||||||
|
|
||||||
|
%10 = OpLabel
|
||||||
|
OpStore %var %uint_1
|
||||||
|
OpBranch %99 ; forward
|
||||||
|
|
||||||
|
%99 = OpLabel
|
||||||
|
OpStore %var %uint_2
|
||||||
|
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{1}
|
||||||
|
}
|
||||||
|
Assignment{
|
||||||
|
Identifier{var}
|
||||||
|
ScalarConstructor{2}
|
||||||
|
}
|
||||||
|
Return{}
|
||||||
|
)")) << ToString(fe.ast_body());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace spirv
|
} // namespace spirv
|
||||||
} // namespace reader
|
} // namespace reader
|
||||||
|
|
Loading…
Reference in New Issue