From cebde298f9701669344182ae7cf863b51a59ed07 Mon Sep 17 00:00:00 2001 From: David Neto Date: Tue, 17 Nov 2020 17:59:38 +0000 Subject: [PATCH] spirv-reader: add GetMemoryObjectDeclarationForHandle Bug: tint:109 Change-Id: Ifb437ce9a39db7f92ca081e7ea551a576b0ecb2b Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/32740 Reviewed-by: David Neto Reviewed-by: dan sinclair Commit-Queue: David Neto Auto-Submit: David Neto --- BUILD.gn | 1 + src/CMakeLists.txt | 1 + src/reader/spirv/parser_impl.cc | 82 +++ src/reader/spirv/parser_impl.h | 28 + src/reader/spirv/parser_impl_handle_test.cc | 749 ++++++++++++++++++++ 5 files changed, 861 insertions(+) create mode 100644 src/reader/spirv/parser_impl_handle_test.cc diff --git a/BUILD.gn b/BUILD.gn index b53cc1f108..924e83218a 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -846,6 +846,7 @@ source_set("tint_unittests_spv_reader_src") { "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 cfd02d1437..717a6303dd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -450,6 +450,7 @@ if(${TINT_BUILD_SPV_READER}) 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 8caacf215d..bcc95a1e12 100644 --- a/src/reader/spirv/parser_impl.cc +++ b/src/reader/spirv/parser_impl.cc @@ -1503,6 +1503,88 @@ bool ParserImpl::EmitFunctions() { 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 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 f44659278e..5f0d350aca 100644 --- a/src/reader/spirv/parser_impl.h +++ b/src/reader/spirv/parser_impl.h @@ -375,6 +375,22 @@ class ParserImpl : Reader { 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 @@ class ParserImpl : Reader { // Maps function_id to a list of entrypoint information std::unordered_map> 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 + 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 + 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 0000000000..70d6afb982 --- /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 + +#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