spirv-reader: sample_mask_in, sample_mask_out

TODO: passing pointer to them as a function parameter

Bug: tint:471
Change-Id: Ibd55bdc77a2bfb0f5712dd9bf332910999b8d0d1
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/40123
Commit-Queue: David Neto <dneto@google.com>
Auto-Submit: David Neto <dneto@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
This commit is contained in:
David Neto 2021-02-04 22:12:30 +00:00 committed by Commit Bot service account
parent 1f2d38c826
commit a2fa690cda
5 changed files with 709 additions and 20 deletions

View File

@ -747,6 +747,9 @@ FunctionEmitter::FunctionEmitter(ParserImpl* pi,
namer_(pi->namer()),
function_(function),
i32_(builder_.create<type::I32>()),
u32_(builder_.create<type::U32>()),
sample_mask_in_id(0u),
sample_mask_out_id(0u),
ep_info_(ep_info) {
PushNewStatementBlock(nullptr, 0, nullptr);
}
@ -2032,6 +2035,17 @@ TypedExpression FunctionEmitter::MakeExpression(uint32_t id) {
Fail() << "unhandled use of a pointer to the SampleId builtin, with ID: "
<< id;
return {};
case SkipReason::kSampleMaskInBuiltinPointer:
Fail()
<< "unhandled use of a pointer to the SampleMask builtin, with ID: "
<< id;
return {};
case SkipReason::kSampleMaskOutBuiltinPointer:
// The result type is always u32.
auto name = namer_.Name(sample_mask_out_id);
return TypedExpression{u32_,
create<ast::IdentifierExpression>(
Source{}, builder_.Symbols().Register(name))};
}
if (identifier_values_.count(id) || parser_impl_.IsScalarSpecConstant(id)) {
auto name = namer_.Name(id);
@ -2968,11 +2982,14 @@ bool FunctionEmitter::EmitStatement(const spvtools::opt::Instruction& inst) {
return true;
case SpvOpStore: {
const auto ptr_id = inst.GetSingleWordInOperand(0);
auto ptr_id = inst.GetSingleWordInOperand(0);
const auto value_id = inst.GetSingleWordInOperand(1);
auto rhs = MakeExpression(value_id);
// Handle exceptional cases
if (GetSkipReason(ptr_id) == SkipReason::kPointSizeBuiltinPointer) {
switch (GetSkipReason(ptr_id)) {
case SkipReason::kPointSizeBuiltinPointer:
if (const auto* c = constant_mgr_->FindDeclaredConstant(value_id)) {
// If we're writing a constant 1.0, then skip the write. That's all
// that WebGPU handles.
@ -2986,7 +3003,20 @@ bool FunctionEmitter::EmitStatement(const spvtools::opt::Instruction& inst) {
return Fail() << "cannot store a value other than constant 1.0 to "
"PointSize builtin: "
<< inst.PrettyPrint();
case SkipReason::kSampleMaskOutBuiltinPointer:
ptr_id = sample_mask_out_id;
if (rhs.type != u32_) {
// WGSL requires sample_mask_out to be signed.
rhs = TypedExpression{
u32_, create<ast::TypeConstructorExpression>(
Source{}, u32_, ast::ExpressionList{rhs.expr})};
}
break;
default:
break;
}
const auto ptr_type_id = def_use_mgr_->GetDef(ptr_id)->type_id();
const auto& builtin_position_info = parser_impl_.GetBuiltInPositionInfo();
if (ptr_type_id == builtin_position_info.pointer_type_id) {
@ -2996,9 +3026,7 @@ bool FunctionEmitter::EmitStatement(const spvtools::opt::Instruction& inst) {
}
// Handle an ordinary store as an assignment.
// TODO(dneto): Order of evaluation?
auto lhs = MakeExpression(ptr_id);
auto rhs = MakeExpression(value_id);
AddStatement(
create<ast::AssignmentStatement>(Source{}, lhs.expr, rhs.expr));
return success();
@ -3024,6 +3052,25 @@ bool FunctionEmitter::EmitStatement(const spvtools::opt::Instruction& inst) {
Source{}, i32_, ast::ExpressionList{id_expr})};
return EmitConstDefinition(inst, expr);
}
case SkipReason::kSampleMaskInBuiltinPointer: {
auto name = namer_.Name(sample_mask_in_id);
ast::Expression* id_expr = create<ast::IdentifierExpression>(
Source{}, builder_.Symbols().Register(name));
auto* load_result_type = parser_impl_.ConvertType(inst.type_id());
ast::Expression* ast_expr = nullptr;
if (load_result_type == i32_) {
ast_expr = create<ast::TypeConstructorExpression>(
Source{}, i32_, ast::ExpressionList{id_expr});
} else if (load_result_type == u32_) {
ast_expr = id_expr;
} else {
return Fail() << "loading the whole SampleMask input array is not "
"supported: "
<< inst.PrettyPrint();
}
return EmitConstDefinition(
inst, TypedExpression{load_result_type, ast_expr});
}
default:
break;
}
@ -3495,9 +3542,8 @@ TypedExpression FunctionEmitter::MakeCompositeExtract(
TypedExpression current_expr(MakeOperand(inst, 0));
auto make_index = [this, source](uint32_t literal) {
auto* type = create<type::U32>();
return create<ast::ScalarConstructorExpression>(
source, create<ast::UintLiteral>(source, type, literal));
source, create<ast::UintLiteral>(source, u32_, literal));
};
const auto composite = inst.GetSingleWordInOperand(0);
@ -3655,8 +3701,8 @@ bool FunctionEmitter::RegisterSpecialBuiltInVariables() {
for (auto& special_var : parser_impl_.special_builtins()) {
const auto id = special_var.first;
const auto builtin = special_var.second;
def_info_[id] =
std::make_unique<DefInfo>(*(def_use_mgr_->GetDef(id)), 0, index);
const auto* var = def_use_mgr_->GetDef(id);
def_info_[id] = std::make_unique<DefInfo>(*var, 0, index);
++index;
auto& def = def_info_[id];
switch (builtin) {
@ -3666,6 +3712,19 @@ bool FunctionEmitter::RegisterSpecialBuiltInVariables() {
case SpvBuiltInSampleId:
def->skip = SkipReason::kSampleIdBuiltinPointer;
break;
case SpvBuiltInSampleMask: {
// Distinguish between input and output variable.
const auto storage_class =
static_cast<SpvStorageClass>(var->GetSingleWordInOperand(0));
if (storage_class == SpvStorageClassInput) {
sample_mask_in_id = id;
def->skip = SkipReason::kSampleMaskInBuiltinPointer;
} else {
sample_mask_out_id = id;
def->skip = SkipReason::kSampleMaskOutBuiltinPointer;
}
break;
}
default:
return Fail() << "unrecognized special builtin: " << int(builtin);
}

View File

@ -44,6 +44,7 @@
#include "src/reader/spirv/parser_impl.h"
#include "src/type/i32_type.h"
#include "src/type/texture_type.h"
#include "src/type/u32_type.h"
namespace tint {
namespace reader {
@ -227,6 +228,14 @@ enum class SkipReason {
/// `kSampleIdBuiltinPointer`: the value is a pointer to the SampleId builtin
/// variable. Don't generate its address.
kSampleIdBuiltinPointer,
/// `kSampleMaskInBuiltinPointer`: the value is a pointer to the SampleMaskIn
/// builtin input variable. Don't generate its address.
kSampleMaskInBuiltinPointer,
/// `kSampleMaskOutBuiltinPointer`: the value is a pointer to the SampleMask
/// builtin output variable.
kSampleMaskOutBuiltinPointer,
};
/// Bookkeeping info for a SPIR-V ID defined in the function, or some
@ -335,6 +344,12 @@ inline std::ostream& operator<<(std::ostream& o, const DefInfo& di) {
case SkipReason::kSampleIdBuiltinPointer:
o << " skip:sampleid_pointer";
break;
case SkipReason::kSampleMaskInBuiltinPointer:
o << " skip:samplemaskin_pointer";
break;
case SkipReason::kSampleMaskOutBuiltinPointer:
o << " skip:samplemaskout_pointer";
break;
}
o << "}";
return o;
@ -1085,6 +1100,12 @@ class FunctionEmitter {
Namer& namer_;
const spvtools::opt::Function& function_;
type::I32* const i32_; // The unique I32 type object.
type::U32* const u32_; // The unique U32 type object.
// The SPIR-V ID for the SampleMask input variable.
uint32_t sample_mask_in_id;
// The SPIR-V ID for the SampleMask output variable.
uint32_t sample_mask_out_id;
// A stack of statement lists. Each list is contained in a construct in
// the next deeper element of stack. The 0th entry represents the statements

View File

@ -1231,6 +1231,31 @@ bool ParserImpl::EmitModuleScopeVariables() {
return success_;
}
// @param var_id SPIR-V id of an OpVariable, assumed to be pointer
// to an array
// @returns the IntConstant for the size of the array, or nullptr
const spvtools::opt::analysis::IntConstant* ParserImpl::GetArraySize(
uint32_t var_id) {
auto* var = def_use_mgr_->GetDef(var_id);
if (!var || var->opcode() != SpvOpVariable) {
return nullptr;
}
auto* ptr_type = def_use_mgr_->GetDef(var->type_id());
if (!ptr_type || ptr_type->opcode() != SpvOpTypePointer) {
return nullptr;
}
auto* array_type = def_use_mgr_->GetDef(ptr_type->GetSingleWordInOperand(1));
if (!array_type || array_type->opcode() != SpvOpTypeArray) {
return nullptr;
}
auto* size = constant_mgr_->FindDeclaredConstant(
array_type->GetSingleWordInOperand(1));
if (!size) {
return nullptr;
}
return size->AsIntConstant();
}
ast::Variable* ParserImpl::MakeVariable(
uint32_t id,
ast::StorageClass sc,
@ -1277,6 +1302,20 @@ ast::Variable* ParserImpl::MakeVariable(
type = forced_type;
}
break;
case SpvBuiltInSampleMask: {
// In SPIR-V this is used for both input and output variable.
// The SPIR-V variable has store type of array of integer scalar,
// either signed or unsigned.
// WGSL requires the store type to be u32.
auto* size = GetArraySize(id);
if (!size || size->GetZeroExtendedValue() != 1) {
Fail() << "WGSL supports a sample mask of at most 32 bits. "
"SampleMask must be an array of 1 element.";
}
special_builtins_[id] = spv_builtin;
type = builder_.create<type::U32>();
break;
}
default:
break;
}

View File

@ -287,6 +287,11 @@ class ParserImpl : Reader {
/// @returns true if parser is still successful.
bool EmitFunction(const spvtools::opt::Function& f);
/// Returns the integer constant for the array size of the given variable.
/// @param var_id SPIR-V ID for an array variable
/// @returns the integer constant for its array size, or nullptr.
const spvtools::opt::analysis::IntConstant* GetArraySize(uint32_t var_id);
/// Creates an AST Variable node for a SPIR-V ID, including any attached
/// decorations, unless it's an ignorable builtin variable.
/// @param id the SPIR-V result ID

View File

@ -2403,6 +2403,571 @@ TEST_F(SpvModuleScopeVarParserTest, SampleId_U32_FunctParam) {
})")) << module_str;
}
// Returns the start of a shader for testing SampleMask
// parameterized by store type.
std::string SampleMaskPreamble(std::string store_type) {
return R"(
OpCapability Shader
OpMemoryModel Logical Simple
OpEntryPoint Fragment %main "main" %1
OpExecutionMode %main OriginUpperLeft
OpDecorate %1 BuiltIn SampleMask
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%float = OpTypeFloat 32
%uint = OpTypeInt 32 0
%int = OpTypeInt 32 1
%int_12 = OpConstant %int 12
%uint_0 = OpConstant %uint 0
%uint_1 = OpConstant %uint 1
%uint_2 = OpConstant %uint 2
%uarr1 = OpTypeArray %uint %uint_1
%uarr2 = OpTypeArray %uint %uint_2
%iarr1 = OpTypeArray %int %uint_1
%iarr2 = OpTypeArray %int %uint_2
%iptr_in_ty = OpTypePointer Input %int
%uptr_in_ty = OpTypePointer Input %uint
%iptr_out_ty = OpTypePointer Output %int
%uptr_out_ty = OpTypePointer Output %uint
%in_ty = OpTypePointer Input )" +
store_type + R"(
%out_ty = OpTypePointer Output )" +
store_type + R"(
)";
}
TEST_F(SpvModuleScopeVarParserTest, SampleMask_In_ArraySize2_Error) {
const std::string assembly = SampleMaskPreamble("%uarr2") + R"(
%1 = OpVariable %in_ty Input
%main = OpFunction %void None %voidfn
%entry = OpLabel
%2 = OpAccessChain %uptr_in_ty %1 %uint_0
%3 = OpLoad %int %2
OpReturn
OpFunctionEnd
)";
auto p = parser(test::Assemble(assembly));
ASSERT_FALSE(p->BuildAndParseInternalModule());
EXPECT_THAT(p->error(),
HasSubstr("WGSL supports a sample mask of at most 32 bits. "
"SampleMask must be an array of 1 element"))
<< p->error() << assembly;
}
TEST_F(SpvModuleScopeVarParserTest, SampleMask_In_U32_Direct) {
const std::string assembly = SampleMaskPreamble("%uarr1") + R"(
%1 = OpVariable %in_ty Input
%main = OpFunction %void None %voidfn
%entry = OpLabel
%2 = OpAccessChain %uptr_in_ty %1 %uint_0
%3 = OpLoad %uint %2
OpReturn
OpFunctionEnd
)";
auto p = parser(test::Assemble(assembly));
ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
EXPECT_TRUE(p->error().empty());
const auto module_str = p->program().to_str();
// Correct declaration
EXPECT_THAT(module_str, HasSubstr(R"(
Variable{
Decorations{
BuiltinDecoration{sample_mask_in}
}
x_1
in
__u32
})"));
// Correct bodies
EXPECT_THAT(module_str, HasSubstr(R"(
VariableDeclStatement{
VariableConst{
x_3
none
__u32
{
Identifier[not set]{x_1}
}
}
})"))
<< module_str;
}
TEST_F(SpvModuleScopeVarParserTest, SampleMask_In_U32_CopyObject) {
const std::string assembly = SampleMaskPreamble("%uarr1") + R"(
%1 = OpVariable %in_ty Input
%main = OpFunction %void None %voidfn
%entry = OpLabel
%2 = OpAccessChain %uptr_in_ty %1 %uint_0
%3 = OpCopyObject %uptr_in_ty %2
%4 = OpLoad %uint %3
OpReturn
OpFunctionEnd
)";
auto p = parser(test::Assemble(assembly));
ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
EXPECT_TRUE(p->error().empty());
const auto module_str = p->program().to_str();
// Correct declaration
EXPECT_THAT(module_str, HasSubstr(R"(
Variable{
Decorations{
BuiltinDecoration{sample_mask_in}
}
x_1
in
__u32
})"));
// Correct bodies
EXPECT_THAT(module_str, HasSubstr(R"(
VariableDeclStatement{
VariableConst{
x_4
none
__u32
{
Identifier[not set]{x_1}
}
}
})"))
<< module_str;
}
TEST_F(SpvModuleScopeVarParserTest, SampleMask_In_U32_AccessChain) {
const std::string assembly = SampleMaskPreamble("%uarr1") + R"(
%1 = OpVariable %in_ty Input
%main = OpFunction %void None %voidfn
%entry = OpLabel
%2 = OpAccessChain %uptr_in_ty %1 %uint_0
%3 = OpAccessChain %uptr_in_ty %2
%4 = OpLoad %uint %3
OpReturn
OpFunctionEnd
)";
auto p = parser(test::Assemble(assembly));
ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
EXPECT_TRUE(p->error().empty());
const auto module_str = p->program().to_str();
// Correct declaration
EXPECT_THAT(module_str, HasSubstr(R"(
Variable{
Decorations{
BuiltinDecoration{sample_mask_in}
}
x_1
in
__u32
})"));
// Correct bodies
EXPECT_THAT(module_str, HasSubstr(R"(
VariableDeclStatement{
VariableConst{
x_4
none
__u32
{
Identifier[not set]{x_1}
}
}
})"))
<< module_str;
}
TEST_F(SpvModuleScopeVarParserTest, SampleMask_In_I32_Direct) {
const std::string assembly = SampleMaskPreamble("%iarr1") + R"(
%1 = OpVariable %in_ty Input
%main = OpFunction %void None %voidfn
%entry = OpLabel
%2 = OpAccessChain %iptr_in_ty %1 %uint_0
%3 = OpLoad %int %2
OpReturn
OpFunctionEnd
)";
auto p = parser(test::Assemble(assembly));
ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
EXPECT_TRUE(p->error().empty());
const auto module_str = p->program().to_str();
// Correct declaration
EXPECT_THAT(module_str, HasSubstr(R"(
Variable{
Decorations{
BuiltinDecoration{sample_mask_in}
}
x_1
in
__u32
})"));
// Correct bodies
EXPECT_THAT(module_str, HasSubstr(R"(
VariableDeclStatement{
VariableConst{
x_3
none
__i32
{
TypeConstructor[not set]{
__i32
Identifier[not set]{x_1}
}
}
}
})"))
<< module_str;
}
TEST_F(SpvModuleScopeVarParserTest, SampleMask_In_I32_CopyObject) {
const std::string assembly = SampleMaskPreamble("%iarr1") + R"(
%1 = OpVariable %in_ty Input
%main = OpFunction %void None %voidfn
%entry = OpLabel
%2 = OpAccessChain %iptr_in_ty %1 %uint_0
%3 = OpCopyObject %iptr_in_ty %2
%4 = OpLoad %int %3
OpReturn
OpFunctionEnd
)";
auto p = parser(test::Assemble(assembly));
ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
EXPECT_TRUE(p->error().empty());
const auto module_str = p->program().to_str();
// Correct declaration
EXPECT_THAT(module_str, HasSubstr(R"(
Variable{
Decorations{
BuiltinDecoration{sample_mask_in}
}
x_1
in
__u32
})"));
// Correct bodies
EXPECT_THAT(module_str, HasSubstr(R"(
VariableDeclStatement{
VariableConst{
x_4
none
__i32
{
TypeConstructor[not set]{
__i32
Identifier[not set]{x_1}
}
}
}
})"))
<< module_str;
}
TEST_F(SpvModuleScopeVarParserTest, SampleMask_In_I32_AccessChain) {
const std::string assembly = SampleMaskPreamble("%iarr1") + R"(
%1 = OpVariable %in_ty Input
%main = OpFunction %void None %voidfn
%entry = OpLabel
%2 = OpAccessChain %iptr_in_ty %1 %uint_0
%3 = OpAccessChain %iptr_in_ty %2
%4 = OpLoad %int %3
OpReturn
OpFunctionEnd
)";
auto p = parser(test::Assemble(assembly));
ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
EXPECT_TRUE(p->error().empty());
const auto module_str = p->program().to_str();
// Correct declaration
EXPECT_THAT(module_str, HasSubstr(R"(
Variable{
Decorations{
BuiltinDecoration{sample_mask_in}
}
x_1
in
__u32
})"));
// Correct bodies
EXPECT_THAT(module_str, HasSubstr(R"(
VariableDeclStatement{
VariableConst{
x_4
none
__i32
{
TypeConstructor[not set]{
__i32
Identifier[not set]{x_1}
}
}
}
})"))
<< module_str;
}
TEST_F(SpvModuleScopeVarParserTest, SampleMask_Out_ArraySize2_Error) {
const std::string assembly = SampleMaskPreamble("%uarr2") + R"(
%1 = OpVariable %out_ty Output
%main = OpFunction %void None %voidfn
%entry = OpLabel
%2 = OpAccessChain %uptr_out_ty %1 %uint_0
OpStore %2 %uint_0
OpReturn
OpFunctionEnd
)";
auto p = parser(test::Assemble(assembly));
ASSERT_FALSE(p->BuildAndParseInternalModule());
EXPECT_THAT(p->error(),
HasSubstr("WGSL supports a sample mask of at most 32 bits. "
"SampleMask must be an array of 1 element"))
<< p->error() << assembly;
}
TEST_F(SpvModuleScopeVarParserTest, SampleMask_Out_U32_Direct) {
const std::string assembly = SampleMaskPreamble("%uarr1") + R"(
%1 = OpVariable %out_ty Output
%main = OpFunction %void None %voidfn
%entry = OpLabel
%2 = OpAccessChain %uptr_out_ty %1 %uint_0
OpStore %2 %uint_0
OpReturn
OpFunctionEnd
)";
auto p = parser(test::Assemble(assembly));
ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
EXPECT_TRUE(p->error().empty());
const auto module_str = p->program().to_str();
// Correct declaration
EXPECT_THAT(module_str, HasSubstr(R"(
Variable{
Decorations{
BuiltinDecoration{sample_mask_out}
}
x_1
out
__u32
})"));
// Correct bodies
EXPECT_THAT(module_str, HasSubstr(R"(
Assignment{
Identifier[not set]{x_1}
ScalarConstructor[not set]{0}
})"))
<< module_str;
}
TEST_F(SpvModuleScopeVarParserTest, SampleMask_Out_U32_CopyObject) {
const std::string assembly = SampleMaskPreamble("%uarr1") + R"(
%1 = OpVariable %out_ty Output
%main = OpFunction %void None %voidfn
%entry = OpLabel
%2 = OpAccessChain %uptr_out_ty %1 %uint_0
%3 = OpCopyObject %uptr_out_ty %2
OpStore %2 %uint_0
OpReturn
OpFunctionEnd
)";
auto p = parser(test::Assemble(assembly));
ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
EXPECT_TRUE(p->error().empty());
const auto module_str = p->program().to_str();
// Correct declaration
EXPECT_THAT(module_str, HasSubstr(R"(
Variable{
Decorations{
BuiltinDecoration{sample_mask_out}
}
x_1
out
__u32
})"));
// Correct bodies
EXPECT_THAT(module_str, HasSubstr(R"(
Assignment{
Identifier[not set]{x_1}
ScalarConstructor[not set]{0}
})"))
<< module_str;
}
TEST_F(SpvModuleScopeVarParserTest, SampleMask_Out_U32_AccessChain) {
const std::string assembly = SampleMaskPreamble("%uarr1") + R"(
%1 = OpVariable %out_ty Output
%main = OpFunction %void None %voidfn
%entry = OpLabel
%2 = OpAccessChain %uptr_out_ty %1 %uint_0
%3 = OpAccessChain %uptr_out_ty %2
OpStore %2 %uint_0
OpReturn
OpFunctionEnd
)";
auto p = parser(test::Assemble(assembly));
ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
EXPECT_TRUE(p->error().empty());
const auto module_str = p->program().to_str();
// Correct declaration
EXPECT_THAT(module_str, HasSubstr(R"(
Variable{
Decorations{
BuiltinDecoration{sample_mask_out}
}
x_1
out
__u32
})"));
// Correct bodies
EXPECT_THAT(module_str, HasSubstr(R"(
Assignment{
Identifier[not set]{x_1}
ScalarConstructor[not set]{0}
})"))
<< module_str;
}
TEST_F(SpvModuleScopeVarParserTest, SampleMask_Out_I32_Direct) {
const std::string assembly = SampleMaskPreamble("%iarr1") + R"(
%1 = OpVariable %out_ty Output
%main = OpFunction %void None %voidfn
%entry = OpLabel
%2 = OpAccessChain %iptr_out_ty %1 %uint_0
OpStore %2 %int_12
OpReturn
OpFunctionEnd
)";
auto p = parser(test::Assemble(assembly));
ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
EXPECT_TRUE(p->error().empty());
const auto module_str = p->program().to_str();
// Correct declaration
EXPECT_THAT(module_str, HasSubstr(R"(
Variable{
Decorations{
BuiltinDecoration{sample_mask_out}
}
x_1
out
__u32
})"));
// Correct bodies
EXPECT_THAT(module_str, HasSubstr(R"(
{
Assignment{
Identifier[not set]{x_1}
TypeConstructor[not set]{
__u32
ScalarConstructor[not set]{12}
}
}
Return{}
})"))
<< module_str;
}
TEST_F(SpvModuleScopeVarParserTest, SampleMask_Out_I32_CopyObject) {
const std::string assembly = SampleMaskPreamble("%iarr1") + R"(
%1 = OpVariable %out_ty Output
%main = OpFunction %void None %voidfn
%entry = OpLabel
%2 = OpAccessChain %iptr_out_ty %1 %uint_0
%3 = OpCopyObject %iptr_out_ty %2
OpStore %2 %int_12
OpReturn
OpFunctionEnd
)";
auto p = parser(test::Assemble(assembly));
ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
EXPECT_TRUE(p->error().empty());
const auto module_str = p->program().to_str();
// Correct declaration
EXPECT_THAT(module_str, HasSubstr(R"(
Variable{
Decorations{
BuiltinDecoration{sample_mask_out}
}
x_1
out
__u32
})"));
// Correct bodies
EXPECT_THAT(module_str, HasSubstr(R"(
{
Assignment{
Identifier[not set]{x_1}
TypeConstructor[not set]{
__u32
ScalarConstructor[not set]{12}
}
}
Return{}
})"))
<< module_str;
}
TEST_F(SpvModuleScopeVarParserTest, SampleMask_Out_I32_AccessChain) {
const std::string assembly = SampleMaskPreamble("%iarr1") + R"(
%1 = OpVariable %out_ty Output
%main = OpFunction %void None %voidfn
%entry = OpLabel
%2 = OpAccessChain %iptr_out_ty %1 %uint_0
%3 = OpAccessChain %iptr_out_ty %2
OpStore %2 %int_12
OpReturn
OpFunctionEnd
)";
auto p = parser(test::Assemble(assembly));
ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
EXPECT_TRUE(p->error().empty());
const auto module_str = p->program().to_str();
// Correct declaration
EXPECT_THAT(module_str, HasSubstr(R"(
Variable{
Decorations{
BuiltinDecoration{sample_mask_out}
}
x_1
out
__u32
})"));
// Correct bodies
EXPECT_THAT(module_str, HasSubstr(R"(
{
Assignment{
Identifier[not set]{x_1}
TypeConstructor[not set]{
__u32
ScalarConstructor[not set]{12}
}
}
Return{}
})"))
<< module_str;
}
// TODO(dneto): Test passing pointer to SampleMask as function parameter,
// both input case and output case.
} // namespace
} // namespace spirv
} // namespace reader