resolver: Validate storage buffers have the [[block]] decoration
Fixed: tint:94 Change-Id: I1d3e512c030ec16031b8c8fcfbde0cd1db5d1ea4 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/48380 Reviewed-by: Ryan Harrison <rharrison@chromium.org> Reviewed-by: James Price <jrprice@google.com> Commit-Queue: Ben Clayton <bclayton@google.com>
This commit is contained in:
parent
8b50537767
commit
85bfea6edd
|
@ -241,17 +241,6 @@ class InspectorHelper : public ProgramBuilder {
|
|||
return struct_type;
|
||||
}
|
||||
|
||||
/// Returns true if the struct with `member_types` requires a block decoration
|
||||
/// @param member_types a vector of member types
|
||||
/// @returns true if block decoration is required
|
||||
bool StructRequiresBlockDecoration(
|
||||
std::vector<type::Type*> member_types) const {
|
||||
// Structure needs a [[block]] attribute if the last member is a
|
||||
// dynamically-sized array.
|
||||
return member_types.back()->Is<type::Array>(
|
||||
[](auto&& a) { return a->IsRuntimeArray(); });
|
||||
}
|
||||
|
||||
/// Generates types appropriate for using in a storage buffer
|
||||
/// @param name name for the type
|
||||
/// @param member_types a vector of member types
|
||||
|
@ -261,8 +250,7 @@ class InspectorHelper : public ProgramBuilder {
|
|||
std::tuple<type::Struct*, type::AccessControl*> MakeStorageBufferTypes(
|
||||
const std::string& name,
|
||||
std::vector<type::Type*> member_types) {
|
||||
bool is_block = StructRequiresBlockDecoration(member_types);
|
||||
auto* struct_type = MakeStructType(name, member_types, is_block);
|
||||
auto* struct_type = MakeStructType(name, member_types, true);
|
||||
auto* access_type = create<type::AccessControl>(
|
||||
ast::AccessControl::kReadWrite, struct_type);
|
||||
return {struct_type, std::move(access_type)};
|
||||
|
@ -277,8 +265,7 @@ class InspectorHelper : public ProgramBuilder {
|
|||
std::tuple<type::Struct*, type::AccessControl*>
|
||||
MakeReadOnlyStorageBufferTypes(const std::string& name,
|
||||
std::vector<type::Type*> member_types) {
|
||||
bool is_block = StructRequiresBlockDecoration(member_types);
|
||||
auto* struct_type = MakeStructType(name, member_types, is_block);
|
||||
auto* struct_type = MakeStructType(name, member_types, true);
|
||||
auto* access_type =
|
||||
create<type::AccessControl>(ast::AccessControl::kReadOnly, struct_type);
|
||||
return {struct_type, std::move(access_type)};
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "src/resolver/resolver.h"
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "src/ast/struct_block_decoration.h"
|
||||
#include "src/resolver/resolver_test_helper.h"
|
||||
#include "src/sem/struct.h"
|
||||
#include "src/type/access_control_type.h"
|
||||
|
@ -26,7 +27,8 @@ namespace {
|
|||
using ResolverHostShareableValidationTest = ResolverTest;
|
||||
|
||||
TEST_F(ResolverHostShareableValidationTest, BoolMember) {
|
||||
auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.bool_())});
|
||||
auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.bool_())},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
auto* a = ty.access(ast::AccessControl::kReadOnly, s);
|
||||
Global(Source{{56, 78}}, "g", a, ast::StorageClass::kStorage);
|
||||
|
||||
|
@ -40,7 +42,8 @@ TEST_F(ResolverHostShareableValidationTest, BoolMember) {
|
|||
}
|
||||
|
||||
TEST_F(ResolverHostShareableValidationTest, BoolVectorMember) {
|
||||
auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.vec3<bool>())});
|
||||
auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.vec3<bool>())},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
auto* a = ty.access(ast::AccessControl::kReadOnly, s);
|
||||
Global(Source{{56, 78}}, "g", a, ast::StorageClass::kStorage);
|
||||
|
||||
|
@ -55,7 +58,8 @@ TEST_F(ResolverHostShareableValidationTest, BoolVectorMember) {
|
|||
|
||||
TEST_F(ResolverHostShareableValidationTest, Aliases) {
|
||||
auto* a1 = ty.alias("a1", ty.bool_());
|
||||
auto* s = Structure("S", {Member(Source{{12, 34}}, "x", a1)});
|
||||
auto* s = Structure("S", {Member(Source{{12, 34}}, "x", a1)},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
auto* ac = ty.access(ast::AccessControl::kReadOnly, s);
|
||||
auto* a2 = ty.alias("a2", ac);
|
||||
Global(Source{{56, 78}}, "g", a2, ast::StorageClass::kStorage);
|
||||
|
@ -74,7 +78,8 @@ TEST_F(ResolverHostShareableValidationTest, NestedStructures) {
|
|||
auto* i2 = Structure("I2", {Member(Source{{3, 4}}, "y", i1)});
|
||||
auto* i3 = Structure("I3", {Member(Source{{5, 6}}, "z", i2)});
|
||||
|
||||
auto* s = Structure("S", {Member(Source{{7, 8}}, "m", i3)});
|
||||
auto* s = Structure("S", {Member(Source{{7, 8}}, "m", i3)},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
auto* a = ty.access(ast::AccessControl::kReadOnly, s);
|
||||
Global(Source{{9, 10}}, "g", a, ast::StorageClass::kStorage);
|
||||
|
||||
|
@ -109,7 +114,8 @@ TEST_F(ResolverHostShareableValidationTest, NoError) {
|
|||
Member(Source{{6, 1}}, "z3", ty.alias("a2", i2)),
|
||||
});
|
||||
|
||||
auto* s = Structure("S", {Member(Source{{7, 8}}, "m", i3)});
|
||||
auto* s = Structure("S", {Member(Source{{7, 8}}, "m", i3)},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
auto* a = ty.access(ast::AccessControl::kReadOnly, s);
|
||||
Global(Source{{9, 10}}, "g", a, ast::StorageClass::kStorage);
|
||||
|
||||
|
|
|
@ -284,27 +284,6 @@ bool Resolver::GlobalVariable(ast::Variable* var) {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (info->storage_class == ast::StorageClass::kStorage) {
|
||||
// https://gpuweb.github.io/gpuweb/wgsl/#variable-declaration
|
||||
// Variables in the storage storage class and variables with a storage
|
||||
// texture type must have an access attribute applied to the store type.
|
||||
|
||||
// https://gpuweb.github.io/gpuweb/wgsl/#module-scope-variables
|
||||
// A variable in the storage storage class is a storage buffer variable. Its
|
||||
// store type must be a host-shareable structure type with block attribute,
|
||||
// satisfying the storage class constraints.
|
||||
|
||||
auto* access = info->type->As<type::AccessControl>();
|
||||
auto* str = access ? access->type()->As<type::Struct>() : nullptr;
|
||||
if (!str) {
|
||||
diagnostics_.add_error(
|
||||
"variables declared in the <storage> storage class must be of an "
|
||||
"[[access]] qualified structure type",
|
||||
var->source());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto* deco : var->decorations()) {
|
||||
Mark(deco);
|
||||
if (!(deco->Is<ast::BindingDecoration>() ||
|
||||
|
@ -325,6 +304,10 @@ bool Resolver::GlobalVariable(ast::Variable* var) {
|
|||
}
|
||||
}
|
||||
|
||||
if (!ValidateGlobalVariable(info)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ApplyStorageClassUsageToType(var->declared_storage_class(), info->type,
|
||||
var->source())) {
|
||||
diagnostics_.add_note("while instantiating variable " +
|
||||
|
@ -336,6 +319,43 @@ bool Resolver::GlobalVariable(ast::Variable* var) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Resolver::ValidateGlobalVariable(const VariableInfo* info) {
|
||||
if (info->storage_class == ast::StorageClass::kStorage) {
|
||||
// https://gpuweb.github.io/gpuweb/wgsl/#variable-declaration
|
||||
// Variables in the storage storage class and variables with a storage
|
||||
// texture type must have an access attribute applied to the store type.
|
||||
|
||||
// https://gpuweb.github.io/gpuweb/wgsl/#module-scope-variables
|
||||
// A variable in the storage storage class is a storage buffer variable. Its
|
||||
// store type must be a host-shareable structure type with block attribute,
|
||||
// satisfying the storage class constraints.
|
||||
|
||||
auto* access = info->type->As<type::AccessControl>();
|
||||
auto* str = access ? access->type()->As<type::Struct>() : nullptr;
|
||||
if (!str) {
|
||||
diagnostics_.add_error(
|
||||
"variables declared in the <storage> storage class must be of an "
|
||||
"[[access]] qualified structure type",
|
||||
info->declaration->source());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!str->IsBlockDecorated()) {
|
||||
diagnostics_.add_error(
|
||||
"structure used as a storage buffer must be declared with the "
|
||||
"[[block]] decoration",
|
||||
str->impl()->source());
|
||||
if (info->declaration->source().range.begin.line) {
|
||||
diagnostics_.add_note("structure used as storage buffer here",
|
||||
info->declaration->source());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Resolver::ValidateVariable(const ast::Variable* var) {
|
||||
auto* type = variable_to_info_[var]->type;
|
||||
if (auto* r = type->UnwrapAll()->As<type::Array>()) {
|
||||
|
|
|
@ -240,6 +240,7 @@ class Resolver {
|
|||
bool ValidateBinary(ast::BinaryExpression* expr);
|
||||
bool ValidateEntryPoint(const ast::Function* func);
|
||||
bool ValidateFunction(const ast::Function* func);
|
||||
bool ValidateGlobalVariable(const VariableInfo* var);
|
||||
bool ValidateMatrixConstructor(const type::Matrix* matrix_type,
|
||||
const ast::ExpressionList& values);
|
||||
bool ValidateParameter(const ast::Variable* param);
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "src/ast/loop_statement.h"
|
||||
#include "src/ast/return_statement.h"
|
||||
#include "src/ast/stage_decoration.h"
|
||||
#include "src/ast/struct_block_decoration.h"
|
||||
#include "src/ast/switch_statement.h"
|
||||
#include "src/ast/unary_op_expression.h"
|
||||
#include "src/ast/variable_decl_statement.h"
|
||||
|
@ -764,7 +765,8 @@ TEST_F(ResolverTest, Function_Parameters) {
|
|||
}
|
||||
|
||||
TEST_F(ResolverTest, Function_RegisterInputOutputVariables) {
|
||||
auto* s = Structure("S", {Member("m", ty.u32())});
|
||||
auto* s = Structure("S", {Member("m", ty.u32())},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
auto* a = ty.access(ast::AccessControl::kReadOnly, s);
|
||||
|
||||
auto* in_var = Global("in_var", ty.f32(), ast::StorageClass::kInput);
|
||||
|
@ -799,7 +801,8 @@ TEST_F(ResolverTest, Function_RegisterInputOutputVariables) {
|
|||
}
|
||||
|
||||
TEST_F(ResolverTest, Function_RegisterInputOutputVariables_SubFunction) {
|
||||
auto* s = Structure("S", {Member("m", ty.u32())});
|
||||
auto* s = Structure("S", {Member("m", ty.u32())},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
auto* a = ty.access(ast::AccessControl::kReadOnly, s);
|
||||
|
||||
auto* in_var = Global("in_var", ty.f32(), ast::StorageClass::kInput);
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "src/resolver/resolver.h"
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "src/ast/struct_block_decoration.h"
|
||||
#include "src/resolver/resolver_test_helper.h"
|
||||
#include "src/sem/struct.h"
|
||||
#include "src/type/access_control_type.h"
|
||||
|
@ -34,7 +35,7 @@ TEST_F(ResolverStorageClassValidationTest, GlobalVariableNoStorageClass_Fail) {
|
|||
"12:34 error v-0022: global variables must have a storage class");
|
||||
}
|
||||
|
||||
TEST_F(ResolverStorageClassValidationTest, Bool) {
|
||||
TEST_F(ResolverStorageClassValidationTest, StorageBufferBool) {
|
||||
// var<storage> g : bool;
|
||||
Global(Source{{56, 78}}, "g", ty.bool_(), ast::StorageClass::kStorage);
|
||||
|
||||
|
@ -45,7 +46,7 @@ TEST_F(ResolverStorageClassValidationTest, Bool) {
|
|||
R"(56:78 error: variables declared in the <storage> storage class must be of an [[access]] qualified structure type)");
|
||||
}
|
||||
|
||||
TEST_F(ResolverStorageClassValidationTest, Pointer) {
|
||||
TEST_F(ResolverStorageClassValidationTest, StorageBufferPointer) {
|
||||
// var<storage> g : ptr<i32, input>;
|
||||
Global(Source{{56, 78}}, "g", ty.pointer<i32>(ast::StorageClass::kInput),
|
||||
ast::StorageClass::kStorage);
|
||||
|
@ -57,7 +58,7 @@ TEST_F(ResolverStorageClassValidationTest, Pointer) {
|
|||
R"(56:78 error: variables declared in the <storage> storage class must be of an [[access]] qualified structure type)");
|
||||
}
|
||||
|
||||
TEST_F(ResolverStorageClassValidationTest, Array) {
|
||||
TEST_F(ResolverStorageClassValidationTest, StorageBufferArray) {
|
||||
// var<storage> g : [[access(read)]] array<S, 3>;
|
||||
auto* s = Structure("S", {Member("a", ty.f32())});
|
||||
auto* a = ty.array(s, 3);
|
||||
|
@ -71,7 +72,7 @@ TEST_F(ResolverStorageClassValidationTest, Array) {
|
|||
R"(56:78 error: variables declared in the <storage> storage class must be of an [[access]] qualified structure type)");
|
||||
}
|
||||
|
||||
TEST_F(ResolverStorageClassValidationTest, BoolAlias) {
|
||||
TEST_F(ResolverStorageClassValidationTest, StorageBufferBoolAlias) {
|
||||
// type a = bool;
|
||||
// var<storage> g : [[access(read)]] a;
|
||||
auto* a = ty.alias("a", ty.bool_());
|
||||
|
@ -84,7 +85,7 @@ TEST_F(ResolverStorageClassValidationTest, BoolAlias) {
|
|||
R"(56:78 error: variables declared in the <storage> storage class must be of an [[access]] qualified structure type)");
|
||||
}
|
||||
|
||||
TEST_F(ResolverStorageClassValidationTest, NoAccessControl) {
|
||||
TEST_F(ResolverStorageClassValidationTest, StorageBufferNoAccessControl) {
|
||||
// var<storage> g : S;
|
||||
auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())});
|
||||
Global(Source{{56, 78}}, "g", s, ast::StorageClass::kStorage);
|
||||
|
@ -96,20 +97,39 @@ TEST_F(ResolverStorageClassValidationTest, NoAccessControl) {
|
|||
R"(56:78 error: variables declared in the <storage> storage class must be of an [[access]] qualified structure type)");
|
||||
}
|
||||
|
||||
TEST_F(ResolverStorageClassValidationTest, NoError_Basic) {
|
||||
TEST_F(ResolverStorageClassValidationTest, StorageBufferNoBlockDecoration) {
|
||||
// struct S { x : i32 };
|
||||
// var<storage> g : [[access(read)]] S;
|
||||
auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())});
|
||||
auto* s = Structure(Source{{12, 34}}, "S", {Member("x", ty.i32())});
|
||||
auto* a = ty.access(ast::AccessControl::kReadOnly, s);
|
||||
Global(Source{{56, 78}}, "g", a, ast::StorageClass::kStorage);
|
||||
|
||||
ASSERT_FALSE(r()->Resolve());
|
||||
|
||||
EXPECT_EQ(
|
||||
r()->error(),
|
||||
R"(12:34 error: structure used as a storage buffer must be declared with the [[block]] decoration
|
||||
56:78 note: structure used as storage buffer here)");
|
||||
}
|
||||
|
||||
TEST_F(ResolverStorageClassValidationTest, StorageBufferNoError_Basic) {
|
||||
// [[block]] struct S { x : i32 };
|
||||
// var<storage> g : [[access(read)]] S;
|
||||
auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
auto* a = ty.access(ast::AccessControl::kReadOnly, s);
|
||||
Global(Source{{56, 78}}, "g", a, ast::StorageClass::kStorage);
|
||||
|
||||
ASSERT_TRUE(r()->Resolve());
|
||||
}
|
||||
|
||||
TEST_F(ResolverStorageClassValidationTest, NoError_Aliases) {
|
||||
TEST_F(ResolverStorageClassValidationTest, StorageBufferNoError_Aliases) {
|
||||
// [[block]] struct S { x : i32 };
|
||||
// type a1 = S;
|
||||
// type a2 = [[access(read)]] a1;
|
||||
// var<storage> g : a2;
|
||||
auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())});
|
||||
auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
auto* a1 = ty.alias("a1", s);
|
||||
auto* ac = ty.access(ast::AccessControl::kReadOnly, a1);
|
||||
auto* a2 = ty.alias("a2", ac);
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "src/resolver/resolver.h"
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "src/ast/struct_block_decoration.h"
|
||||
#include "src/resolver/resolver_test_helper.h"
|
||||
#include "src/sem/struct.h"
|
||||
|
||||
|
@ -167,7 +168,8 @@ TEST_F(ResolverStorageClassUseTest, StructReachableViaLocalArray) {
|
|||
}
|
||||
|
||||
TEST_F(ResolverStorageClassUseTest, StructMultipleStorageClassUses) {
|
||||
auto* s = Structure("S", {Member("a", ty.f32())});
|
||||
auto* s = Structure("S", {Member("a", ty.f32())},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
auto* ac = ty.access(ast::AccessControl::kReadOnly, s);
|
||||
Global("x", s, ast::StorageClass::kUniform);
|
||||
Global("y", ac, ast::StorageClass::kStorage);
|
||||
|
|
|
@ -388,10 +388,12 @@ void frag_main() {
|
|||
|
||||
TEST_F(HlslGeneratorImplTest_Function,
|
||||
Emit_Decoration_EntryPoint_With_RW_StorageBuffer_Read) {
|
||||
auto* s = Structure("Data", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.f32()),
|
||||
});
|
||||
auto* s = Structure("Data",
|
||||
{
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.f32()),
|
||||
},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
|
||||
type::AccessControl ac(ast::AccessControl::kReadWrite, s);
|
||||
|
||||
|
@ -432,10 +434,12 @@ void frag_main() {
|
|||
|
||||
TEST_F(HlslGeneratorImplTest_Function,
|
||||
Emit_Decoration_EntryPoint_With_RO_StorageBuffer_Read) {
|
||||
auto* s = Structure("Data", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.f32()),
|
||||
});
|
||||
auto* s = Structure("Data",
|
||||
{
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.f32()),
|
||||
},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
|
||||
type::AccessControl ac(ast::AccessControl::kReadOnly, s);
|
||||
|
||||
|
@ -476,10 +480,12 @@ void frag_main() {
|
|||
|
||||
TEST_F(HlslGeneratorImplTest_Function,
|
||||
Emit_Decoration_EntryPoint_With_WO_StorageBuffer_Store) {
|
||||
auto* s = Structure("Data", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.f32()),
|
||||
});
|
||||
auto* s = Structure("Data",
|
||||
{
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.f32()),
|
||||
},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
|
||||
type::AccessControl ac(ast::AccessControl::kWriteOnly, s);
|
||||
|
||||
|
@ -518,10 +524,12 @@ void frag_main() {
|
|||
|
||||
TEST_F(HlslGeneratorImplTest_Function,
|
||||
Emit_Decoration_EntryPoint_With_StorageBuffer_Store) {
|
||||
auto* s = Structure("Data", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.f32()),
|
||||
});
|
||||
auto* s = Structure("Data",
|
||||
{
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.f32()),
|
||||
},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
|
||||
type::AccessControl ac(ast::AccessControl::kReadWrite, s);
|
||||
|
||||
|
@ -777,7 +785,8 @@ void frag_main() {
|
|||
|
||||
TEST_F(HlslGeneratorImplTest_Function,
|
||||
Emit_Decoration_Called_By_EntryPoint_With_StorageBuffer) {
|
||||
auto* s = Structure("S", {Member("x", ty.f32())});
|
||||
auto* s = Structure("S", {Member("x", ty.f32())},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
auto* ac = ty.access(ast::AccessControl::kReadWrite, s);
|
||||
Global("coord", ac, ast::StorageClass::kStorage, nullptr,
|
||||
ast::DecorationList{
|
||||
|
|
|
@ -173,10 +173,12 @@ TEST_F(HlslGeneratorImplTest_Type, EmitType_StructDecl) {
|
|||
}
|
||||
|
||||
TEST_F(HlslGeneratorImplTest_Type, EmitType_StructDecl_OmittedIfStorageBuffer) {
|
||||
auto* s = Structure("S", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.f32()),
|
||||
});
|
||||
auto* s = Structure("S",
|
||||
{
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.f32()),
|
||||
},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
Global("g", ty.access(ast::AccessControl::kReadWrite, s),
|
||||
ast::StorageClass::kStorage);
|
||||
|
||||
|
|
|
@ -307,10 +307,12 @@ vertex tint_symbol_2 vert_main2() {
|
|||
|
||||
TEST_F(MslGeneratorImplTest,
|
||||
Emit_FunctionDecoration_EntryPoint_With_RW_StorageBuffer) {
|
||||
auto* s = Structure("Data", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.f32()),
|
||||
});
|
||||
auto* s = Structure("Data",
|
||||
{
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.f32()),
|
||||
},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
|
||||
type::AccessControl ac(ast::AccessControl::kReadWrite, s);
|
||||
|
||||
|
@ -351,10 +353,12 @@ fragment void frag_main(device Data& coord [[buffer(0)]]) {
|
|||
|
||||
TEST_F(MslGeneratorImplTest,
|
||||
Emit_FunctionDecoration_EntryPoint_With_RO_StorageBuffer) {
|
||||
auto* s = Structure("Data", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.f32()),
|
||||
});
|
||||
auto* s = Structure("Data",
|
||||
{
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.f32()),
|
||||
},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
|
||||
type::AccessControl ac(ast::AccessControl::kReadOnly, s);
|
||||
|
||||
|
@ -609,10 +613,12 @@ fragment void frag_main(constant float4& coord [[buffer(0)]]) {
|
|||
|
||||
TEST_F(MslGeneratorImplTest,
|
||||
Emit_FunctionDecoration_Called_By_EntryPoint_With_RW_StorageBuffer) {
|
||||
auto* s = Structure("Data", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.f32()),
|
||||
});
|
||||
auto* s = Structure("Data",
|
||||
{
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.f32()),
|
||||
},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
|
||||
type::AccessControl ac(ast::AccessControl::kReadWrite, s);
|
||||
|
||||
|
@ -665,10 +671,12 @@ fragment void frag_main(device Data& coord [[buffer(0)]]) {
|
|||
|
||||
TEST_F(MslGeneratorImplTest,
|
||||
Emit_FunctionDecoration_Called_By_EntryPoint_With_RO_StorageBuffer) {
|
||||
auto* s = Structure("Data", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.f32()),
|
||||
});
|
||||
auto* s = Structure("Data",
|
||||
{
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.f32()),
|
||||
},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
|
||||
type::AccessControl ac(ast::AccessControl::kReadOnly, s);
|
||||
|
||||
|
|
|
@ -201,35 +201,37 @@ TEST_F(MslGeneratorImplTest, EmitType_StructDecl) {
|
|||
}
|
||||
|
||||
TEST_F(MslGeneratorImplTest, EmitType_Struct_Layout_NonComposites) {
|
||||
auto* s = Structure(
|
||||
"S", {
|
||||
Member("a", ty.i32(), {MemberSize(32)}),
|
||||
Member("b", ty.f32(), {MemberAlign(128), MemberSize(128)}),
|
||||
Member("c", ty.vec2<f32>()),
|
||||
Member("d", ty.u32()),
|
||||
Member("e", ty.vec3<f32>()),
|
||||
Member("f", ty.u32()),
|
||||
Member("g", ty.vec4<f32>()),
|
||||
Member("h", ty.u32()),
|
||||
Member("i", ty.mat2x2<f32>()),
|
||||
Member("j", ty.u32()),
|
||||
Member("k", ty.mat2x3<f32>()),
|
||||
Member("l", ty.u32()),
|
||||
Member("m", ty.mat2x4<f32>()),
|
||||
Member("n", ty.u32()),
|
||||
Member("o", ty.mat3x2<f32>()),
|
||||
Member("p", ty.u32()),
|
||||
Member("q", ty.mat3x3<f32>()),
|
||||
Member("r", ty.u32()),
|
||||
Member("s", ty.mat3x4<f32>()),
|
||||
Member("t", ty.u32()),
|
||||
Member("u", ty.mat4x2<f32>()),
|
||||
Member("v", ty.u32()),
|
||||
Member("w", ty.mat4x3<f32>()),
|
||||
Member("x", ty.u32()),
|
||||
Member("y", ty.mat4x4<f32>()),
|
||||
Member("z", ty.f32()),
|
||||
});
|
||||
auto* s =
|
||||
Structure("S",
|
||||
{
|
||||
Member("a", ty.i32(), {MemberSize(32)}),
|
||||
Member("b", ty.f32(), {MemberAlign(128), MemberSize(128)}),
|
||||
Member("c", ty.vec2<f32>()),
|
||||
Member("d", ty.u32()),
|
||||
Member("e", ty.vec3<f32>()),
|
||||
Member("f", ty.u32()),
|
||||
Member("g", ty.vec4<f32>()),
|
||||
Member("h", ty.u32()),
|
||||
Member("i", ty.mat2x2<f32>()),
|
||||
Member("j", ty.u32()),
|
||||
Member("k", ty.mat2x3<f32>()),
|
||||
Member("l", ty.u32()),
|
||||
Member("m", ty.mat2x4<f32>()),
|
||||
Member("n", ty.u32()),
|
||||
Member("o", ty.mat3x2<f32>()),
|
||||
Member("p", ty.u32()),
|
||||
Member("q", ty.mat3x3<f32>()),
|
||||
Member("r", ty.u32()),
|
||||
Member("s", ty.mat3x4<f32>()),
|
||||
Member("t", ty.u32()),
|
||||
Member("u", ty.mat4x2<f32>()),
|
||||
Member("v", ty.u32()),
|
||||
Member("w", ty.mat4x3<f32>()),
|
||||
Member("x", ty.u32()),
|
||||
Member("y", ty.mat4x4<f32>()),
|
||||
Member("z", ty.f32()),
|
||||
},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
|
||||
Global("G", ty.access(ast::AccessControl::kReadOnly, s),
|
||||
ast::StorageClass::kStorage);
|
||||
|
@ -326,13 +328,15 @@ TEST_F(MslGeneratorImplTest, EmitType_Struct_Layout_Structures) {
|
|||
Member("b", ty.f32()),
|
||||
});
|
||||
|
||||
auto* s = Structure("S", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", inner_x),
|
||||
Member("c", ty.f32()),
|
||||
Member("d", inner_y),
|
||||
Member("e", ty.f32()),
|
||||
});
|
||||
auto* s = Structure("S",
|
||||
{
|
||||
Member("a", ty.i32()),
|
||||
Member("b", inner_x),
|
||||
Member("c", ty.f32()),
|
||||
Member("d", inner_y),
|
||||
Member("e", ty.f32()),
|
||||
},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
|
||||
Global("G", ty.access(ast::AccessControl::kReadOnly, s),
|
||||
ast::StorageClass::kStorage);
|
||||
|
@ -523,7 +527,8 @@ TEST_F(MslGeneratorImplTest, AttemptTintPadSymbolCollision) {
|
|||
Member("tint_pad_28", ty.u32()),
|
||||
Member("tint_pad_4", ty.mat4x4<f32>()),
|
||||
Member("tint_pad_21", ty.f32()),
|
||||
});
|
||||
},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
|
||||
Global("G", ty.access(ast::AccessControl::kReadOnly, s),
|
||||
ast::StorageClass::kStorage);
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include "src/ast/constant_id_decoration.h"
|
||||
#include "src/ast/stage_decoration.h"
|
||||
#include "src/ast/struct_block_decoration.h"
|
||||
#include "src/writer/spirv/spv_dump.h"
|
||||
#include "src/writer/spirv/test_helper.h"
|
||||
|
||||
|
@ -387,10 +388,12 @@ TEST_F(BuilderTest, GlobalVar_DeclReadOnly) {
|
|||
// };
|
||||
// var b : [[access(read)]] A
|
||||
|
||||
auto* A = Structure("A", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.i32()),
|
||||
});
|
||||
auto* A = Structure("A",
|
||||
{
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.i32()),
|
||||
},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
auto* ac = create<type::AccessControl>(ast::AccessControl::kReadOnly, A);
|
||||
|
||||
auto* var = Global("b", ac, ast::StorageClass::kStorage);
|
||||
|
@ -399,7 +402,8 @@ TEST_F(BuilderTest, GlobalVar_DeclReadOnly) {
|
|||
|
||||
EXPECT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
|
||||
|
||||
EXPECT_EQ(DumpInstructions(b.annots()), R"(OpMemberDecorate %3 0 Offset 0
|
||||
EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %3 Block
|
||||
OpMemberDecorate %3 0 Offset 0
|
||||
OpMemberDecorate %3 0 NonWritable
|
||||
OpMemberDecorate %3 1 Offset 4
|
||||
OpMemberDecorate %3 1 NonWritable
|
||||
|
@ -423,7 +427,8 @@ TEST_F(BuilderTest, GlobalVar_TypeAliasDeclReadOnly) {
|
|||
// type B = A;
|
||||
// var b : [[access(read)]] B
|
||||
|
||||
auto* A = Structure("A", {Member("a", ty.i32())});
|
||||
auto* A = Structure("A", {Member("a", ty.i32())},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
auto* B = ty.alias("B", A);
|
||||
auto* ac = create<type::AccessControl>(ast::AccessControl::kReadOnly, B);
|
||||
auto* var = Global("b", ac, ast::StorageClass::kStorage);
|
||||
|
@ -432,7 +437,8 @@ TEST_F(BuilderTest, GlobalVar_TypeAliasDeclReadOnly) {
|
|||
|
||||
EXPECT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
|
||||
|
||||
EXPECT_EQ(DumpInstructions(b.annots()), R"(OpMemberDecorate %3 0 Offset 0
|
||||
EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %3 Block
|
||||
OpMemberDecorate %3 0 Offset 0
|
||||
OpMemberDecorate %3 0 NonWritable
|
||||
)");
|
||||
EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %3 "A"
|
||||
|
@ -453,7 +459,8 @@ TEST_F(BuilderTest, GlobalVar_TypeAliasAssignReadOnly) {
|
|||
// type B = [[access(read)]] A;
|
||||
// var b : B
|
||||
|
||||
auto* A = Structure("A", {Member("a", ty.i32())});
|
||||
auto* A = Structure("A", {Member("a", ty.i32())},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
auto* ac = create<type::AccessControl>(ast::AccessControl::kReadOnly, A);
|
||||
auto* B = ty.alias("B", ac);
|
||||
auto* var = Global("b", B, ast::StorageClass::kStorage);
|
||||
|
@ -462,7 +469,8 @@ TEST_F(BuilderTest, GlobalVar_TypeAliasAssignReadOnly) {
|
|||
|
||||
EXPECT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
|
||||
|
||||
EXPECT_EQ(DumpInstructions(b.annots()), R"(OpMemberDecorate %3 0 Offset 0
|
||||
EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %3 Block
|
||||
OpMemberDecorate %3 0 Offset 0
|
||||
OpMemberDecorate %3 0 NonWritable
|
||||
)");
|
||||
EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %3 "A"
|
||||
|
@ -483,7 +491,8 @@ TEST_F(BuilderTest, GlobalVar_TwoVarDeclReadOnly) {
|
|||
// var b : [[access(read)]] A
|
||||
// var c : [[access(read_write)]] A
|
||||
|
||||
auto* A = Structure("A", {Member("a", ty.i32())});
|
||||
auto* A = Structure("A", {Member("a", ty.i32())},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
type::AccessControl read{ast::AccessControl::kReadOnly, A};
|
||||
type::AccessControl rw{ast::AccessControl::kReadWrite, A};
|
||||
|
||||
|
@ -496,8 +505,10 @@ TEST_F(BuilderTest, GlobalVar_TwoVarDeclReadOnly) {
|
|||
EXPECT_TRUE(b.GenerateGlobalVariable(var_c)) << b.error();
|
||||
|
||||
EXPECT_EQ(DumpInstructions(b.annots()),
|
||||
R"(OpMemberDecorate %3 0 Offset 0
|
||||
R"(OpDecorate %3 Block
|
||||
OpMemberDecorate %3 0 Offset 0
|
||||
OpMemberDecorate %3 0 NonWritable
|
||||
OpDecorate %7 Block
|
||||
OpMemberDecorate %7 0 Offset 0
|
||||
)");
|
||||
EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %3 "A"
|
||||
|
|
Loading…
Reference in New Issue