spirv-reader: flatten output matrix, array, struct

Bug: tint:912
Change-Id: Iebbcb7ea8d870cccadad7dd1ce8aaccf8965b370
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/56301
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: James Price <jrprice@google.com>
Auto-Submit: David Neto <dneto@google.com>
This commit is contained in:
David Neto 2021-06-29 14:38:56 +00:00
parent c15dbfa1ae
commit 91622e3853
7 changed files with 516 additions and 101 deletions

View File

@ -935,14 +935,15 @@ ast::BlockStatement* FunctionEmitter::MakeFunctionBody() {
return body;
}
bool FunctionEmitter::EmitInputParameter(std::string var_name,
const Type* var_type,
ast::DecorationList* decos,
std::vector<int> index_prefix,
const Type* tip_type,
const Type* forced_param_type,
ast::VariableList* params,
ast::StatementList* statements) {
bool FunctionEmitter::EmitPipelineInput(std::string var_name,
const Type* var_type,
ast::DecorationList* decos,
std::vector<int> index_prefix,
const Type* tip_type,
const Type* forced_param_type,
ast::VariableList* params,
ast::StatementList* statements) {
// TODO(dneto): Handle structs where the locations are annotated on members.
tip_type = tip_type->UnwrapAlias();
if (auto* ref_type = tip_type->As<Reference>()) {
tip_type = ref_type->type;
@ -954,8 +955,8 @@ bool FunctionEmitter::EmitInputParameter(std::string var_name,
const Type* vec_ty = ty_.Vector(matrix_type->type, matrix_type->rows);
for (int col = 0; col < num_columns; col++) {
index_prefix.back() = col;
if (!EmitInputParameter(var_name, var_type, decos, index_prefix, vec_ty,
forced_param_type, params, statements)) {
if (!EmitPipelineInput(var_name, var_type, decos, index_prefix, vec_ty,
forced_param_type, params, statements)) {
return false;
}
}
@ -968,8 +969,8 @@ bool FunctionEmitter::EmitInputParameter(std::string var_name,
const Type* elem_ty = array_type->type;
for (int i = 0; i < static_cast<int>(array_type->size); i++) {
index_prefix.back() = i;
if (!EmitInputParameter(var_name, var_type, decos, index_prefix, elem_ty,
forced_param_type, params, statements)) {
if (!EmitPipelineInput(var_name, var_type, decos, index_prefix, elem_ty,
forced_param_type, params, statements)) {
return false;
}
}
@ -979,9 +980,9 @@ bool FunctionEmitter::EmitInputParameter(std::string var_name,
index_prefix.push_back(0);
for (int i = 0; i < static_cast<int>(members.size()); ++i) {
index_prefix.back() = i;
if (!EmitInputParameter(var_name, var_type, decos, index_prefix,
members[i], forced_param_type, params,
statements)) {
if (!EmitPipelineInput(var_name, var_type, decos, index_prefix,
members[i], forced_param_type, params,
statements)) {
return false;
}
}
@ -1047,6 +1048,120 @@ bool FunctionEmitter::EmitInputParameter(std::string var_name,
return success();
}
bool FunctionEmitter::EmitPipelineOutput(std::string var_name,
const Type* var_type,
ast::DecorationList* decos,
std::vector<int> index_prefix,
const Type* tip_type,
const Type* forced_member_type,
ast::StructMemberList* return_members,
ast::ExpressionList* return_exprs) {
// TODO(dneto): Handle structs where the locations are annotated on members.
tip_type = tip_type->UnwrapAlias();
if (auto* ref_type = tip_type->As<Reference>()) {
tip_type = ref_type->type;
}
if (auto* matrix_type = tip_type->As<Matrix>()) {
index_prefix.push_back(0);
const auto num_columns = static_cast<int>(matrix_type->columns);
const Type* vec_ty = ty_.Vector(matrix_type->type, matrix_type->rows);
for (int col = 0; col < num_columns; col++) {
index_prefix.back() = col;
if (!EmitPipelineOutput(var_name, var_type, decos, index_prefix, vec_ty,
forced_member_type, return_members,
return_exprs)) {
return false;
}
}
return success();
} else if (auto* array_type = tip_type->As<Array>()) {
if (array_type->size == 0) {
return Fail() << "runtime-size array not allowed on pipeline IO";
}
index_prefix.push_back(0);
const Type* elem_ty = array_type->type;
for (int i = 0; i < static_cast<int>(array_type->size); i++) {
index_prefix.back() = i;
if (!EmitPipelineOutput(var_name, var_type, decos, index_prefix, elem_ty,
forced_member_type, return_members,
return_exprs)) {
return false;
}
}
return success();
} else if (auto* struct_type = tip_type->As<Struct>()) {
const auto& members = struct_type->members;
index_prefix.push_back(0);
for (int i = 0; i < static_cast<int>(members.size()); ++i) {
index_prefix.back() = i;
if (!EmitPipelineOutput(var_name, var_type, decos, index_prefix,
members[i], forced_member_type, return_members,
return_exprs)) {
return false;
}
}
return success();
}
const bool is_builtin = ast::HasDecoration<ast::BuiltinDecoration>(*decos);
const Type* member_type = is_builtin ? forced_member_type : tip_type;
// Derive the member name directly from the variable name. They can't
// collide.
const auto member_name = namer_.MakeDerivedName(var_name);
// Create the member.
// TODO(dneto): Note: If the parameter has non-location decorations,
// then those decoration AST nodes will be reused between multiple elements
// of a matrix, array, or structure. Normally that's disallowed but currently
// the SPIR-V reader will make duplicates when the entire AST is cloned
// at the top level of the SPIR-V reader flow. Consider rewriting this
// to avoid this node-sharing.
return_members->push_back(
builder_.Member(member_name, member_type->Build(builder_), *decos));
// Create an expression to evaluate the part of the variable indexed by
// the index_prefix.
ast::Expression* load_source = builder_.Expr(var_name);
// Index into the variable as needed to pick out the flattened member.
auto* current_type = var_type->UnwrapAlias()->UnwrapRef()->UnwrapAlias();
for (auto index : index_prefix) {
if (auto* matrix_type = current_type->As<Matrix>()) {
load_source = builder_.IndexAccessor(load_source, builder_.Expr(index));
current_type = ty_.Vector(matrix_type->type, matrix_type->rows);
} else if (auto* array_type = current_type->As<Array>()) {
load_source = builder_.IndexAccessor(load_source, builder_.Expr(index));
current_type = array_type->type->UnwrapAlias();
} else if (auto* struct_type = current_type->As<Struct>()) {
load_source = builder_.MemberAccessor(
load_source,
builder_.Expr(parser_impl_.GetMemberName(*struct_type, index)));
current_type = struct_type->members[index];
}
}
if (is_builtin && (tip_type != forced_member_type)) {
// The member will have the WGSL type, but we need bitcast to
// the variable store type.
load_source = create<ast::BitcastExpression>(
forced_member_type->Build(builder_), load_source);
}
return_exprs->push_back(load_source);
// Increment the location attribute, in case more parameters will follow.
for (auto*& deco : *decos) {
if (auto* loc_deco = deco->As<ast::LocationDecoration>()) {
// Replace this location decoration with a new one with one higher index.
// The old one doesn't leak because it's kept in the builder's AST node
// list.
deco = builder_.Location(loc_deco->source(), loc_deco->value() + 1);
}
}
return success();
}
bool FunctionEmitter::EmitEntryPointAsWrapper() {
Source source;
@ -1082,7 +1197,6 @@ bool FunctionEmitter::EmitEntryPointAsWrapper() {
// variables must not have them.
const auto var_name = namer_.GetName(var_id);
const auto var_sym = builder_.Symbols().Register(var_name);
bool ok = true;
if (HasBuiltinSampleMask(param_decos)) {
@ -1091,13 +1205,12 @@ bool FunctionEmitter::EmitEntryPointAsWrapper() {
auto* sample_mask_array_type =
store_type->UnwrapRef()->UnwrapAlias()->As<Array>();
TINT_ASSERT(Reader, sample_mask_array_type);
ok = EmitInputParameter(var_name, store_type, &param_decos, {0},
sample_mask_array_type->type, forced_param_type,
&(decl.params), &stmts);
ok = EmitPipelineInput(var_name, store_type, &param_decos, {0},
sample_mask_array_type->type, forced_param_type,
&(decl.params), &stmts);
} else {
// The normal path.
ok =
EmitInputParameter(var_name, store_type, &param_decos, {}, store_type,
ok = EmitPipelineInput(var_name, store_type, &param_decos, {}, store_type,
forced_param_type, &(decl.params), &stmts);
}
if (!ok) {
@ -1127,30 +1240,34 @@ bool FunctionEmitter::EmitEntryPointAsWrapper() {
const auto return_struct_sym =
builder_.Symbols().Register(return_struct_name);
// Define the structure.
std::vector<ast::StructMember*> return_members;
ast::ExpressionList return_exprs;
const auto& builtin_position_info = parser_impl_.GetBuiltInPositionInfo();
// Define the structure.
ast::ExpressionList return_exprs;
std::vector<ast::StructMember*> return_members;
for (uint32_t var_id : ep_info_->outputs) {
const Type* param_type = nullptr;
const Type* store_type = nullptr;
ast::DecorationList out_decos;
if (var_id == builtin_position_info.per_vertex_var_id) {
// The SPIR-V gl_PerVertex variable has already been remapped to
// a gl_Position variable.
// Substitute the type.
store_type = param_type = ty_.Vector(ty_.F32(), 4);
out_decos.emplace_back(
create<ast::BuiltinDecoration>(source, ast::Builtin::kPosition));
// a gl_Position variable. Substitute the type.
const Type* param_type = ty_.Vector(ty_.F32(), 4);
ast::DecorationList out_decos{
create<ast::BuiltinDecoration>(source, ast::Builtin::kPosition)};
const auto var_name = namer_.GetName(var_id);
return_members.push_back(
builder_.Member(var_name, param_type->Build(builder_), out_decos));
return_exprs.push_back(builder_.Expr(var_name));
} else {
const auto* var = def_use_mgr_->GetDef(var_id);
TINT_ASSERT(Reader, var != nullptr);
TINT_ASSERT(Reader, var->opcode() == SpvOpVariable);
store_type = GetVariableStoreType(*var);
param_type = store_type;
if (!parser_impl_.ConvertDecorationsForVariable(var_id, &param_type,
&out_decos, true)) {
const Type* store_type = GetVariableStoreType(*var);
const Type* forced_member_type = store_type;
ast::DecorationList out_decos;
if (!parser_impl_.ConvertDecorationsForVariable(
var_id, &forced_member_type, &out_decos, true)) {
// This occurs, and is not an error, for the PointSize builtin.
if (!success()) {
// But exit early if an error was logged.
@ -1158,45 +1275,29 @@ bool FunctionEmitter::EmitEntryPointAsWrapper() {
}
continue;
}
}
// TODO(dneto): flatten structs and arrays to vectors or scalars.
// The Per-vertex structure is already flattened.
// The member name is the same as the variable name, which is already
// unique across all module-scope declarations.
const auto var_name = namer_.GetName(var_id);
const auto var_sym = builder_.Symbols().Register(var_name);
// Form the member type.
// Reuse the var name for the member name. They can't clash.
ast::StructMember* return_member = create<ast::StructMember>(
Source{}, var_sym, param_type->Build(builder_), out_decos);
return_members.push_back(return_member);
ast::Expression* return_member_value =
create<ast::IdentifierExpression>(source, var_sym);
if (HasBuiltinSampleMask(out_decos)) {
// In Vulkan SPIR-V, the sample mask is an array. In WGSL it's a scalar.
// Get the first element only.
return_member_value = create<ast::ArrayAccessorExpression>(
source, return_member_value, parser_impl_.MakeNullValue(ty_.I32()));
if (const auto* arr_ty = store_type->UnwrapAlias()->As<Array>()) {
if (arr_ty->type->IsSignedScalarOrVector()) {
// sample_mask is unsigned in WGSL. Bitcast it.
return_member_value = create<ast::BitcastExpression>(
source, param_type->Build(builder_), return_member_value);
}
const auto var_name = namer_.GetName(var_id);
bool ok = true;
if (HasBuiltinSampleMask(out_decos)) {
// In Vulkan SPIR-V, the sample mask is an array. In WGSL it's a
// scalar. Use the first element only.
auto* sample_mask_array_type =
store_type->UnwrapRef()->UnwrapAlias()->As<Array>();
TINT_ASSERT(Reader, sample_mask_array_type);
ok = EmitPipelineOutput(var_name, store_type, &out_decos, {0},
sample_mask_array_type->type,
forced_member_type, &return_members,
&return_exprs);
} else {
// Vulkan SPIR-V requires this. Validation should have failed already.
return Fail()
<< "expected SampleMask to be an array of integer scalars";
// The normal path.
ok = EmitPipelineOutput(var_name, store_type, &out_decos, {},
store_type, forced_member_type,
&return_members, &return_exprs);
}
if (!ok) {
return false;
}
} else {
// No other builtin outputs need signedness conversion.
}
// Save the expression.
return_exprs.push_back(return_member_value);
}
if (return_members.empty()) {

View File

@ -428,14 +428,43 @@ class FunctionEmitter {
/// @param params The parameter list where the new parameter is appended.
/// @param statements The statement list where the assignment is appended.
/// @returns false if emission failed
bool EmitInputParameter(std::string var_name,
bool EmitPipelineInput(std::string var_name,
const Type* var_type,
ast::DecorationList* decos,
std::vector<int> index_prefix,
const Type* tip_type,
const Type* forced_param_type,
ast::VariableList* params,
ast::StatementList* statements);
/// Creates one or more struct members from an output variable, and the
/// expressions that compute the value they contribute to the entry point
/// return value. The part of the output variable is specfied
/// by the `index_prefix`, which successively indexes into the variable.
/// Assumes the variable has already been created in the Private storage
/// class.
/// @param var_name The name of the variable
/// @param var_type The store type of the variable
/// @param decos The variable's decorations
/// @param index_prefix Indices stepping into the variable, indicating
/// what part of the variable to populate.
/// @param tip_type The type of the component inside variable, after indexing
/// with the indices in `index_prefix`.
/// @param forced_member_type The type forced by WGSL, if the variable is a
/// builtin, otherwise the same as var_type.
/// @param return_members The struct member list where the new member is
/// added.
/// @param return_exprs The expression list where the return expression is
/// added.
/// @returns false if emission failed
bool EmitPipelineOutput(std::string var_name,
const Type* var_type,
ast::DecorationList* decos,
std::vector<int> index_prefix,
const Type* tip_type,
const Type* forced_param_type,
ast::VariableList* params,
ast::StatementList* statements);
const Type* forced_member_type,
ast::StructMemberList* return_members,
ast::ExpressionList* return_exprs);
/// Create an ast::BlockStatement representing the body of the function.
/// This creates the statement stack, which is non-empty for the lifetime

View File

@ -3373,7 +3373,7 @@ TEST_F(SpvModuleScopeVarParserTest, SampleMask_Out_U32_Direct) {
const std::string expected = R"(Module{
Struct main_out {
StructMember{[[ BuiltinDecoration{sample_mask}
]] x_1: __u32}
]] x_1_1: __u32}
}
Variable{
x_1
@ -3438,7 +3438,7 @@ TEST_F(SpvModuleScopeVarParserTest, SampleMask_Out_U32_CopyObject) {
const std::string expected = R"(Module{
Struct main_out {
StructMember{[[ BuiltinDecoration{sample_mask}
]] x_1: __u32}
]] x_1_1: __u32}
}
Variable{
x_1
@ -3503,7 +3503,7 @@ TEST_F(SpvModuleScopeVarParserTest, SampleMask_Out_U32_AccessChain) {
const std::string expected = R"(Module{
Struct main_out {
StructMember{[[ BuiltinDecoration{sample_mask}
]] x_1: __u32}
]] x_1_1: __u32}
}
Variable{
x_1
@ -3567,7 +3567,7 @@ TEST_F(SpvModuleScopeVarParserTest, SampleMask_Out_I32_Direct) {
const std::string expected = R"(Module{
Struct main_out {
StructMember{[[ BuiltinDecoration{sample_mask}
]] x_1: __u32}
]] x_1_1: __u32}
}
Variable{
x_1
@ -3634,7 +3634,7 @@ TEST_F(SpvModuleScopeVarParserTest, SampleMask_Out_I32_CopyObject) {
const std::string expected = R"(Module{
Struct main_out {
StructMember{[[ BuiltinDecoration{sample_mask}
]] x_1: __u32}
]] x_1_1: __u32}
}
Variable{
x_1
@ -3701,7 +3701,7 @@ TEST_F(SpvModuleScopeVarParserTest, SampleMask_Out_I32_AccessChain) {
const std::string expected = R"(Module{
Struct main_out {
StructMember{[[ BuiltinDecoration{sample_mask}
]] x_1: __u32}
]] x_1_1: __u32}
}
Variable{
x_1
@ -3844,7 +3844,7 @@ TEST_F(SpvModuleScopeVarParserTest, SampleMask_Out_WithStride) {
Arr_3 -> __array__i32_2_stride_4
Struct main_out {
StructMember{[[ BuiltinDecoration{sample_mask}
]] x_1: __u32}
]] x_1_1: __u32}
}
Variable{
x_1
@ -5549,9 +5549,9 @@ TEST_F(SpvModuleScopeVarParserTest, EntryPointWrapping_IOLocations) {
R"(
Struct main_out {
StructMember{[[ LocationDecoration{0}
]] x_2: __u32}
]] x_2_1: __u32}
StructMember{[[ LocationDecoration{40}
]] x_4: __u32}
]] x_4_1: __u32}
}
Variable{
x_1
@ -5953,7 +5953,7 @@ TEST_F(SpvModuleScopeVarParserTest,
const std::string expected = R"(Module{
Struct main_out {
StructMember{[[ BuiltinDecoration{sample_mask}
]] x_1: __u32}
]] x_1_1: __u32}
}
Variable{
x_1
@ -6026,7 +6026,7 @@ TEST_F(SpvModuleScopeVarParserTest,
const std::string expected = R"(Module{
Struct main_out {
StructMember{[[ BuiltinDecoration{sample_mask}
]] x_1: __u32}
]] x_1_1: __u32}
}
Variable{
x_1
@ -6100,7 +6100,7 @@ TEST_F(SpvModuleScopeVarParserTest,
const std::string expected = R"(Module{
Struct main_out {
StructMember{[[ BuiltinDecoration{frag_depth}
]] x_1: __f32}
]] x_1_1: __f32}
}
Variable{
x_1
@ -6326,7 +6326,7 @@ TEST_F(SpvModuleScopeVarParserTest, Input_FlattenArray_OneLevel) {
const std::string expected = R"(Module{
Struct main_out {
StructMember{[[ BuiltinDecoration{position}
]] x_2: __vec_4__f32}
]] x_2_1: __vec_4__f32}
}
Variable{
x_1
@ -6453,7 +6453,7 @@ TEST_F(SpvModuleScopeVarParserTest, Input_FlattenMatrix) {
const std::string expected = R"(Module{
Struct main_out {
StructMember{[[ BuiltinDecoration{position}
]] x_2: __vec_4__f32}
]] x_2_1: __vec_4__f32}
}
Variable{
x_1
@ -6573,7 +6573,7 @@ TEST_F(SpvModuleScopeVarParserTest, Input_FlattenStruct) {
}
Struct main_out {
StructMember{[[ BuiltinDecoration{position}
]] x_2: __vec_4__f32}
]] x_2_1: __vec_4__f32}
}
Variable{
x_1
@ -6686,7 +6686,7 @@ TEST_F(SpvModuleScopeVarParserTest, Input_FlattenNested) {
const std::string expected = R"(Module{
Struct main_out {
StructMember{[[ BuiltinDecoration{position}
]] x_2: __vec_4__f32}
]] x_2_1: __vec_4__f32}
}
Variable{
x_1
@ -6805,7 +6805,292 @@ TEST_F(SpvModuleScopeVarParserTest, Input_FlattenNested) {
EXPECT_EQ(got, expected) << got;
}
// TODO(dneto): flatting structures
TEST_F(SpvModuleScopeVarParserTest, Output_FlattenArray_OneLevel) {
const std::string assembly = R"(
OpCapability Shader
OpMemoryModel Logical Simple
OpEntryPoint Vertex %main "main" %1 %2
OpDecorate %1 Location 4
OpDecorate %2 BuiltIn Position
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%uint_1 = OpConstant %uint 1
%uint_3 = OpConstant %uint 3
%arr = OpTypeArray %float %uint_3
%11 = OpTypePointer Output %arr
%1 = OpVariable %11 Output
%12 = OpTypePointer Output %v4float
%2 = OpVariable %12 Output
%main = OpFunction %void None %voidfn
%entry = OpLabel
OpReturn
OpFunctionEnd
)";
auto p = parser(test::Assemble(assembly));
ASSERT_TRUE(p->Parse()) << p->error() << assembly;
EXPECT_TRUE(p->error().empty());
const auto got = p->program().to_str();
const std::string expected = R"(Module{
Struct main_out {
StructMember{[[ LocationDecoration{4}
]] x_1_1: __f32}
StructMember{[[ LocationDecoration{5}
]] x_1_2: __f32}
StructMember{[[ LocationDecoration{6}
]] x_1_3: __f32}
StructMember{[[ BuiltinDecoration{position}
]] x_2_1: __vec_4__f32}
}
Variable{
x_1
private
undefined
__array__f32_3
}
Variable{
x_2
private
undefined
__vec_4__f32
}
Function main_1 -> __void
()
{
Return{}
}
Function main -> __type_name_main_out
StageDecoration{vertex}
()
{
Call[not set]{
Identifier[not set]{main_1}
(
)
}
Return{
{
TypeConstructor[not set]{
__type_name_main_out
ArrayAccessor[not set]{
Identifier[not set]{x_1}
ScalarConstructor[not set]{0}
}
ArrayAccessor[not set]{
Identifier[not set]{x_1}
ScalarConstructor[not set]{1}
}
ArrayAccessor[not set]{
Identifier[not set]{x_1}
ScalarConstructor[not set]{2}
}
Identifier[not set]{x_2}
}
}
}
}
}
)";
EXPECT_EQ(got, expected) << got;
}
TEST_F(SpvModuleScopeVarParserTest, Output_FlattenMatrix) {
const std::string assembly = R"(
OpCapability Shader
OpMemoryModel Logical Simple
OpEntryPoint Vertex %main "main" %1 %2
OpDecorate %1 Location 9
OpDecorate %2 BuiltIn Position
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%m2v4float = OpTypeMatrix %v4float 2
%uint = OpTypeInt 32 0
%11 = OpTypePointer Output %m2v4float
%1 = OpVariable %11 Output
%12 = OpTypePointer Output %v4float
%2 = OpVariable %12 Output
%main = OpFunction %void None %voidfn
%entry = OpLabel
OpReturn
OpFunctionEnd
)";
auto p = parser(test::Assemble(assembly));
ASSERT_TRUE(p->Parse()) << p->error() << assembly;
EXPECT_TRUE(p->error().empty());
const auto got = p->program().to_str();
const std::string expected = R"(Module{
Struct main_out {
StructMember{[[ LocationDecoration{9}
]] x_1_1: __vec_4__f32}
StructMember{[[ LocationDecoration{10}
]] x_1_2: __vec_4__f32}
StructMember{[[ BuiltinDecoration{position}
]] x_2_1: __vec_4__f32}
}
Variable{
x_1
private
undefined
__mat_4_2__f32
}
Variable{
x_2
private
undefined
__vec_4__f32
}
Function main_1 -> __void
()
{
Return{}
}
Function main -> __type_name_main_out
StageDecoration{vertex}
()
{
Call[not set]{
Identifier[not set]{main_1}
(
)
}
Return{
{
TypeConstructor[not set]{
__type_name_main_out
ArrayAccessor[not set]{
Identifier[not set]{x_1}
ScalarConstructor[not set]{0}
}
ArrayAccessor[not set]{
Identifier[not set]{x_1}
ScalarConstructor[not set]{1}
}
Identifier[not set]{x_2}
}
}
}
}
}
)";
EXPECT_EQ(got, expected) << got;
}
TEST_F(SpvModuleScopeVarParserTest, Output_FlattenStruct) {
const std::string assembly = R"(
OpCapability Shader
OpMemoryModel Logical Simple
OpEntryPoint Vertex %main "main" %1 %2
OpName %strct "Communicators"
OpMemberName %strct 0 "alice"
OpMemberName %strct 1 "bob"
OpDecorate %1 Location 9
OpDecorate %2 BuiltIn Position
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%strct = OpTypeStruct %float %v4float
%11 = OpTypePointer Output %strct
%1 = OpVariable %11 Output
%12 = OpTypePointer Output %v4float
%2 = OpVariable %12 Output
%main = OpFunction %void None %voidfn
%entry = OpLabel
OpReturn
OpFunctionEnd
)";
auto p = parser(test::Assemble(assembly));
ASSERT_TRUE(p->Parse()) << p->error() << assembly;
EXPECT_TRUE(p->error().empty());
const auto got = p->program().to_str();
const std::string expected = R"(Module{
Struct Communicators {
StructMember{alice: __f32}
StructMember{bob: __vec_4__f32}
}
Struct main_out {
StructMember{[[ LocationDecoration{9}
]] x_1_1: __f32}
StructMember{[[ LocationDecoration{10}
]] x_1_2: __vec_4__f32}
StructMember{[[ BuiltinDecoration{position}
]] x_2_1: __vec_4__f32}
}
Variable{
x_1
private
undefined
__type_name_Communicators
}
Variable{
x_2
private
undefined
__vec_4__f32
}
Function main_1 -> __void
()
{
Return{}
}
Function main -> __type_name_main_out
StageDecoration{vertex}
()
{
Call[not set]{
Identifier[not set]{main_1}
(
)
}
Return{
{
TypeConstructor[not set]{
__type_name_main_out
MemberAccessor[not set]{
Identifier[not set]{x_1}
Identifier[not set]{alice}
}
MemberAccessor[not set]{
Identifier[not set]{x_1}
Identifier[not set]{bob}
}
Identifier[not set]{x_2}
}
}
}
}
}
)";
EXPECT_EQ(got, expected) << got;
}
} // namespace
} // namespace spirv

