spirv-reader: apply access control to storage buffers
- Apply the AccessControlType wrappar around the struct type for any variable in the StorageBuffer storage class. - Drop the NonWritable member decorations for the struct type. Bug: tint:108 Change-Id: I6496c8c3e8b5d92b2ed0071385915d2b8065a80d Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/31020 Reviewed-by: dan sinclair <dsinclair@chromium.org> Commit-Queue: dan sinclair <dsinclair@chromium.org>
This commit is contained in:
parent
c55fc39acb
commit
6857ed0b0b
|
@ -808,7 +808,7 @@ TEST_F(SpvParserTest, RemapStorageBuffer_TypesAndVarDeclarations) {
|
||||||
Variable{
|
Variable{
|
||||||
myvar
|
myvar
|
||||||
storage_buffer
|
storage_buffer
|
||||||
__struct_S
|
__access_control_read_write__struct_S
|
||||||
})"));
|
})"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,6 +51,7 @@
|
||||||
#include "src/ast/struct_member.h"
|
#include "src/ast/struct_member.h"
|
||||||
#include "src/ast/struct_member_decoration.h"
|
#include "src/ast/struct_member_decoration.h"
|
||||||
#include "src/ast/struct_member_offset_decoration.h"
|
#include "src/ast/struct_member_offset_decoration.h"
|
||||||
|
#include "src/ast/type/access_control_type.h"
|
||||||
#include "src/ast/type/alias_type.h"
|
#include "src/ast/type/alias_type.h"
|
||||||
#include "src/ast/type/array_type.h"
|
#include "src/ast/type/array_type.h"
|
||||||
#include "src/ast/type/bool_type.h"
|
#include "src/ast/type/bool_type.h"
|
||||||
|
@ -391,9 +392,10 @@ ParserImpl::ConvertMemberDecoration(uint32_t struct_type_id,
|
||||||
}
|
}
|
||||||
return std::make_unique<ast::StructMemberOffsetDecoration>(decoration[1]);
|
return std::make_unique<ast::StructMemberOffsetDecoration>(decoration[1]);
|
||||||
case SpvDecorationNonReadable:
|
case SpvDecorationNonReadable:
|
||||||
|
// WGSL doesn't have a member decoration for this. Silently drop it.
|
||||||
|
return nullptr;
|
||||||
case SpvDecorationNonWritable:
|
case SpvDecorationNonWritable:
|
||||||
// TODO(dneto): Drop these for now.
|
// WGSL doesn't have a member decoration for this.
|
||||||
// https://github.com/gpuweb/gpuweb/issues/935
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
case SpvDecorationColMajor:
|
case SpvDecorationColMajor:
|
||||||
// WGSL only supports column major matrices.
|
// WGSL only supports column major matrices.
|
||||||
|
@ -813,6 +815,7 @@ ast::type::Type* ParserImpl::ConvertType(
|
||||||
// Compute members
|
// Compute members
|
||||||
ast::StructMemberList ast_members;
|
ast::StructMemberList ast_members;
|
||||||
const auto members = struct_ty->element_types();
|
const auto members = struct_ty->element_types();
|
||||||
|
unsigned num_non_writable_members = 0;
|
||||||
for (uint32_t member_index = 0; member_index < members.size();
|
for (uint32_t member_index = 0; member_index < members.size();
|
||||||
++member_index) {
|
++member_index) {
|
||||||
const auto member_type_id = type_mgr_->GetId(members[member_index]);
|
const auto member_type_id = type_mgr_->GetId(members[member_index]);
|
||||||
|
@ -822,6 +825,7 @@ ast::type::Type* ParserImpl::ConvertType(
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
ast::StructMemberDecorationList ast_member_decorations;
|
ast::StructMemberDecorationList ast_member_decorations;
|
||||||
|
bool is_non_writable = false;
|
||||||
for (auto& decoration : GetDecorationsForMember(type_id, member_index)) {
|
for (auto& decoration : GetDecorationsForMember(type_id, member_index)) {
|
||||||
if (decoration.empty()) {
|
if (decoration.empty()) {
|
||||||
Fail() << "malformed SPIR-V decoration: it's empty";
|
Fail() << "malformed SPIR-V decoration: it's empty";
|
||||||
|
@ -844,6 +848,11 @@ ast::type::Type* ParserImpl::ConvertType(
|
||||||
}
|
}
|
||||||
Fail() << "unrecognized builtin " << decoration[1];
|
Fail() << "unrecognized builtin " << decoration[1];
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
} else if (decoration[0] == SpvDecorationNonWritable) {
|
||||||
|
// WGSL doesn't represent individual members as non-writable. Instead,
|
||||||
|
// apply the ReadOnly access control to the containing struct if all
|
||||||
|
// the members are non-writable.
|
||||||
|
is_non_writable = true;
|
||||||
} else {
|
} else {
|
||||||
auto ast_member_decoration =
|
auto ast_member_decoration =
|
||||||
ConvertMemberDecoration(type_id, member_index, decoration);
|
ConvertMemberDecoration(type_id, member_index, decoration);
|
||||||
|
@ -855,6 +864,11 @@ ast::type::Type* ParserImpl::ConvertType(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (is_non_writable) {
|
||||||
|
// Count a member as non-writable only once, no matter how many
|
||||||
|
// NonWritable decorations are applied to it.
|
||||||
|
++num_non_writable_members;
|
||||||
|
}
|
||||||
const auto member_name = namer_.GetMemberName(type_id, member_index);
|
const auto member_name = namer_.GetMemberName(type_id, member_index);
|
||||||
auto ast_struct_member = std::make_unique<ast::StructMember>(
|
auto ast_struct_member = std::make_unique<ast::StructMember>(
|
||||||
member_name, ast_member_ty, std::move(ast_member_decorations));
|
member_name, ast_member_ty, std::move(ast_member_decorations));
|
||||||
|
@ -871,6 +885,9 @@ ast::type::Type* ParserImpl::ConvertType(
|
||||||
|
|
||||||
auto* result = ctx_.type_mgr().Get(std::move(ast_struct_type));
|
auto* result = ctx_.type_mgr().Get(std::move(ast_struct_type));
|
||||||
id_to_type_[type_id] = result;
|
id_to_type_[type_id] = result;
|
||||||
|
if (num_non_writable_members == members.size()) {
|
||||||
|
read_only_struct_types_.insert(result);
|
||||||
|
}
|
||||||
ast_module_.AddConstructedType(result);
|
ast_module_.AddConstructedType(result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -1058,6 +1075,16 @@ std::unique_ptr<ast::Variable> ParserImpl::MakeVariable(uint32_t id,
|
||||||
Fail() << "internal error: can't make ast::Variable for null type";
|
Fail() << "internal error: can't make ast::Variable for null type";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sc == ast::StorageClass::kStorageBuffer) {
|
||||||
|
// Apply the access(read) or access(read_write) modifier.
|
||||||
|
auto access = read_only_struct_types_.count(type)
|
||||||
|
? ast::AccessControl::kReadOnly
|
||||||
|
: ast::AccessControl::kReadWrite;
|
||||||
|
type = ctx_.type_mgr().Get(
|
||||||
|
std::make_unique<ast::type::AccessControlType>(access, type));
|
||||||
|
}
|
||||||
|
|
||||||
auto ast_var = std::make_unique<ast::Variable>(namer_.Name(id), sc, type);
|
auto ast_var = std::make_unique<ast::Variable>(namer_.Name(id), sc, type);
|
||||||
|
|
||||||
ast::VariableDecorationList ast_decorations;
|
ast::VariableDecorationList ast_decorations;
|
||||||
|
|
|
@ -485,6 +485,9 @@ class ParserImpl : Reader {
|
||||||
// and Block decoration.
|
// and Block decoration.
|
||||||
std::unordered_set<uint32_t> remap_buffer_block_type_;
|
std::unordered_set<uint32_t> remap_buffer_block_type_;
|
||||||
|
|
||||||
|
// The struct types with only read-only members.
|
||||||
|
std::unordered_set<ast::type::Type*> read_only_struct_types_;
|
||||||
|
|
||||||
// Maps function_id to a list of entrypoint information
|
// Maps function_id to a list of entrypoint information
|
||||||
std::unordered_map<uint32_t, std::vector<EntryPointInfo>>
|
std::unordered_map<uint32_t, std::vector<EntryPointInfo>>
|
||||||
function_to_ep_info_;
|
function_to_ep_info_;
|
||||||
|
|
|
@ -1203,7 +1203,7 @@ TEST_F(SpvParserTest, ModuleScopeVar_DescriptorSetDecoration_Valid) {
|
||||||
}
|
}
|
||||||
myvar
|
myvar
|
||||||
storage_buffer
|
storage_buffer
|
||||||
__struct_S
|
__access_control_read_write__struct_S
|
||||||
})"))
|
})"))
|
||||||
<< module_str;
|
<< module_str;
|
||||||
}
|
}
|
||||||
|
@ -1257,7 +1257,7 @@ TEST_F(SpvParserTest, ModuleScopeVar_BindingDecoration_Valid) {
|
||||||
}
|
}
|
||||||
myvar
|
myvar
|
||||||
storage_buffer
|
storage_buffer
|
||||||
__struct_S
|
__access_control_read_write__struct_S
|
||||||
})"))
|
})"))
|
||||||
<< module_str;
|
<< module_str;
|
||||||
}
|
}
|
||||||
|
@ -1292,7 +1292,8 @@ TEST_F(SpvParserTest,
|
||||||
"instruction, found '4'."));
|
"instruction, found '4'."));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SpvParserTest, ModuleScopeVar_NonReadableDecoration_DroppedForNow) {
|
TEST_F(SpvParserTest,
|
||||||
|
ModuleScopeVar_StructMember_NonReadableDecoration_Dropped) {
|
||||||
auto* p = parser(test::Assemble(R"(
|
auto* p = parser(test::Assemble(R"(
|
||||||
OpName %myvar "myvar"
|
OpName %myvar "myvar"
|
||||||
OpDecorate %strct Block
|
OpDecorate %strct Block
|
||||||
|
@ -1301,7 +1302,7 @@ TEST_F(SpvParserTest, ModuleScopeVar_NonReadableDecoration_DroppedForNow) {
|
||||||
%ptr_sb_strct = OpTypePointer StorageBuffer %strct
|
%ptr_sb_strct = OpTypePointer StorageBuffer %strct
|
||||||
%myvar = OpVariable %ptr_sb_strct StorageBuffer
|
%myvar = OpVariable %ptr_sb_strct StorageBuffer
|
||||||
)"));
|
)"));
|
||||||
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
|
||||||
EXPECT_TRUE(p->error().empty());
|
EXPECT_TRUE(p->error().empty());
|
||||||
const auto module_str = p->module().to_str();
|
const auto module_str = p->module().to_str();
|
||||||
EXPECT_THAT(module_str, HasSubstr(R"(
|
EXPECT_THAT(module_str, HasSubstr(R"(
|
||||||
|
@ -1314,36 +1315,9 @@ TEST_F(SpvParserTest, ModuleScopeVar_NonReadableDecoration_DroppedForNow) {
|
||||||
Variable{
|
Variable{
|
||||||
myvar
|
myvar
|
||||||
storage_buffer
|
storage_buffer
|
||||||
__struct_S
|
__access_control_read_write__struct_S
|
||||||
}
|
}
|
||||||
})")) << module_str;
|
)")) << module_str;
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(SpvParserTest, ModuleScopeVar_NonWritableDecoration_DroppedForNow) {
|
|
||||||
auto* p = parser(test::Assemble(R"(
|
|
||||||
OpName %myvar "myvar"
|
|
||||||
OpDecorate %strct Block
|
|
||||||
OpMemberDecorate %strct 0 NonWritable
|
|
||||||
)" + CommonTypes() + R"(
|
|
||||||
%ptr_sb_strct = OpTypePointer StorageBuffer %strct
|
|
||||||
%myvar = OpVariable %ptr_sb_strct StorageBuffer
|
|
||||||
)"));
|
|
||||||
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
|
||||||
EXPECT_TRUE(p->error().empty());
|
|
||||||
const auto module_str = p->module().to_str();
|
|
||||||
EXPECT_THAT(module_str, HasSubstr(R"(
|
|
||||||
S Struct{
|
|
||||||
[[block]]
|
|
||||||
StructMember{field0: __u32}
|
|
||||||
StructMember{field1: __f32}
|
|
||||||
StructMember{field2: __array__u32_2}
|
|
||||||
}
|
|
||||||
Variable{
|
|
||||||
myvar
|
|
||||||
storage_buffer
|
|
||||||
__struct_S
|
|
||||||
}
|
|
||||||
})")) << module_str;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SpvParserTest, ModuleScopeVar_ColMajorDecoration_Dropped) {
|
TEST_F(SpvParserTest, ModuleScopeVar_ColMajorDecoration_Dropped) {
|
||||||
|
@ -1370,7 +1344,7 @@ TEST_F(SpvParserTest, ModuleScopeVar_ColMajorDecoration_Dropped) {
|
||||||
Variable{
|
Variable{
|
||||||
myvar
|
myvar
|
||||||
storage_buffer
|
storage_buffer
|
||||||
__struct_S
|
__access_control_read_write__struct_S
|
||||||
}
|
}
|
||||||
})")) << module_str;
|
})")) << module_str;
|
||||||
}
|
}
|
||||||
|
@ -1399,7 +1373,7 @@ TEST_F(SpvParserTest, ModuleScopeVar_MatrixStrideDecoration_Dropped) {
|
||||||
Variable{
|
Variable{
|
||||||
myvar
|
myvar
|
||||||
storage_buffer
|
storage_buffer
|
||||||
__struct_S
|
__access_control_read_write__struct_S
|
||||||
}
|
}
|
||||||
})")) << module_str;
|
})")) << module_str;
|
||||||
}
|
}
|
||||||
|
@ -1424,6 +1398,97 @@ TEST_F(SpvParserTest, ModuleScopeVar_RowMajorDecoration_IsError) {
|
||||||
<< p->error();
|
<< p->error();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvParserTest, ModuleScopeVar_StorageBuffer_NonWritable_AllMembers) {
|
||||||
|
// Variable should have access(read)
|
||||||
|
auto* p = parser(test::Assemble(R"(
|
||||||
|
OpName %myvar "myvar"
|
||||||
|
OpDecorate %s Block
|
||||||
|
OpMemberDecorate %s 0 NonWritable
|
||||||
|
OpMemberDecorate %s 1 NonWritable
|
||||||
|
%float = OpTypeFloat 32
|
||||||
|
|
||||||
|
%s = OpTypeStruct %float %float
|
||||||
|
%ptr_sb_s = OpTypePointer StorageBuffer %s
|
||||||
|
%myvar = OpVariable %ptr_sb_s StorageBuffer
|
||||||
|
)"));
|
||||||
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
||||||
|
EXPECT_TRUE(p->error().empty());
|
||||||
|
const auto module_str = p->module().to_str();
|
||||||
|
EXPECT_THAT(module_str, HasSubstr(R"(
|
||||||
|
S Struct{
|
||||||
|
[[block]]
|
||||||
|
StructMember{field0: __f32}
|
||||||
|
StructMember{field1: __f32}
|
||||||
|
}
|
||||||
|
Variable{
|
||||||
|
myvar
|
||||||
|
storage_buffer
|
||||||
|
__access_control_read_only__struct_S
|
||||||
|
}
|
||||||
|
})")) << module_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SpvParserTest, ModuleScopeVar_StorageBuffer_NonWritable_NotAllMembers) {
|
||||||
|
// Variable should have access(read_write)
|
||||||
|
auto* p = parser(test::Assemble(R"(
|
||||||
|
OpName %myvar "myvar"
|
||||||
|
OpDecorate %s Block
|
||||||
|
OpMemberDecorate %s 0 NonWritable
|
||||||
|
%float = OpTypeFloat 32
|
||||||
|
|
||||||
|
%s = OpTypeStruct %float %float
|
||||||
|
%ptr_sb_s = OpTypePointer StorageBuffer %s
|
||||||
|
%myvar = OpVariable %ptr_sb_s StorageBuffer
|
||||||
|
)"));
|
||||||
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
||||||
|
EXPECT_TRUE(p->error().empty());
|
||||||
|
const auto module_str = p->module().to_str();
|
||||||
|
EXPECT_THAT(module_str, HasSubstr(R"(
|
||||||
|
S Struct{
|
||||||
|
[[block]]
|
||||||
|
StructMember{field0: __f32}
|
||||||
|
StructMember{field1: __f32}
|
||||||
|
}
|
||||||
|
Variable{
|
||||||
|
myvar
|
||||||
|
storage_buffer
|
||||||
|
__access_control_read_write__struct_S
|
||||||
|
}
|
||||||
|
})")) << module_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(
|
||||||
|
SpvParserTest,
|
||||||
|
ModuleScopeVar_StorageBuffer_NonWritable_NotAllMembers_DuplicatedOnSameMember) {
|
||||||
|
// Variable should have access(read_write)
|
||||||
|
auto* p = parser(test::Assemble(R"(
|
||||||
|
OpName %myvar "myvar"
|
||||||
|
OpDecorate %s Block
|
||||||
|
OpMemberDecorate %s 0 NonWritable
|
||||||
|
OpMemberDecorate %s 0 NonWritable ; same member. Don't double-count it
|
||||||
|
%float = OpTypeFloat 32
|
||||||
|
|
||||||
|
%s = OpTypeStruct %float %float
|
||||||
|
%ptr_sb_s = OpTypePointer StorageBuffer %s
|
||||||
|
%myvar = OpVariable %ptr_sb_s StorageBuffer
|
||||||
|
)"));
|
||||||
|
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
|
||||||
|
EXPECT_TRUE(p->error().empty());
|
||||||
|
const auto module_str = p->module().to_str();
|
||||||
|
EXPECT_THAT(module_str, HasSubstr(R"(
|
||||||
|
S Struct{
|
||||||
|
[[block]]
|
||||||
|
StructMember{field0: __f32}
|
||||||
|
StructMember{field1: __f32}
|
||||||
|
}
|
||||||
|
Variable{
|
||||||
|
myvar
|
||||||
|
storage_buffer
|
||||||
|
__access_control_read_write__struct_S
|
||||||
|
}
|
||||||
|
})")) << module_str;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace spirv
|
} // namespace spirv
|
||||||
} // namespace reader
|
} // namespace reader
|
||||||
|
|
Loading…
Reference in New Issue