spirv-reader: add GetMemoryObjectDeclarationForHandle
Bug: tint:109
Change-Id: Ifb437ce9a39db7f92ca081e7ea551a576b0ecb2b
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/32740
Reviewed-by: David Neto <dneto@google.com>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Commit-Queue: David Neto <dneto@google.com>
Auto-Submit: David Neto <dneto@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index b53cc1f..924e832 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -846,6 +846,7 @@
"src/reader/spirv/parser_impl_convert_type_test.cc",
"src/reader/spirv/parser_impl_function_decl_test.cc",
"src/reader/spirv/parser_impl_get_decorations_test.cc",
+ "src/reader/spirv/parser_impl_handle_test.cc",
"src/reader/spirv/parser_impl_import_test.cc",
"src/reader/spirv/parser_impl_module_var_test.cc",
"src/reader/spirv/parser_impl_named_types_test.cc",
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index cfd02d1..717a630 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -450,6 +450,7 @@
reader/spirv/parser_impl_convert_type_test.cc
reader/spirv/parser_impl_function_decl_test.cc
reader/spirv/parser_impl_get_decorations_test.cc
+ reader/spirv/parser_impl_handle_test.cc
reader/spirv/parser_impl_import_test.cc
reader/spirv/parser_impl_module_var_test.cc
reader/spirv/parser_impl_named_types_test.cc
diff --git a/src/reader/spirv/parser_impl.cc b/src/reader/spirv/parser_impl.cc
index 8caacf2..bcc95a1 100644
--- a/src/reader/spirv/parser_impl.cc
+++ b/src/reader/spirv/parser_impl.cc
@@ -1503,6 +1503,88 @@
return success_;
}
+const spvtools::opt::Instruction*
+ParserImpl::GetMemoryObjectDeclarationForHandle(uint32_t id,
+ bool follow_image) {
+ auto local_fail = [this, id,
+ follow_image]() -> const spvtools::opt::Instruction* {
+ const auto* inst = def_use_mgr_->GetDef(id);
+ Fail() << "Could not find memory object declaration for the "
+ << (follow_image ? "image" : "sampler") << " underlying id " << id
+ << (inst ? inst->PrettyPrint() : std::string());
+ return nullptr;
+ };
+
+ auto& memo_table =
+ (follow_image ? mem_obj_decl_image_ : mem_obj_decl_sampler_);
+
+ // Use a visited set to defend against bad input which might have long
+ // chains or even loops.
+ std::unordered_set<uint32_t> visited;
+
+ // Trace backward in the SSA data flow until we hit a memory object
+ // declaration.
+ while (true) {
+ auto where = memo_table.find(id);
+ if (where != memo_table.end()) {
+ return where->second;
+ }
+ // Protect against loops.
+ auto visited_iter = visited.find(id);
+ if (visited_iter != visited.end()) {
+ // We've hit a loop. Mark all the visited nodes
+ // as dead ends.
+ for (auto iter : visited) {
+ memo_table[iter] = nullptr;
+ }
+ return nullptr;
+ }
+ visited.insert(id);
+
+ const auto* inst = def_use_mgr_->GetDef(id);
+ if (inst == nullptr) {
+ return local_fail();
+ }
+ switch (inst->opcode()) {
+ case SpvOpFunctionParameter:
+ case SpvOpVariable:
+ // We found the memory object declaration.
+ // Remember it as the answer for the whole path.
+ for (auto iter : visited) {
+ memo_table[iter] = inst;
+ }
+ return inst;
+ case SpvOpLoad:
+ // Follow the pointer being loaded
+ id = inst->GetSingleWordInOperand(0);
+ break;
+ case SpvOpCopyObject:
+ // Follow the object being copied.
+ id = inst->GetSingleWordInOperand(0);
+ break;
+ case SpvOpAccessChain:
+ case SpvOpInBoundsAccessChain:
+ case SpvOpPtrAccessChain:
+ case SpvOpInBoundsPtrAccessChain:
+ // Follow the base pointer.
+ id = inst->GetSingleWordInOperand(0);
+ break;
+ case SpvOpSampledImage:
+ // Follow the image or the sampler, depending on the follow_image
+ // parameter.
+ id = inst->GetSingleWordInOperand(follow_image ? 0 : 1);
+ break;
+ case SpvOpImage:
+ // Follow the sampled image
+ id = inst->GetSingleWordInOperand(0);
+ break;
+ default:
+ // This is not valid.
+ return local_fail();
+ }
+ }
+}
+
} // namespace spirv
} // namespace reader
} // namespace tint
diff --git a/src/reader/spirv/parser_impl.h b/src/reader/spirv/parser_impl.h
index f446592..5f0d350a 100644
--- a/src/reader/spirv/parser_impl.h
+++ b/src/reader/spirv/parser_impl.h
@@ -375,6 +375,22 @@
return scalar_spec_constants_.find(id) != scalar_spec_constants_.end();
}
+ /// For a SPIR-V ID that defines a sampler, image, or sampled image value,
+ /// return the SPIR-V instruction that represents the memory object
+ /// declaration for the object. If we encounter an OpSampledImage along the
+ /// way, follow the image operand when follow_image is true; otherwise follow
+ /// the sampler operand. Returns null and emits an error if it can't trace
+ /// back to a memory object declaration.
+ /// This method can be used any time after BuildInternalModule has been
+ /// invoked.
+ /// @param id the SPIR-V ID of the sampler, image, or sampled image
+ /// @param follow_image indicates whether to follow the image operand of
+ /// OpSampledImage
+ /// @returns the memory object declaration for the handle, or nullptr on error
+ const spvtools::opt::Instruction* GetMemoryObjectDeclarationForHandle(
+ uint32_t id,
+ bool follow_image);
+
private:
/// Converts a specific SPIR-V type to a Tint type. Integer case
ast::type::Type* ConvertType(const spvtools::opt::analysis::Integer* int_ty);
@@ -497,6 +513,18 @@
// Maps function_id to a list of entrypoint information
std::unordered_map<uint32_t, std::vector<EntryPointInfo>>
function_to_ep_info_;
+
+ // Maps from a SPIR-V ID to its underlying memory object declaration,
+ // following image paths. This a memoization table for
+ // GetMemoryObjectDeclarationForHandle. (A SPIR-V memory object declaration is
+ // an OpVariable or an OpFunctinParameter with pointer type).
+ std::unordered_map<uint32_t, const spvtools::opt::Instruction*>
+ mem_obj_decl_image_;
+ // Maps from a SPIR-V ID to its underlying memory object declaration,
+ // following sampler paths. This a memoization table for
+ // GetMemoryObjectDeclarationForHandle.
+ std::unordered_map<uint32_t, const spvtools::opt::Instruction*>
+ mem_obj_decl_sampler_;
};
} // namespace spirv
diff --git a/src/reader/spirv/parser_impl_handle_test.cc b/src/reader/spirv/parser_impl_handle_test.cc
new file mode 100644
index 0000000..70d6afb
--- /dev/null
+++ b/src/reader/spirv/parser_impl_handle_test.cc
@@ -0,0 +1,749 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <string>
+
+#include "gmock/gmock.h"
+#include "src/reader/spirv/function.h"
+#include "src/reader/spirv/parser_impl.h"
+#include "src/reader/spirv/parser_impl_test_helper.h"
+#include "src/reader/spirv/spirv_tools_helpers_test.h"
+
+namespace tint {
+namespace reader {
+namespace spirv {
+namespace {
+
+using ::testing::Eq;
+using ::testing::HasSubstr;
+using ::testing::Not;
+
+std::string Preamble() {
+ return R"(
+ OpCapability Shader
+ OpCapability Sampled1D
+ OpCapability Image1D
+ OpCapability StorageImageExtendedFormats
+ OpMemoryModel Logical Simple
+ )";
+}
+
+std::string CommonTypes() {
+ return R"(
+ %void = OpTypeVoid
+ %voidfn = OpTypeFunction %void
+
+ %float = OpTypeFloat 32
+ %uint = OpTypeInt 32 0
+ %int = OpTypeInt 32 1
+
+ %uint_1 = OpConstant %uint 1
+ %uint_2 = OpConstant %uint 2
+ %uint_100 = OpConstant %uint 100
+
+ %v4uint = OpTypeVector %uint 4
+ %v4int = OpTypeVector %int 4
+ %v4float = OpTypeVector %float 4
+
+; Define types for all sampler and texture types that can map to WGSL,
+; modulo texel formats for storage textures. For now, we limit
+; ourselves to 2-channel 32-bit texel formats.
+
+; Because the SPIR-V reader also already generalizes so it can work with
+; combined image-samplers, we also test that too.
+
+ %sampler = OpTypeSampler
+
+ ; sampled images
+ %f_texture_1d = OpTypeImage %float 1D 0 0 0 1 Unknown
+ %f_texture_1d_array = OpTypeImage %float 1D 0 1 0 1 Unknown
+ %f_texture_2d = OpTypeImage %float 2D 0 0 0 1 Unknown
+ %f_texture_2d_ms = OpTypeImage %float 2D 0 0 1 1 Unknown
+ %f_texture_2d_array = OpTypeImage %float 2D 0 1 0 1 Unknown
+ %f_texture_2d_ms_array = OpTypeImage %float 2D 0 1 1 1 Unknown ; not in WebGPU
+ %f_texture_3d = OpTypeImage %float 3D 0 0 0 1 Unknown
+ %f_texture_cube = OpTypeImage %float Cube 0 0 0 1 Unknown
+ %f_texture_cube_array = OpTypeImage %float Cube 0 1 0 1 Unknown
+
+ ; storage images
+ %f_storage_1d = OpTypeImage %float 1D 0 0 0 1 Rg32f
+ %f_storage_1d_array = OpTypeImage %float 1D 0 1 0 1 Rg32f
+ %f_storage_2d = OpTypeImage %float 2D 0 0 0 1 Rg32f
+ %f_storage_2d_array = OpTypeImage %float 2D 0 1 0 1 Rg32f
+ %f_storage_3d = OpTypeImage %float 3D 0 0 0 1 Rg32f
+
+ ; Now all the same, but for unsigned integer sampled type.
+
+ %u_texture_1d = OpTypeImage %uint 1D 0 0 0 1 Unknown
+ %u_texture_1d_array = OpTypeImage %uint 1D 0 1 0 1 Unknown
+ %u_texture_2d = OpTypeImage %uint 2D 0 0 0 1 Unknown
+ %u_texture_2d_ms = OpTypeImage %uint 2D 0 0 1 1 Unknown
+ %u_texture_2d_array = OpTypeImage %uint 2D 0 1 0 1 Unknown
+ %u_texture_2d_ms_array = OpTypeImage %uint 2D 0 1 1 1 Unknown ; not in WebGPU
+ %u_texture_3d = OpTypeImage %uint 3D 0 0 0 1 Unknown
+ %u_texture_cube = OpTypeImage %uint Cube 0 0 0 1 Unknown
+ %u_texture_cube_array = OpTypeImage %uint Cube 0 1 0 1 Unknown
+
+ %u_storage_1d = OpTypeImage %uint 1D 0 0 0 1 Rg32ui
+ %u_storage_1d_array = OpTypeImage %uint 1D 0 1 0 1 Rg32ui
+ %u_storage_2d = OpTypeImage %uint 2D 0 0 0 1 Rg32ui
+ %u_storage_2d_array = OpTypeImage %uint 2D 0 1 0 1 Rg32ui
+ %u_storage_3d = OpTypeImage %uint 3D 0 0 0 1 Rg32ui
+
+ ; Now all the same, but for signed integer sampled type.
+
+ %i_texture_1d = OpTypeImage %int 1D 0 0 0 1 Unknown
+ %i_texture_1d_array = OpTypeImage %int 1D 0 1 0 1 Unknown
+ %i_texture_2d = OpTypeImage %int 2D 0 0 0 1 Unknown
+ %i_texture_2d_ms = OpTypeImage %int 2D 0 0 1 1 Unknown
+ %i_texture_2d_array = OpTypeImage %int 2D 0 1 0 1 Unknown
+ %i_texture_2d_ms_array = OpTypeImage %int 2D 0 1 1 1 Unknown ; not in WebGPU
+ %i_texture_3d = OpTypeImage %int 3D 0 0 0 1 Unknown
+ %i_texture_cube = OpTypeImage %int Cube 0 0 0 1 Unknown
+ %i_texture_cube_array = OpTypeImage %int Cube 0 1 0 1 Unknown
+
+ %i_storage_1d = OpTypeImage %int 1D 0 0 0 1 Rg32i
+ %i_storage_1d_array = OpTypeImage %int 1D 0 1 0 1 Rg32i
+ %i_storage_2d = OpTypeImage %int 2D 0 0 0 1 Rg32i
+ %i_storage_2d_array = OpTypeImage %int 2D 0 1 0 1 Rg32i
+ %i_storage_3d = OpTypeImage %int 3D 0 0 0 1 Rg32i
+
+ ;; Now pointers to each of the above, so we can declare variables for them.
+
+ %ptr_sampler = OpTypePointer UniformConstant %sampler
+
+ %ptr_f_texture_1d = OpTypePointer UniformConstant %f_texture_1d
+ %ptr_f_texture_1d_array = OpTypePointer UniformConstant %f_texture_1d_array
+ %ptr_f_texture_2d = OpTypePointer UniformConstant %f_texture_2d
+ %ptr_f_texture_2d_ms = OpTypePointer UniformConstant %f_texture_2d_ms
+ %ptr_f_texture_2d_array = OpTypePointer UniformConstant %f_texture_2d_array
+ %ptr_f_texture_2d_ms_array = OpTypePointer UniformConstant %f_texture_2d_ms_array
+ %ptr_f_texture_3d = OpTypePointer UniformConstant %f_texture_3d
+ %ptr_f_texture_cube = OpTypePointer UniformConstant %f_texture_cube
+ %ptr_f_texture_cube_array = OpTypePointer UniformConstant %f_texture_cube_array
+
+ ; storage images
+ %ptr_f_storage_1d = OpTypePointer UniformConstant %f_storage_1d
+ %ptr_f_storage_1d_array = OpTypePointer UniformConstant %f_storage_1d_array
+ %ptr_f_storage_2d = OpTypePointer UniformConstant %f_storage_2d
+ %ptr_f_storage_2d_array = OpTypePointer UniformConstant %f_storage_2d_array
+ %ptr_f_storage_3d = OpTypePointer UniformConstant %f_storage_3d
+
+ ; Now all the same, but for unsigned integer sampled type.
+
+ %ptr_u_texture_1d = OpTypePointer UniformConstant %u_texture_1d
+ %ptr_u_texture_1d_array = OpTypePointer UniformConstant %u_texture_1d_array
+ %ptr_u_texture_2d = OpTypePointer UniformConstant %u_texture_2d
+ %ptr_u_texture_2d_ms = OpTypePointer UniformConstant %u_texture_2d_ms
+ %ptr_u_texture_2d_array = OpTypePointer UniformConstant %u_texture_2d_array
+ %ptr_u_texture_2d_ms_array = OpTypePointer UniformConstant %u_texture_2d_ms_array
+ %ptr_u_texture_3d = OpTypePointer UniformConstant %u_texture_3d
+ %ptr_u_texture_cube = OpTypePointer UniformConstant %u_texture_cube
+ %ptr_u_texture_cube_array = OpTypePointer UniformConstant %u_texture_cube_array
+
+ %ptr_u_storage_1d = OpTypePointer UniformConstant %u_storage_1d
+ %ptr_u_storage_1d_array = OpTypePointer UniformConstant %u_storage_1d_array
+ %ptr_u_storage_2d = OpTypePointer UniformConstant %u_storage_2d
+ %ptr_u_storage_2d_array = OpTypePointer UniformConstant %u_storage_2d_array
+ %ptr_u_storage_3d = OpTypePointer UniformConstant %u_storage_3d
+
+ ; Now all the same, but for signed integer sampled type.
+
+ %ptr_i_texture_1d = OpTypePointer UniformConstant %i_texture_1d
+ %ptr_i_texture_1d_array = OpTypePointer UniformConstant %i_texture_1d_array
+ %ptr_i_texture_2d = OpTypePointer UniformConstant %i_texture_2d
+ %ptr_i_texture_2d_ms = OpTypePointer UniformConstant %i_texture_2d_ms
+ %ptr_i_texture_2d_array = OpTypePointer UniformConstant %i_texture_2d_array
+ %ptr_i_texture_2d_ms_array = OpTypePointer UniformConstant %i_texture_2d_ms_array
+ %ptr_i_texture_3d = OpTypePointer UniformConstant %i_texture_3d
+ %ptr_i_texture_cube = OpTypePointer UniformConstant %i_texture_cube
+ %ptr_i_texture_cube_array = OpTypePointer UniformConstant %i_texture_cube_array
+
+ %ptr_i_storage_1d = OpTypePointer UniformConstant %i_storage_1d
+ %ptr_i_storage_1d_array = OpTypePointer UniformConstant %i_storage_1d_array
+ %ptr_i_storage_2d = OpTypePointer UniformConstant %i_storage_2d
+ %ptr_i_storage_2d_array = OpTypePointer UniformConstant %i_storage_2d_array
+ %ptr_i_storage_3d = OpTypePointer UniformConstant %i_storage_3d
+
+ )";
+}
+
+TEST_F(SpvParserTest, GetMemoryObjectDeclarationForHandle_Variable_Direct) {
+ const auto assembly = Preamble() + CommonTypes() + R"(
+ %10 = OpVariable %ptr_sampler UniformConstant
+ %20 = OpVariable %ptr_f_texture_1d UniformConstant
+ )";
+ auto* p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildInternalModule());
+ EXPECT_TRUE(p->error().empty());
+ const auto* sampler = p->GetMemoryObjectDeclarationForHandle(10, false);
+ const auto* image = p->GetMemoryObjectDeclarationForHandle(20, true);
+
+ ASSERT_TRUE(sampler != nullptr);
+ EXPECT_EQ(sampler->result_id(), 10u);
+
+ ASSERT_TRUE(image != nullptr);
+ EXPECT_EQ(image->result_id(), 20u);
+}
+
+TEST_F(SpvParserTest,
+ GetMemoryObjectDeclarationForHandle_Variable_AccessChain) {
+ // Show that we would generalize to arrays of handles, even though that
+ // is not supported in WGSL MVP.
+ const auto assembly = Preamble() + CommonTypes() + R"(
+
+ %sampler_array = OpTypeArray %sampler %uint_100
+ %image_array = OpTypeArray %f_texture_1d %uint_100
+
+ %ptr_sampler_array = OpTypePointer UniformConstant %sampler_array
+ %ptr_image_array = OpTypePointer UniformConstant %image_array
+
+ %10 = OpVariable %ptr_sampler_array UniformConstant
+ %20 = OpVariable %ptr_image_array UniformConstant
+
+ %main = OpFunction %void None %voidfn
+ %entry = OpLabel
+
+ %110 = OpAccessChain %ptr_sampler %10 %uint_1
+ %120 = OpAccessChain %ptr_f_texture_1d %20 %uint_2
+
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto* p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildInternalModule());
+ EXPECT_TRUE(p->error().empty());
+ const auto* sampler = p->GetMemoryObjectDeclarationForHandle(110, false);
+ const auto* image = p->GetMemoryObjectDeclarationForHandle(120, true);
+
+ ASSERT_TRUE(sampler != nullptr);
+ EXPECT_EQ(sampler->result_id(), 10u);
+
+ ASSERT_TRUE(image != nullptr);
+ EXPECT_EQ(image->result_id(), 20u);
+}
+
+TEST_F(SpvParserTest,
+ GetMemoryObjectDeclarationForHandle_Variable_InBoundsAccessChain) {
+ const auto assembly = Preamble() + CommonTypes() + R"(
+
+ %sampler_array = OpTypeArray %sampler %uint_100
+ %image_array = OpTypeArray %f_texture_1d %uint_100
+
+ %ptr_sampler_array = OpTypePointer UniformConstant %sampler_array
+ %ptr_image_array = OpTypePointer UniformConstant %image_array
+
+ %10 = OpVariable %ptr_sampler_array UniformConstant
+ %20 = OpVariable %ptr_image_array UniformConstant
+
+ %main = OpFunction %void None %voidfn
+ %entry = OpLabel
+
+ %110 = OpInBoundsAccessChain %ptr_sampler %10 %uint_1
+ %120 = OpInBoundsAccessChain %ptr_f_texture_1d %20 %uint_2
+
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto* p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildInternalModule());
+ EXPECT_TRUE(p->error().empty());
+ const auto* sampler = p->GetMemoryObjectDeclarationForHandle(110, false);
+ const auto* image = p->GetMemoryObjectDeclarationForHandle(120, true);
+
+ ASSERT_TRUE(sampler != nullptr);
+ EXPECT_EQ(sampler->result_id(), 10u);
+
+ ASSERT_TRUE(image != nullptr);
+ EXPECT_EQ(image->result_id(), 20u);
+}
+
+TEST_F(SpvParserTest,
+ GetMemoryObjectDeclarationForHandle_Variable_PtrAccessChain) {
+ // Show that we would generalize to arrays of handles, even though that
+ // is not supported in WGSL MVP.
+ const auto assembly = Preamble() + CommonTypes() + R"(
+
+ %sampler_array = OpTypeArray %sampler %uint_100
+ %image_array = OpTypeArray %f_texture_1d %uint_100
+
+ %ptr_sampler_array = OpTypePointer UniformConstant %sampler_array
+ %ptr_image_array = OpTypePointer UniformConstant %image_array
+
+ %10 = OpVariable %ptr_sampler_array UniformConstant
+ %20 = OpVariable %ptr_image_array UniformConstant
+
+ %main = OpFunction %void None %voidfn
+ %entry = OpLabel
+
+ %110 = OpPtrAccessChain %ptr_sampler %10 %uint_1 %uint_1
+ %120 = OpPtrAccessChain %ptr_f_texture_1d %20 %uint_1 %uint_2
+
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto* p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildInternalModule());
+ EXPECT_TRUE(p->error().empty());
+ const auto* sampler = p->GetMemoryObjectDeclarationForHandle(110, false);
+ const auto* image = p->GetMemoryObjectDeclarationForHandle(120, true);
+
+ ASSERT_TRUE(sampler != nullptr);
+ EXPECT_EQ(sampler->result_id(), 10u);
+
+ ASSERT_TRUE(image != nullptr);
+ EXPECT_EQ(image->result_id(), 20u);
+}
+
+TEST_F(SpvParserTest,
+ GetMemoryObjectDeclarationForHandle_Variable_InBoundsPtrAccessChain) {
+ const auto assembly = Preamble() + CommonTypes() + R"(
+
+ %sampler_array = OpTypeArray %sampler %uint_100
+ %image_array = OpTypeArray %f_texture_1d %uint_100
+
+ %ptr_sampler_array = OpTypePointer UniformConstant %sampler_array
+ %ptr_image_array = OpTypePointer UniformConstant %image_array
+
+ %10 = OpVariable %ptr_sampler_array UniformConstant
+ %20 = OpVariable %ptr_image_array UniformConstant
+
+ %main = OpFunction %void None %voidfn
+ %entry = OpLabel
+
+ %110 = OpInBoundsPtrAccessChain %ptr_sampler %10 %uint_1 %uint_1
+ %120 = OpInBoundsPtrAccessChain %ptr_f_texture_1d %20 %uint_1 %uint_2
+
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto* p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildInternalModule());
+ EXPECT_TRUE(p->error().empty());
+ const auto* sampler = p->GetMemoryObjectDeclarationForHandle(110, false);
+ const auto* image = p->GetMemoryObjectDeclarationForHandle(120, true);
+
+ ASSERT_TRUE(sampler != nullptr);
+ EXPECT_EQ(sampler->result_id(), 10u);
+
+ ASSERT_TRUE(image != nullptr);
+ EXPECT_EQ(image->result_id(), 20u);
+}
+
+TEST_F(SpvParserTest, GetMemoryObjectDeclarationForHandle_Variable_CopyObject) {
+ const auto assembly = Preamble() + CommonTypes() + R"(
+
+ %10 = OpVariable %ptr_sampler UniformConstant
+ %20 = OpVariable %ptr_f_texture_1d UniformConstant
+
+ %main = OpFunction %void None %voidfn
+ %entry = OpLabel
+
+ %110 = OpCopyObject %ptr_sampler %10
+ %120 = OpCopyObject %ptr_f_texture_1d %20
+
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto* p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildInternalModule());
+ EXPECT_TRUE(p->error().empty());
+ const auto* sampler = p->GetMemoryObjectDeclarationForHandle(110, false);
+ const auto* image = p->GetMemoryObjectDeclarationForHandle(120, true);
+
+ ASSERT_TRUE(sampler != nullptr);
+ EXPECT_EQ(sampler->result_id(), 10u);
+
+ ASSERT_TRUE(image != nullptr);
+ EXPECT_EQ(image->result_id(), 20u);
+}
+
+TEST_F(SpvParserTest, GetMemoryObjectDeclarationForHandle_Variable_Load) {
+ const auto assembly = Preamble() + CommonTypes() + R"(
+
+ %10 = OpVariable %ptr_sampler UniformConstant
+ %20 = OpVariable %ptr_f_texture_1d UniformConstant
+
+ %main = OpFunction %void None %voidfn
+ %entry = OpLabel
+
+ %110 = OpLoad %sampler %10
+ %120 = OpLoad %f_texture_1d %20
+
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto* p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildInternalModule());
+ EXPECT_TRUE(p->error().empty());
+ const auto* sampler = p->GetMemoryObjectDeclarationForHandle(110, false);
+ const auto* image = p->GetMemoryObjectDeclarationForHandle(120, true);
+
+ ASSERT_TRUE(sampler != nullptr);
+ EXPECT_EQ(sampler->result_id(), 10u);
+
+ ASSERT_TRUE(image != nullptr);
+ EXPECT_EQ(image->result_id(), 20u);
+}
+
+TEST_F(SpvParserTest,
+ GetMemoryObjectDeclarationForHandle_Variable_SampledImage) {
+ // Trace through the sampled image instruction, but in two different
+ // directions.
+ const auto assembly = Preamble() + CommonTypes() + R"(
+ %sampled_image_type = OpTypeSampledImage %f_texture_1d
+
+ %10 = OpVariable %ptr_sampler UniformConstant
+ %20 = OpVariable %ptr_f_texture_1d UniformConstant
+
+ %main = OpFunction %void None %voidfn
+ %entry = OpLabel
+
+ %s = OpLoad %sampler %10
+ %im = OpLoad %f_texture_1d %20
+ %100 = OpSampledImage %sampled_image_type %im %s
+
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto* p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildInternalModule());
+ EXPECT_TRUE(p->error().empty());
+ const auto* sampler = p->GetMemoryObjectDeclarationForHandle(100, false);
+ const auto* image = p->GetMemoryObjectDeclarationForHandle(100, true);
+
+ ASSERT_TRUE(sampler != nullptr);
+ EXPECT_EQ(sampler->result_id(), 10u);
+
+ ASSERT_TRUE(image != nullptr);
+ EXPECT_EQ(image->result_id(), 20u);
+}
+
+TEST_F(SpvParserTest, GetMemoryObjectDeclarationForHandle_Variable_Image) {
+ const auto assembly = Preamble() + CommonTypes() + R"(
+ %sampled_image_type = OpTypeSampledImage %f_texture_1d
+
+ %10 = OpVariable %ptr_sampler UniformConstant
+ %20 = OpVariable %ptr_f_texture_1d UniformConstant
+
+ %main = OpFunction %void None %voidfn
+ %entry = OpLabel
+
+ %s = OpLoad %sampler %10
+ %im = OpLoad %f_texture_1d %20
+ %100 = OpSampledImage %sampled_image_type %im %s
+ %200 = OpImage %im %100
+
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto* p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildInternalModule());
+ EXPECT_TRUE(p->error().empty());
+
+ const auto* image = p->GetMemoryObjectDeclarationForHandle(200, true);
+ ASSERT_TRUE(image != nullptr);
+ EXPECT_EQ(image->result_id(), 20u);
+}
+
+TEST_F(SpvParserTest, GetMemoryObjectDeclarationForHandle_FuncParam_Direct) {
+ const auto assembly = Preamble() + CommonTypes() + R"(
+ %fty = OpTypeFunction %void %ptr_sampler %ptr_f_texture_1d
+
+ %func = OpFunction %void None %fty
+ %10 = OpFunctionParameter %ptr_sampler
+ %20 = OpFunctionParameter %ptr_f_texture_1d
+ %entry = OpLabel
+ OpReturn
+ )";
+ auto* p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildInternalModule());
+ EXPECT_TRUE(p->error().empty());
+ const auto* sampler = p->GetMemoryObjectDeclarationForHandle(10, false);
+ const auto* image = p->GetMemoryObjectDeclarationForHandle(20, true);
+
+ ASSERT_TRUE(sampler != nullptr);
+ EXPECT_EQ(sampler->result_id(), 10u);
+
+ ASSERT_TRUE(image != nullptr);
+ EXPECT_EQ(image->result_id(), 20u);
+}
+
+TEST_F(SpvParserTest,
+ GetMemoryObjectDeclarationForHandle_FuncParam_AccessChain) {
+ // Show that we would generalize to arrays of handles, even though that
+ // is not supported in WGSL MVP.
+ const auto assembly = Preamble() + CommonTypes() + R"(
+ %sampler_array = OpTypeArray %sampler %uint_100
+ %image_array = OpTypeArray %f_texture_1d %uint_100
+
+ %ptr_sampler_array = OpTypePointer UniformConstant %sampler_array
+ %ptr_image_array = OpTypePointer UniformConstant %image_array
+
+ %fty = OpTypeFunction %void %ptr_sampler_array %ptr_image_array
+
+ %func = OpFunction %void None %fty
+ %10 = OpFunctionParameter %ptr_sampler_array
+ %20 = OpFunctionParameter %ptr_image_array
+ %entry = OpLabel
+
+ %110 = OpAccessChain %ptr_sampler %10 %uint_1
+ %120 = OpAccessChain %ptr_f_texture_1d %20 %uint_2
+
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto* p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildInternalModule());
+ EXPECT_TRUE(p->error().empty());
+ const auto* sampler = p->GetMemoryObjectDeclarationForHandle(110, false);
+ const auto* image = p->GetMemoryObjectDeclarationForHandle(120, true);
+
+ ASSERT_TRUE(sampler != nullptr);
+ EXPECT_EQ(sampler->result_id(), 10u);
+
+ ASSERT_TRUE(image != nullptr);
+ EXPECT_EQ(image->result_id(), 20u);
+}
+
+TEST_F(SpvParserTest,
+ GetMemoryObjectDeclarationForHandle_FuncParam_InBoundsAccessChain) {
+ const auto assembly = Preamble() + CommonTypes() + R"(
+ %sampler_array = OpTypeArray %sampler %uint_100
+ %image_array = OpTypeArray %f_texture_1d %uint_100
+
+ %ptr_sampler_array = OpTypePointer UniformConstant %sampler_array
+ %ptr_image_array = OpTypePointer UniformConstant %image_array
+
+ %fty = OpTypeFunction %void %ptr_sampler_array %ptr_image_array
+
+ %func = OpFunction %void None %fty
+ %10 = OpFunctionParameter %ptr_sampler_array
+ %20 = OpFunctionParameter %ptr_image_array
+ %entry = OpLabel
+
+ %110 = OpInBoundsAccessChain %ptr_sampler %10 %uint_1
+ %120 = OpInBoundsAccessChain %ptr_f_texture_1d %20 %uint_2
+
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto* p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildInternalModule());
+ EXPECT_TRUE(p->error().empty());
+ const auto* sampler = p->GetMemoryObjectDeclarationForHandle(110, false);
+ const auto* image = p->GetMemoryObjectDeclarationForHandle(120, true);
+
+ ASSERT_TRUE(sampler != nullptr);
+ EXPECT_EQ(sampler->result_id(), 10u);
+
+ ASSERT_TRUE(image != nullptr);
+ EXPECT_EQ(image->result_id(), 20u);
+}
+
+TEST_F(SpvParserTest,
+ GetMemoryObjectDeclarationForHandle_FuncParam_PtrAccessChain) {
+ // Show that we would generalize to arrays of handles, even though that
+ // is not supported in WGSL MVP.
+ const auto assembly = Preamble() + CommonTypes() + R"(
+ %sampler_array = OpTypeArray %sampler %uint_100
+ %image_array = OpTypeArray %f_texture_1d %uint_100
+
+ %ptr_sampler_array = OpTypePointer UniformConstant %sampler_array
+ %ptr_image_array = OpTypePointer UniformConstant %image_array
+
+ %fty = OpTypeFunction %void %ptr_sampler_array %ptr_image_array
+
+ %func = OpFunction %void None %fty
+ %10 = OpFunctionParameter %ptr_sampler_array
+ %20 = OpFunctionParameter %ptr_image_array
+ %entry = OpLabel
+
+ %110 = OpPtrAccessChain %ptr_sampler %10 %uint_1 %uint_1
+ %120 = OpPtrAccessChain %ptr_f_texture_1d %20 %uint_1 %uint_2
+
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto* p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildInternalModule());
+ EXPECT_TRUE(p->error().empty());
+ const auto* sampler = p->GetMemoryObjectDeclarationForHandle(110, false);
+ const auto* image = p->GetMemoryObjectDeclarationForHandle(120, true);
+
+ ASSERT_TRUE(sampler != nullptr);
+ EXPECT_EQ(sampler->result_id(), 10u);
+
+ ASSERT_TRUE(image != nullptr);
+ EXPECT_EQ(image->result_id(), 20u);
+}
+
+TEST_F(SpvParserTest,
+ GetMemoryObjectDeclarationForHandle_FuncParam_InBoundsPtrAccessChain) {
+ const auto assembly = Preamble() + CommonTypes() + R"(
+ %sampler_array = OpTypeArray %sampler %uint_100
+ %image_array = OpTypeArray %f_texture_1d %uint_100
+
+ %ptr_sampler_array = OpTypePointer UniformConstant %sampler_array
+ %ptr_image_array = OpTypePointer UniformConstant %image_array
+
+ %fty = OpTypeFunction %void %ptr_sampler_array %ptr_image_array
+
+ %func = OpFunction %void None %fty
+ %10 = OpFunctionParameter %ptr_sampler_array
+ %20 = OpFunctionParameter %ptr_image_array
+ %entry = OpLabel
+
+ %110 = OpInBoundsPtrAccessChain %ptr_sampler %10 %uint_1 %uint_1
+ %120 = OpInBoundsPtrAccessChain %ptr_f_texture_1d %20 %uint_1 %uint_2
+
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto* p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildInternalModule());
+ EXPECT_TRUE(p->error().empty());
+ const auto* sampler = p->GetMemoryObjectDeclarationForHandle(110, false);
+ const auto* image = p->GetMemoryObjectDeclarationForHandle(120, true);
+
+ ASSERT_TRUE(sampler != nullptr);
+ EXPECT_EQ(sampler->result_id(), 10u);
+
+ ASSERT_TRUE(image != nullptr);
+ EXPECT_EQ(image->result_id(), 20u);
+}
+
+TEST_F(SpvParserTest,
+ GetMemoryObjectDeclarationForHandle_FuncParam_CopyObject) {
+ const auto assembly = Preamble() + CommonTypes() + R"(
+ %fty = OpTypeFunction %void %ptr_sampler %ptr_f_texture_1d
+
+ %func = OpFunction %void None %fty
+ %10 = OpFunctionParameter %ptr_sampler
+ %20 = OpFunctionParameter %ptr_f_texture_1d
+ %entry = OpLabel
+
+ %110 = OpCopyObject %ptr_sampler %10
+ %120 = OpCopyObject %ptr_f_texture_1d %20
+
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto* p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildInternalModule());
+ EXPECT_TRUE(p->error().empty());
+ const auto* sampler = p->GetMemoryObjectDeclarationForHandle(110, false);
+ const auto* image = p->GetMemoryObjectDeclarationForHandle(120, true);
+
+ ASSERT_TRUE(sampler != nullptr);
+ EXPECT_EQ(sampler->result_id(), 10u);
+
+ ASSERT_TRUE(image != nullptr);
+ EXPECT_EQ(image->result_id(), 20u);
+}
+
+TEST_F(SpvParserTest, GetMemoryObjectDeclarationForHandle_FuncParam_Load) {
+ const auto assembly = Preamble() + CommonTypes() + R"(
+ %fty = OpTypeFunction %void %ptr_sampler %ptr_f_texture_1d
+
+ %func = OpFunction %void None %fty
+ %10 = OpFunctionParameter %ptr_sampler
+ %20 = OpFunctionParameter %ptr_f_texture_1d
+ %entry = OpLabel
+
+ %110 = OpLoad %sampler %10
+ %120 = OpLoad %f_texture_1d %20
+
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto* p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildInternalModule());
+ EXPECT_TRUE(p->error().empty());
+ const auto* sampler = p->GetMemoryObjectDeclarationForHandle(110, false);
+ const auto* image = p->GetMemoryObjectDeclarationForHandle(120, true);
+
+ ASSERT_TRUE(sampler != nullptr);
+ EXPECT_EQ(sampler->result_id(), 10u);
+
+ ASSERT_TRUE(image != nullptr);
+ EXPECT_EQ(image->result_id(), 20u);
+}
+
+TEST_F(SpvParserTest,
+ GetMemoryObjectDeclarationForHandle_FuncParam_SampledImage) {
+ // Trace through the sampled image instruction, but in two different
+ // directions.
+ const auto assembly = Preamble() + CommonTypes() + R"(
+ %sampled_image_type = OpTypeSampledImage %f_texture_1d
+
+ %fty = OpTypeFunction %void %ptr_sampler %ptr_f_texture_1d
+
+ %func = OpFunction %void None %fty
+ %10 = OpFunctionParameter %ptr_sampler
+ %20 = OpFunctionParameter %ptr_f_texture_1d
+ %entry = OpLabel
+
+ %s = OpLoad %sampler %10
+ %im = OpLoad %f_texture_1d %20
+ %100 = OpSampledImage %sampled_image_type %im %s
+
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto* p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildInternalModule());
+ EXPECT_TRUE(p->error().empty());
+ const auto* sampler = p->GetMemoryObjectDeclarationForHandle(100, false);
+ const auto* image = p->GetMemoryObjectDeclarationForHandle(100, true);
+
+ ASSERT_TRUE(sampler != nullptr);
+ EXPECT_EQ(sampler->result_id(), 10u);
+
+ ASSERT_TRUE(image != nullptr);
+ EXPECT_EQ(image->result_id(), 20u);
+}
+
+TEST_F(SpvParserTest, GetMemoryObjectDeclarationForHandle_FuncParam_Image) {
+ const auto assembly = Preamble() + CommonTypes() + R"(
+ %sampled_image_type = OpTypeSampledImage %f_texture_1d
+
+ %fty = OpTypeFunction %void %ptr_sampler %ptr_f_texture_1d
+
+ %func = OpFunction %void None %fty
+ %10 = OpFunctionParameter %ptr_sampler
+ %20 = OpFunctionParameter %ptr_f_texture_1d
+ %entry = OpLabel
+
+ %s = OpLoad %sampler %10
+ %im = OpLoad %f_texture_1d %20
+ %100 = OpSampledImage %sampled_image_type %im %s
+ %200 = OpImage %im %100
+
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto* p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildInternalModule());
+ EXPECT_TRUE(p->error().empty());
+
+ const auto* image = p->GetMemoryObjectDeclarationForHandle(200, true);
+ ASSERT_TRUE(image != nullptr);
+ EXPECT_EQ(image->result_id(), 20u);
+}
+
+} // namespace
+} // namespace spirv
+} // namespace reader
+} // namespace tint