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>
diff --git a/src/reader/spirv/function_memory_test.cc b/src/reader/spirv/function_memory_test.cc
index 984ee84..7291980 100644
--- a/src/reader/spirv/function_memory_test.cc
+++ b/src/reader/spirv/function_memory_test.cc
@@ -808,7 +808,7 @@
Variable{
myvar
storage_buffer
- __struct_S
+ __access_control_read_write__struct_S
})"));
}
diff --git a/src/reader/spirv/parser_impl.cc b/src/reader/spirv/parser_impl.cc
index 0f43943..d57ee6c 100644
--- a/src/reader/spirv/parser_impl.cc
+++ b/src/reader/spirv/parser_impl.cc
@@ -51,6 +51,7 @@
#include "src/ast/struct_member.h"
#include "src/ast/struct_member_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/array_type.h"
#include "src/ast/type/bool_type.h"
@@ -391,9 +392,10 @@
}
return std::make_unique<ast::StructMemberOffsetDecoration>(decoration[1]);
case SpvDecorationNonReadable:
+ // WGSL doesn't have a member decoration for this. Silently drop it.
+ return nullptr;
case SpvDecorationNonWritable:
- // TODO(dneto): Drop these for now.
- // https://github.com/gpuweb/gpuweb/issues/935
+ // WGSL doesn't have a member decoration for this.
return nullptr;
case SpvDecorationColMajor:
// WGSL only supports column major matrices.
@@ -813,6 +815,7 @@
// Compute members
ast::StructMemberList ast_members;
const auto members = struct_ty->element_types();
+ unsigned num_non_writable_members = 0;
for (uint32_t member_index = 0; member_index < members.size();
++member_index) {
const auto member_type_id = type_mgr_->GetId(members[member_index]);
@@ -822,6 +825,7 @@
return nullptr;
}
ast::StructMemberDecorationList ast_member_decorations;
+ bool is_non_writable = false;
for (auto& decoration : GetDecorationsForMember(type_id, member_index)) {
if (decoration.empty()) {
Fail() << "malformed SPIR-V decoration: it's empty";
@@ -844,6 +848,11 @@
}
Fail() << "unrecognized builtin " << decoration[1];
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 {
auto ast_member_decoration =
ConvertMemberDecoration(type_id, member_index, decoration);
@@ -855,6 +864,11 @@
}
}
}
+ 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);
auto ast_struct_member = std::make_unique<ast::StructMember>(
member_name, ast_member_ty, std::move(ast_member_decorations));
@@ -871,6 +885,9 @@
auto* result = ctx_.type_mgr().Get(std::move(ast_struct_type));
id_to_type_[type_id] = result;
+ if (num_non_writable_members == members.size()) {
+ read_only_struct_types_.insert(result);
+ }
ast_module_.AddConstructedType(result);
return result;
}
@@ -1058,6 +1075,16 @@
Fail() << "internal error: can't make ast::Variable for null type";
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);
ast::VariableDecorationList ast_decorations;
diff --git a/src/reader/spirv/parser_impl.h b/src/reader/spirv/parser_impl.h
index 555c77f..7becccd 100644
--- a/src/reader/spirv/parser_impl.h
+++ b/src/reader/spirv/parser_impl.h
@@ -485,6 +485,9 @@
// and Block decoration.
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
std::unordered_map<uint32_t, std::vector<EntryPointInfo>>
function_to_ep_info_;
diff --git a/src/reader/spirv/parser_impl_module_var_test.cc b/src/reader/spirv/parser_impl_module_var_test.cc
index 253ec6a..467d79a 100644
--- a/src/reader/spirv/parser_impl_module_var_test.cc
+++ b/src/reader/spirv/parser_impl_module_var_test.cc
@@ -1203,7 +1203,7 @@
}
myvar
storage_buffer
- __struct_S
+ __access_control_read_write__struct_S
})"))
<< module_str;
}
@@ -1257,7 +1257,7 @@
}
myvar
storage_buffer
- __struct_S
+ __access_control_read_write__struct_S
})"))
<< module_str;
}
@@ -1292,7 +1292,8 @@
"instruction, found '4'."));
}
-TEST_F(SpvParserTest, ModuleScopeVar_NonReadableDecoration_DroppedForNow) {
+TEST_F(SpvParserTest,
+ ModuleScopeVar_StructMember_NonReadableDecoration_Dropped) {
auto* p = parser(test::Assemble(R"(
OpName %myvar "myvar"
OpDecorate %strct Block
@@ -1301,7 +1302,7 @@
%ptr_sb_strct = OpTypePointer StorageBuffer %strct
%myvar = OpVariable %ptr_sb_strct StorageBuffer
)"));
- ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << p->error();
+ ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
EXPECT_TRUE(p->error().empty());
const auto module_str = p->module().to_str();
EXPECT_THAT(module_str, HasSubstr(R"(
@@ -1314,36 +1315,9 @@
Variable{
myvar
storage_buffer
- __struct_S
+ __access_control_read_write__struct_S
}
-})")) << 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;
+)")) << module_str;
}
TEST_F(SpvParserTest, ModuleScopeVar_ColMajorDecoration_Dropped) {
@@ -1370,7 +1344,7 @@
Variable{
myvar
storage_buffer
- __struct_S
+ __access_control_read_write__struct_S
}
})")) << module_str;
}
@@ -1399,7 +1373,7 @@
Variable{
myvar
storage_buffer
- __struct_S
+ __access_control_read_write__struct_S
}
})")) << module_str;
}
@@ -1424,6 +1398,97 @@
<< 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 spirv
} // namespace reader