View File

@ -1545,13 +1545,13 @@ void main_1() {
}
struct main_out {
float4 x_GLF_color;
float4 x_GLF_color_1;
};
struct tint_symbol_1 {
float4 gl_FragCoord_param : SV_Position;
};
struct tint_symbol_2 {
float4 x_GLF_color : SV_Target0;
float4 x_GLF_color_1 : SV_Target0;
};
tint_symbol_2 main(tint_symbol_1 tint_symbol) {
@ -1559,6 +1559,6 @@ tint_symbol_2 main(tint_symbol_1 tint_symbol) {
gl_FragCoord = gl_FragCoord_param;
main_1();
const main_out tint_symbol_3 = {x_GLF_color};
const tint_symbol_2 tint_symbol_83 = {tint_symbol_3.x_GLF_color};
const tint_symbol_2 tint_symbol_83 = {tint_symbol_3.x_GLF_color_1};
return tint_symbol_83;
}

View File

@ -11,10 +11,10 @@ struct buf0 {
/* 0x0000 */ packed_float2 resolution;
};
struct main_out {
float4 x_GLF_color;
float4 x_GLF_color_1;
};
struct tint_symbol_2 {
float4 x_GLF_color [[color(0)]];
float4 x_GLF_color_1 [[color(0)]];
};
void swap_i1_i1_(thread int* const i, thread int* const j, thread QuicksortObject* const tint_symbol_83) {
@ -1553,8 +1553,8 @@ fragment tint_symbol_2 tint_symbol(float4 gl_FragCoord_param [[position]], const
thread float4 tint_symbol_91 = 0.0f;
tint_symbol_89 = gl_FragCoord_param;
main_1(x_188, &(tint_symbol_90), &(tint_symbol_89), &(tint_symbol_91));
main_out const tint_symbol_3 = {.x_GLF_color=tint_symbol_91};
tint_symbol_2 const tint_symbol_82 = {.x_GLF_color=tint_symbol_3.x_GLF_color};
main_out const tint_symbol_3 = {.x_GLF_color_1=tint_symbol_91};
tint_symbol_2 const tint_symbol_82 = {.x_GLF_color_1=tint_symbol_3.x_GLF_color_1};
return tint_symbol_82;
}

View File

@ -47,7 +47,7 @@
OpName %i_2 "i_2"
OpName %uv "uv"
OpName %main_out "main_out"
OpMemberName %main_out 0 "x_GLF_color"
OpMemberName %main_out 0 "x_GLF_color_1"
OpName %tint_symbol_3 "tint_symbol_3"
OpName %tint_symbol_1 "tint_symbol_1"
OpName %main "main"

View File

@ -1494,7 +1494,7 @@ fn main_1() {
struct main_out {
[[location(0)]]
x_GLF_color : vec4<f32>;
x_GLF_color_1 : vec4<f32>;
};
[[stage(fragment)]]