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>
This commit is contained in:
David Neto 2020-11-17 17:59:38 +00:00 committed by Commit Bot service account
parent 0ae939bc0c
commit cebde298f9
5 changed files with 861 additions and 0 deletions

View File

@ -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",

View File

@ -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

View File

@ -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<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

View File

@ -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<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

View File

@ -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