| // Copyright (c) 2025 Google LLC |
| // |
| // 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 <array> |
| #include <iostream> |
| #include <ostream> |
| |
| #include "spirv-tools/optimizer.hpp" |
| #include "test/opt/pass_fixture.h" |
| #include "test/opt/pass_utils.h" |
| |
| namespace spvtools { |
| namespace opt { |
| namespace { |
| |
| struct SplitCombinedImageSamplerPassTest : public PassTest<::testing::Test> { |
| virtual void SetUp() override { |
| SetTargetEnv(SPV_ENV_VULKAN_1_0); |
| SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); |
| SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES | |
| SPV_BINARY_TO_TEXT_OPTION_INDENT | |
| SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); |
| } |
| }; |
| |
| struct TypeCase { |
| const char* glsl_type; |
| const char* image_type_decl; |
| }; |
| std::ostream& operator<<(std::ostream& os, const TypeCase& tc) { |
| os << tc.glsl_type; |
| return os; |
| } |
| |
| struct SplitCombinedImageSamplerPassTypeCaseTest |
| : public PassTest<::testing::TestWithParam<TypeCase>> { |
| virtual void SetUp() override { |
| SetTargetEnv(SPV_ENV_VULKAN_1_0); |
| SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); |
| SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES | |
| SPV_BINARY_TO_TEXT_OPTION_INDENT | |
| SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); |
| } |
| }; |
| |
| std::vector<TypeCase> ImageTypeCases() { |
| return std::vector<TypeCase>{ |
| {"sampler2D", "OpTypeImage %float 2D 0 0 0 1 Unknown"}, |
| {"sampler2DShadow", "OpTypeImage %float 2D 1 0 0 1 Unknown"}, |
| {"sampler2DArray", "OpTypeImage %float 2D 0 1 0 1 Unknown"}, |
| {"sampler2DArrayShadow", "OpTypeImage %float 2D 1 1 0 1 Unknown"}, |
| {"sampler2DMS", "OpTypeImage %float 2D 0 0 1 1 Unknown"}, |
| {"sampler2DMSArray", "OpTypeImage %float 2D 0 1 1 1 Unknown"}, |
| {"sampler3D", "OpTypeImage %float 3D 0 0 0 1 Unknown"}, |
| {"samplerCube", "OpTypeImage %float Cube 0 0 0 1 Unknown"}, |
| {"samplerCubeShadow", "OpTypeImage %float Cube 1 0 0 1 Unknown"}, |
| {"samplerCubeArray", "OpTypeImage %float Cube 0 1 0 1 Unknown"}, |
| {"samplerCubeArrayShadow", "OpTypeImage %float Cube 1 1 0 1 Unknown"}, |
| {"isampler2D", "OpTypeImage %int 2D 0 0 0 1 Unknown"}, |
| {"isampler2DShadow", "OpTypeImage %int 2D 1 0 0 1 Unknown"}, |
| {"isampler2DArray", "OpTypeImage %int 2D 0 1 0 1 Unknown"}, |
| {"isampler2DArrayShadow", "OpTypeImage %int 2D 1 1 0 1 Unknown"}, |
| {"isampler2DMS", "OpTypeImage %int 2D 0 0 1 1 Unknown"}, |
| {"isampler2DMSArray", "OpTypeImage %int 2D 0 1 1 1 Unknown"}, |
| {"isampler3D", "OpTypeImage %int 3D 0 0 0 1 Unknown"}, |
| {"isamplerCube", "OpTypeImage %int Cube 0 0 0 1 Unknown"}, |
| {"isamplerCubeShadow", "OpTypeImage %int Cube 1 0 0 1 Unknown"}, |
| {"isamplerCubeArray", "OpTypeImage %int Cube 0 1 0 1 Unknown"}, |
| {"isamplerCubeArrayShadow", "OpTypeImage %int Cube 1 1 0 1 Unknown"}, |
| {"usampler2D", "OpTypeImage %uint 2D 0 0 0 1 Unknown"}, |
| {"usampler2DShadow", "OpTypeImage %uint 2D 1 0 0 1 Unknown"}, |
| {"usampler2DArray", "OpTypeImage %uint 2D 0 1 0 1 Unknown"}, |
| {"usampler2DArrayShadow", "OpTypeImage %uint 2D 1 1 0 1 Unknown"}, |
| {"usampler2DMS", "OpTypeImage %uint 2D 0 0 1 1 Unknown"}, |
| {"usampler2DMSArray", "OpTypeImage %uint 2D 0 1 1 1 Unknown"}, |
| {"usampler3D", "OpTypeImage %uint 3D 0 0 0 1 Unknown"}, |
| {"usamplerCube", "OpTypeImage %uint Cube 0 0 0 1 Unknown"}, |
| {"usamplerCubeShadow", "OpTypeImage %uint Cube 1 0 0 1 Unknown"}, |
| {"usamplerCubeArray", "OpTypeImage %uint Cube 0 1 0 1 Unknown"}, |
| {"usamplerCubeArrayShadow", "OpTypeImage %uint Cube 1 1 0 1 Unknown"}, |
| }; |
| } |
| |
| std::string Preamble(const std::string shader_interface = "") { |
| return R"( OpCapability Shader |
| OpCapability RuntimeDescriptorArray |
| OpExtension "SPV_EXT_descriptor_indexing" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint GLCompute %main "main")" + |
| shader_interface + R"( |
| OpExecutionMode %main LocalSize 1 1 1 |
| OpName %main "main" |
| OpName %main_0 "main_0" |
| OpName %voidfn "voidfn" |
| )"; |
| } |
| |
| std::string PreambleFragment(const std::string shader_interface = "") { |
| return R"( OpCapability Shader |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %main "main")" + |
| shader_interface + R"( |
| OpExecutionMode %main OriginUpperLeft |
| OpName %main "main" |
| OpName %main_0 "main_0" |
| OpName %voidfn "voidfn" |
| )"; |
| } |
| |
| std::string BasicTypes() { |
| return R"( %float = OpTypeFloat 32 |
| %uint = OpTypeInt 32 0 |
| %int = OpTypeInt 32 1 |
| %uint_0 = OpConstant %uint 0 |
| %uint_1 = OpConstant %uint 1 |
| %uint_3 = OpConstant %uint 3 |
| %float_0 = OpConstant %float 0 |
| %v2float = OpTypeVector %float 2 |
| %v3float = OpTypeVector %float 3 |
| %v4float = OpTypeVector %float 4 |
| %13 = OpConstantNull %v2float |
| %14 = OpConstantNull %v3float |
| %15 = OpConstantNull %v4float |
| %void = OpTypeVoid |
| %voidfn = OpTypeFunction %void |
| )"; |
| } |
| std::string Main() { |
| return R"( |
| %main = OpFunction %void None %voidfn |
| %main_0 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| } |
| std::string NoCheck() { return "; CHECK-NOT: nothing to see"; } |
| |
| TEST_F(SplitCombinedImageSamplerPassTest, SamplerOnly_NoChange) { |
| const std::string kTest = Preamble() + |
| R"( OpDecorate %100 DescriptorSet 0 |
| OpDecorate %100 Binding 0 |
| )" + BasicTypes() + R"( %10 = OpTypeSampler |
| %_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10 |
| %100 = OpVariable %_ptr_UniformConstant_10 UniformConstant |
| %main = OpFunction %void None %voidfn |
| %main_0 = OpLabel |
| %6 = OpLoad %10 %100 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( |
| kTest + NoCheck(), /* do_validation= */ true); |
| EXPECT_EQ(status, Pass::Status::SuccessWithoutChange) |
| << "status" << kTest << "\n -> \n" |
| << disasm; |
| EXPECT_EQ(disasm, kTest) << "disasm"; |
| } |
| |
| TEST_F(SplitCombinedImageSamplerPassTest, ImageOnly_NoChange) { |
| const std::string kTest = Preamble() + |
| R"( OpDecorate %100 DescriptorSet 0 |
| OpDecorate %100 Binding 0 |
| )" + BasicTypes() + R"( %10 = OpTypeImage %float 2D 0 0 0 1 Unknown |
| %_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10 |
| %100 = OpVariable %_ptr_UniformConstant_10 UniformConstant |
| %main = OpFunction %void None %voidfn |
| %main_0 = OpLabel |
| %6 = OpLoad %10 %100 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| SCOPED_TRACE("image only"); |
| auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( |
| kTest + NoCheck(), /* do_validation= */ true); |
| EXPECT_EQ(status, Pass::Status::SuccessWithoutChange); |
| EXPECT_EQ(disasm, kTest); |
| } |
| |
| TEST_F(SplitCombinedImageSamplerPassTest, PtrSampledImageOnly_DeletesPtrType) { |
| const std::string kTest = Preamble() + BasicTypes() + R"( |
| ; CHECK: OpCapability Shader |
| ; CHECK-NOT: OpTypePointer UniformConstant |
| ; CHECK: OpFunction %void |
| %100 = OpTypeImage %float 2D 0 0 0 1 Unknown |
| %101 = OpTypeSampledImage %100 |
| %102 = OpTypePointer UniformConstant %101 |
| %main = OpFunction %void None %voidfn |
| %main_0 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( |
| kTest + NoCheck(), /* do_validation= */ true); |
| EXPECT_EQ(status, Pass::Status::SuccessWithChange) << "status"; |
| } |
| |
| TEST_F(SplitCombinedImageSamplerPassTest, |
| PtrArraySampledImageOnly_DeletesPtrType) { |
| const std::string kTest = Preamble() + BasicTypes() + R"( |
| ; CHECK: OpCapability Shader |
| ; CHECK-NOT: OpTypePointer UniformConstant |
| ; CHECK: OpFunction %void |
| %100 = OpTypeImage %float 2D 0 0 0 1 Unknown |
| %101 = OpTypeSampledImage %100 |
| %103 = OpTypeArray %101 %uint_1 |
| %104 = OpTypePointer UniformConstant %103 |
| %main = OpFunction %void None %voidfn |
| %main_0 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( |
| kTest + NoCheck(), /* do_validation= */ true); |
| EXPECT_EQ(status, Pass::Status::SuccessWithChange) << "status"; |
| } |
| |
| TEST_F(SplitCombinedImageSamplerPassTest, |
| PtrRtArraySampledImageOnly_DeletesPtrType) { |
| const std::string kTest = Preamble() + BasicTypes() + R"( |
| ; CHECK: OpCapability Shader |
| ; CHECK-NOT: OpTypePointer UniformConstant |
| ; CHECK: OpFunction %void |
| %100 = OpTypeImage %float 2D 0 0 0 1 Unknown |
| %101 = OpTypeSampledImage %100 |
| %103 = OpTypeRuntimeArray %101 |
| %104 = OpTypePointer UniformConstant %103 |
| %main = OpFunction %void None %voidfn |
| %main_0 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( |
| kTest + NoCheck(), /* do_validation= */ true); |
| EXPECT_EQ(status, Pass::Status::SuccessWithChange) << "status"; |
| } |
| |
| TEST_F(SplitCombinedImageSamplerPassTest, |
| Combined_NoSampler_CreatedBeforeSampledImage) { |
| // No OpTypeSampler to begin with. |
| const std::string kTest = Preamble() + |
| R"( OpDecorate %100 DescriptorSet 0 |
| OpDecorate %100 Binding 0 |
| |
| ; A sampler type is created and placed at the start of types. |
| ; CHECK: OpDecorate %{{\d+}} Binding 0 |
| ; CHECK: OpDecorate %{{\d+}} Binding 0 |
| ; CHECK-NOT: TypeSampledImage |
| ; CHECK: TypeSampler |
| ; CHECK: TypeSampledImage |
| |
| )" + BasicTypes() + R"( %10 = OpTypeImage %float 2D 0 0 0 1 Unknown |
| %11 = OpTypeSampledImage %10 |
| %_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11 |
| |
| %100 = OpVariable %_ptr_UniformConstant_11 UniformConstant |
| %main = OpFunction %void None %voidfn |
| %main_0 = OpLabel |
| %6 = OpLoad %11 %100 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( |
| kTest, /* do_validation= */ true); |
| EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; |
| } |
| |
| TEST_F(SplitCombinedImageSamplerPassTest, Combined_SynthesizeVarNames) { |
| // Also tests binding info is copied to both variables. |
| const std::string kTest = Preamble() + |
| R"( |
| OpName %orig_var "orig_var" |
| OpDecorate %orig_var DescriptorSet 0 |
| OpDecorate %orig_var Binding 0 |
| |
| ; The combined image variable is replaced by an image variable and a sampler variable. |
| |
| ; CHECK: OpCapability |
| ; The original name is deleted |
| ; CHECK-NOT: OpName %orig_var " |
| ; CHECK: OpName %orig_var_image "orig_var_image" |
| ; CHECK: OpName %orig_var_sampler "orig_var_sampler" |
| ; CHECK-NOT: OpName %orig_var " |
| |
| ; CHECK: OpDecorate %orig_var_image DescriptorSet 0 |
| ; CHECK: OpDecorate %orig_var_sampler DescriptorSet 0 |
| ; CHECK: OpDecorate %orig_var_image Binding 0 |
| ; CHECK: OpDecorate %orig_var_sampler Binding 0 |
| |
| ; CHECK: %10 = OpTypeImage % |
| ; CHECK: %[[image_ptr_ty:\w+]] = OpTypePointer UniformConstant %10 |
| ; CHECK: %[[sampler_ty:\d+]] = OpTypeSampler |
| ; CHECK: %[[sampler_ptr_ty:\w+]] = OpTypePointer UniformConstant %[[sampler_ty]] |
| |
| |
| ; CHECK-NOT: %orig_var = OpVariable |
| ; CHECK-DAG: %orig_var_sampler = OpVariable %[[sampler_ptr_ty]] UniformConstant |
| ; CHECK-DAG: %orig_var_image = OpVariable %[[image_ptr_ty]] UniformConstant |
| ; CHECK: = OpFunction |
| |
| )" + BasicTypes() + R"( |
| %10 = OpTypeImage %float 2D 0 0 0 1 Unknown |
| %11 = OpTypeSampledImage %10 |
| %_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11 |
| |
| %orig_var = OpVariable %_ptr_UniformConstant_11 UniformConstant |
| %main = OpFunction %void None %voidfn |
| %main_0 = OpLabel |
| %101 = OpLoad %11 %orig_var |
| OpReturn |
| OpFunctionEnd |
| )"; |
| auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( |
| kTest, /* do_validation= */ true); |
| EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; |
| } |
| |
| TEST_P(SplitCombinedImageSamplerPassTypeCaseTest, Combined_RemapLoad) { |
| // Also tests binding info is copied to both variables. |
| const std::string kTest = Preamble() + |
| R"( |
| OpName %combined "combined" |
| OpDecorate %100 DescriptorSet 0 |
| OpDecorate %100 Binding 0 |
| |
| ; CHECK: OpName |
| ; CHECK-NOT: OpDecorate %100 |
| ; CHECK: OpDecorate %[[image_var:\d+]] DescriptorSet 0 |
| ; CHECK: OpDecorate %[[sampler_var:\d+]] DescriptorSet 0 |
| ; CHECK: OpDecorate %[[image_var]] Binding 0 |
| ; CHECK: OpDecorate %[[sampler_var]] Binding 0 |
| |
| ; CHECK: %10 = OpTypeImage % |
| ; CHECK: %[[image_ptr_ty:\w+]] = OpTypePointer UniformConstant %10 |
| ; CHECK: %[[sampler_ty:\d+]] = OpTypeSampler |
| ; CHECK: %[[sampler_ptr_ty:\w+]] = OpTypePointer UniformConstant %[[sampler_ty]] |
| |
| ; The combined image variable is replaced by an image variable and a sampler variable. |
| |
| ; CHECK-NOT: %100 = OpVariable |
| ; CHECK-DAG: %[[sampler_var]] = OpVariable %[[sampler_ptr_ty]] UniformConstant |
| ; CHECK-DAG: %[[image_var]] = OpVariable %[[image_ptr_ty]] UniformConstant |
| ; CHECK: = OpFunction |
| |
| ; The load of the combined image+sampler is replaced by a two loads, then |
| ; a combination operation. |
| ; CHECK: %[[im:\d+]] = OpLoad %10 %[[image_var]] |
| ; CHECK: %[[s:\d+]] = OpLoad %[[sampler_ty]] %[[sampler_var]] |
| ; CHECK: %combined = OpSampledImage %11 %[[im]] %[[s]] |
| |
| )" + BasicTypes() + |
| " %10 = " + GetParam().image_type_decl + R"( |
| %11 = OpTypeSampledImage %10 |
| %_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11 |
| |
| %100 = OpVariable %_ptr_UniformConstant_11 UniformConstant |
| %main = OpFunction %void None %voidfn |
| %main_0 = OpLabel |
| %combined = OpLoad %11 %100 |
| |
| ; Uses of the combined image sampler are preserved. |
| ; CHECK: OpCopyObject %11 %combined |
| |
| %7 = OpCopyObject %11 %combined |
| OpReturn |
| OpFunctionEnd |
| )"; |
| auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( |
| kTest, /* do_validation= */ true); |
| EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; |
| } |
| |
| TEST_P(SplitCombinedImageSamplerPassTypeCaseTest, |
| Combined_RemapLoad_RelaxedPrecisionOnVarCopied) { |
| // All decorations on the variable are copied. In this case, RelaxedPrecision |
| const std::string kTest = Preamble() + |
| R"( |
| OpName %combined "combined" |
| OpDecorate %100 DescriptorSet 0 |
| OpDecorate %100 Binding 0 |
| OpDecorate %100 RelaxedPrecision |
| |
| ; CHECK: OpName |
| ; CHECK-NOT: OpDecorate %100 |
| ; CHECK: OpDecorate %[[image_var:\d+]] DescriptorSet 0 |
| ; CHECK: OpDecorate %[[sampler_var:\d+]] DescriptorSet 0 |
| ; CHECK: OpDecorate %[[image_var]] Binding 0 |
| ; CHECK: OpDecorate %[[sampler_var]] Binding 0 |
| ; CHECK: OpDecorate %[[image_var:\d+]] RelaxedPrecision |
| ; CHECK: OpDecorate %[[sampler_var:\d+]] RelaxedPrecision |
| |
| ; CHECK: %10 = OpTypeImage % |
| ; CHECK: %[[image_ptr_ty:\w+]] = OpTypePointer UniformConstant %10 |
| ; CHECK: %[[sampler_ty:\d+]] = OpTypeSampler |
| ; CHECK: %[[sampler_ptr_ty:\w+]] = OpTypePointer UniformConstant %[[sampler_ty]] |
| |
| ; The combined image variable is replaced by an image variable and a sampler variable. |
| |
| ; CHECK-NOT: %100 = OpVariable |
| ; CHECK-NOT: OpVariable _ptr_UniformConstant_11 |
| ; CHECK-DAG: %[[sampler_var]] = OpVariable %[[sampler_ptr_ty]] UniformConstant |
| ; CHECK-DAG: %[[image_var]] = OpVariable %[[image_ptr_ty]] UniformConstant |
| ; CHECK: = OpFunction |
| |
| ; The load of the combined image+sampler is replaced by a two loads, then |
| ; a combination operation. |
| ; CHECK: %[[im:\d+]] = OpLoad %10 %[[image_var]] |
| ; CHECK: %[[s:\d+]] = OpLoad %[[sampler_ty]] %[[sampler_var]] |
| ; CHECK: %combined = OpSampledImage %11 %[[im]] %[[s]] |
| |
| %bool = OpTypeBool ; location marker |
| )" + BasicTypes() + |
| " %10 = " + GetParam().image_type_decl + R"( |
| %11 = OpTypeSampledImage %10 |
| %_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11 |
| |
| %100 = OpVariable %_ptr_UniformConstant_11 UniformConstant |
| %main = OpFunction %void None %voidfn |
| %main_0 = OpLabel |
| %combined = OpLoad %11 %100 |
| |
| ; Uses of the combined image sampler are preserved. |
| ; CHECK: OpCopyObject %11 %combined |
| |
| %7 = OpCopyObject %11 %combined |
| OpReturn |
| OpFunctionEnd |
| )"; |
| auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( |
| kTest, /* do_validation= */ true); |
| EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; |
| } |
| |
| TEST_P(SplitCombinedImageSamplerPassTypeCaseTest, |
| Combined_RemapLoad_RelaxedPrecisionOnLoadCopied) { |
| // Copy decorations form an OpLoad that is replaced. |
| const std::string kTest = Preamble() + |
| R"( |
| OpName %combined "combined" |
| OpDecorate %100 DescriptorSet 0 |
| OpDecorate %100 Binding 0 |
| OpDecorate %combined RelaxedPrecision |
| |
| ; CHECK: OpName |
| ; CHECK-NOT: OpDecorate %100 |
| ; CHECK: OpDecorate %[[image_var:\d+]] DescriptorSet 0 |
| ; CHECK: OpDecorate %[[sampler_var:\d+]] DescriptorSet 0 |
| ; CHECK: OpDecorate %[[image_var]] Binding 0 |
| ; CHECK: OpDecorate %[[sampler_var]] Binding 0 |
| |
| ; This is what we are checking in this test. |
| ; CHECK: OpDecorate %[[im:\d+]] RelaxedPrecision |
| ; CHECK: OpDecorate %[[s:\d+]] RelaxedPrecision |
| |
| ; CHECK: %10 = OpTypeImage % |
| ; CHECK: %[[image_ptr_ty:\w+]] = OpTypePointer UniformConstant %10 |
| ; CHECK: %[[sampler_ty:\d+]] = OpTypeSampler |
| ; CHECK: %[[sampler_ptr_ty:\w+]] = OpTypePointer UniformConstant %[[sampler_ty]] |
| |
| ; The combined image variable is replaced by an image variable and a sampler variable. |
| |
| ; CHECK-NOT: %100 = OpVariable |
| ; CHECK-NOT: OpVariable _ptr_UniformConstant_11 |
| ; CHECK-DAG: %[[sampler_var]] = OpVariable %[[sampler_ptr_ty]] UniformConstant |
| ; CHECK-DAG: %[[image_var]] = OpVariable %[[image_ptr_ty]] UniformConstant |
| ; CHECK: = OpFunction |
| |
| ; The load of the combined image+sampler is replaced by a two loads, then |
| ; a combination operation. The new loads get the same decorations that the |
| ; original load had. |
| ; CHECK: %[[im]] = OpLoad %10 %[[image_var]] |
| ; CHECK: %[[s]] = OpLoad %[[sampler_ty]] %[[sampler_var]] |
| ; CHECK: %combined = OpSampledImage %11 %[[im]] %[[s]] |
| |
| %bool = OpTypeBool ; location marker |
| )" + BasicTypes() + |
| " %10 = " + GetParam().image_type_decl + R"( |
| %11 = OpTypeSampledImage %10 |
| %_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11 |
| |
| %100 = OpVariable %_ptr_UniformConstant_11 UniformConstant |
| %main = OpFunction %void None %voidfn |
| %main_0 = OpLabel |
| %combined = OpLoad %11 %100 |
| |
| ; Uses of the combined image sampler are preserved. |
| ; CHECK: OpCopyObject %11 %combined |
| |
| %7 = OpCopyObject %11 %combined |
| OpReturn |
| OpFunctionEnd |
| )"; |
| auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( |
| kTest, /* do_validation= */ true); |
| EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; |
| } |
| |
| TEST_P(SplitCombinedImageSamplerPassTypeCaseTest, |
| Combined_DeletesCopyObjectOfPtr) { |
| // OpCopyObject is deleted, and its uses updated. |
| const std::string kTest = Preamble() + |
| R"( |
| OpDecorate %100 DescriptorSet 0 |
| OpDecorate %100 Binding 0 |
| |
| ; CHECK: OpName |
| ; CHECK-NOT: OpDecorate %100 |
| ; CHECK: OpDecorate %[[image_var:\d+]] DescriptorSet 0 |
| ; CHECK: OpDecorate %[[sampler_var:\d+]] DescriptorSet 0 |
| ; CHECK: OpDecorate %[[image_var]] Binding 0 |
| ; CHECK: OpDecorate %[[sampler_var]] Binding 0 |
| |
| ; CHECK: %10 = OpTypeImage % |
| ; CHECK: %[[image_ptr_ty:\w+]] = OpTypePointer UniformConstant %10 |
| ; CHECK: %[[sampler_ty:\d+]] = OpTypeSampler |
| ; CHECK: %[[sampler_ptr_ty:\w+]] = OpTypePointer UniformConstant %[[sampler_ty]] |
| |
| ; The combined image variable is replaced by an image variable and a sampler variable. |
| |
| ; CHECK-NOT: %100 = OpVariable |
| ; CHECK-DAG: %[[sampler_var]] = OpVariable %[[sampler_ptr_ty]] UniformConstant |
| ; CHECK-DAG: %[[image_var]] = OpVariable %[[image_ptr_ty]] UniformConstant |
| ; CHECK: = OpFunction |
| |
| |
| )" + BasicTypes() + |
| " %10 = " + GetParam().image_type_decl + R"( |
| %11 = OpTypeSampledImage %10 |
| %_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11 |
| |
| %100 = OpVariable %_ptr_UniformConstant_11 UniformConstant |
| %main = OpFunction %void None %voidfn |
| %main_0 = OpLabel |
| %101 = OpCopyObject %_ptr_UniformConstant_11 %100 |
| %102 = OpLoad %11 %101 |
| %103 = OpCopyObject %_ptr_UniformConstant_11 %101 |
| %104 = OpCopyObject %11 %102 ;; this copy survives |
| OpReturn |
| OpFunctionEnd |
| |
| ; The OpCopyObject instructions are removed. |
| ; The load of the combined image+sampler is replaced by a two loads, then |
| ; a combination operation. The only OpCopyObject that remains is the copy |
| ; of the copy of the sampled image value. |
| ; CHECK: %[[im:\d+]] = OpLoad %10 %[[image_var]] |
| ; CHECK: %[[s:\d+]] = OpLoad %[[sampler_ty]] %[[sampler_var]] |
| ; CHECK: %[[si:\d+]] = OpSampledImage %11 %[[im]] %[[s]] |
| ; CHECK-NEXT: OpCopyObject %11 %[[si]] |
| ; CHECK-NEXT: OpReturn |
| )"; |
| |
| auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( |
| kTest, /* do_validation= */ true); |
| EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; |
| } |
| |
| TEST_P(SplitCombinedImageSamplerPassTypeCaseTest, ArrayCombined_RemapLoad) { |
| const std::string kTest = Preamble() + |
| R"( |
| OpName %combined "combined" |
| OpDecorate %100 DescriptorSet 0 |
| OpDecorate %100 Binding 0 |
| |
| ; CHECK: OpName |
| ; CHECK-NOT: OpDecorate %100 |
| ; CHECK: OpDecorate %[[image_var:\d+]] DescriptorSet 0 |
| ; CHECK: OpDecorate %[[sampler_var:\d+]] DescriptorSet 0 |
| ; CHECK: OpDecorate %[[image_var]] Binding 0 |
| ; CHECK: OpDecorate %[[sampler_var]] Binding 0 |
| |
| ; CHECK: %10 = OpTypeImage % |
| ; CHECK: %[[image_ptr_ty:\w+]] = OpTypePointer UniformConstant %10 |
| ; CHECK: %[[sampler_ty:\d+]] = OpTypeSampler |
| ; CHECK: %[[sampler_ptr_ty:\w+]] = OpTypePointer UniformConstant %[[sampler_ty]] |
| |
| ; The combined image variable is replaced by an image variable and a sampler variable. |
| |
| ; CHECK: %[[array_image_ty:\w+]] = OpTypeArray %10 %uint_3 |
| ; CHECK: %[[ptr_array_image_ty:\w+]] = OpTypePointer UniformConstant %[[array_image_ty]] |
| |
| ; CHECK: %[[array_sampler_ty:\w+]] = OpTypeArray %[[sampler_ty]] %uint_3 |
| ; CHECK: %[[ptr_array_sampler_ty:\w+]] = OpTypePointer UniformConstant %[[array_sampler_ty]] |
| |
| ; CHECK-NOT: %100 = OpVariable |
| ; CHECK-DAG: %[[sampler_var]] = OpVariable %[[ptr_array_sampler_ty]] UniformConstant |
| ; CHECK-DAG: %[[image_var]] = OpVariable %[[ptr_array_image_ty]] UniformConstant |
| ; CHECK: = OpFunction |
| |
| ; The access chain and load is replaced by two access chains, two loads, then |
| ; a combine operation. |
| ; CHECK: %[[ptr_im:\d+]] = OpAccessChain %[[image_ptr_ty]] %[[image_var]] %uint_1 |
| ; CHECK: %[[ptr_s:\d+]] = OpAccessChain %[[sampler_ptr_ty]] %[[sampler_var]] %uint_1 |
| ; CHECK: %[[im:\d+]] = OpLoad %10 %[[ptr_im]] |
| ; CHECK: %[[s:\d+]] = OpLoad %[[sampler_ty]] %[[ptr_s]] |
| ; CHECK: %combined = OpSampledImage %11 %[[im]] %[[s]] |
| |
| )" + BasicTypes() + |
| " %10 = " + GetParam().image_type_decl + R"( |
| %11 = OpTypeSampledImage %10 |
| %_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11 |
| %12 = OpTypeArray %11 %uint_3 |
| %_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12 |
| |
| %100 = OpVariable %_ptr_UniformConstant_12 UniformConstant |
| %main = OpFunction %void None %voidfn |
| %main_0 = OpLabel |
| %ptr = OpAccessChain %_ptr_UniformConstant_11 %100 %uint_1 |
| %combined = OpLoad %11 %ptr |
| |
| ; Uses of the combined image sampler are preserved. |
| ; CHECK: OpCopyObject %11 %combined |
| |
| %7 = OpCopyObject %11 %combined |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( |
| kTest, /* do_validation= */ true); |
| EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; |
| } |
| |
| TEST_P(SplitCombinedImageSamplerPassTypeCaseTest, RtArrayCombined_RemapLoad) { |
| const std::string kTest = Preamble() + |
| R"( |
| OpName %combined "combined" |
| OpDecorate %100 DescriptorSet 0 |
| OpDecorate %100 Binding 0 |
| |
| ; CHECK: OpName |
| ; CHECK-NOT: OpDecorate %100 |
| ; CHECK: OpDecorate %[[image_var:\d+]] DescriptorSet 0 |
| ; CHECK: OpDecorate %[[sampler_var:\d+]] DescriptorSet 0 |
| ; CHECK: OpDecorate %[[image_var]] Binding 0 |
| ; CHECK: OpDecorate %[[sampler_var]] Binding 0 |
| |
| ; CHECK: %10 = OpTypeImage % |
| ; CHECK: %[[image_ptr_ty:\w+]] = OpTypePointer UniformConstant %10 |
| ; CHECK: %[[sampler_ty:\d+]] = OpTypeSampler |
| ; CHECK: %[[sampler_ptr_ty:\w+]] = OpTypePointer UniformConstant %[[sampler_ty]] |
| |
| ; The combined image variable is replaced by an image variable and a sampler variable. |
| |
| ; CHECK: %[[array_image_ty:\w+]] = OpTypeRuntimeArray %10 |
| ; CHECK: %[[ptr_array_image_ty:\w+]] = OpTypePointer UniformConstant %[[array_image_ty]] |
| |
| ; CHECK: %[[array_sampler_ty:\w+]] = OpTypeRuntimeArray %[[sampler_ty]] |
| ; CHECK: %[[ptr_array_sampler_ty:\w+]] = OpTypePointer UniformConstant %[[array_sampler_ty]] |
| |
| ; CHECK-NOT: %100 = OpVariable |
| ; CHECK-DAG: %[[sampler_var]] = OpVariable %[[ptr_array_sampler_ty]] UniformConstant |
| ; CHECK-DAG: %[[image_var]] = OpVariable %[[ptr_array_image_ty]] UniformConstant |
| ; CHECK: = OpFunction |
| |
| ; The access chain and load is replaced by two access chains, two loads, then |
| ; a combine operation. |
| ; CHECK: %[[ptr_im:\d+]] = OpAccessChain %[[image_ptr_ty]] %[[image_var]] %uint_1 |
| ; CHECK: %[[ptr_s:\d+]] = OpAccessChain %[[sampler_ptr_ty]] %[[sampler_var]] %uint_1 |
| ; CHECK: %[[im:\d+]] = OpLoad %10 %[[ptr_im]] |
| ; CHECK: %[[s:\d+]] = OpLoad %[[sampler_ty]] %[[ptr_s]] |
| ; CHECK: %combined = OpSampledImage %11 %[[im]] %[[s]] |
| |
| )" + BasicTypes() + |
| " %10 = " + GetParam().image_type_decl + R"( |
| %11 = OpTypeSampledImage %10 |
| %_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11 |
| %12 = OpTypeRuntimeArray %11 |
| %_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12 |
| |
| %100 = OpVariable %_ptr_UniformConstant_12 UniformConstant |
| %main = OpFunction %void None %voidfn |
| %main_0 = OpLabel |
| %ptr = OpAccessChain %_ptr_UniformConstant_11 %100 %uint_1 |
| %combined = OpLoad %11 %ptr |
| |
| ; Uses of the combined image sampler are preserved. |
| ; CHECK: OpCopyObject %11 %combined |
| |
| %7 = OpCopyObject %11 %combined |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( |
| kTest, /* do_validation= */ true); |
| EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(AllCombinedTypes, |
| SplitCombinedImageSamplerPassTypeCaseTest, |
| ::testing::ValuesIn(ImageTypeCases())); |
| |
| // Remap entry point |
| |
| struct EntryPointRemapCase { |
| const spv_target_env environment = SPV_ENV_VULKAN_1_0; |
| const char* initial_interface = ""; |
| const char* expected_interface = nullptr; |
| }; |
| |
| std::ostream& operator<<(std::ostream& os, const EntryPointRemapCase& eprc) { |
| os << "(env " << spvLogStringForEnv(eprc.environment) << ", init " |
| << eprc.initial_interface << " -> expect " << eprc.expected_interface |
| << ")"; |
| return os; |
| } |
| |
| struct SplitCombinedImageSamplerPassEntryPointRemapTest |
| : public PassTest<::testing::TestWithParam<EntryPointRemapCase>> { |
| virtual void SetUp() override { |
| SetTargetEnv(GetParam().environment); |
| SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); |
| SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES | |
| SPV_BINARY_TO_TEXT_OPTION_INDENT | |
| SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); |
| } |
| }; |
| |
| std::vector<EntryPointRemapCase> EntryPointInterfaceCases() { |
| return std::vector<EntryPointRemapCase>{ |
| {SPV_ENV_VULKAN_1_0, " %in_var %out_var", " %in_var %out_var"}, |
| {SPV_ENV_VULKAN_1_4, " %combined_var", |
| " %combined_var_image %combined_var_sampler"}, |
| {SPV_ENV_VULKAN_1_4, " %combined_var %in_var %out_var", |
| " %combined_var_image %in_var %out_var %combined_var_sampler"}, |
| {SPV_ENV_VULKAN_1_4, " %in_var %combined_var %out_var", |
| " %in_var %combined_var_image %out_var %combined_var_sampler"}, |
| {SPV_ENV_VULKAN_1_4, " %in_var %out_var %combined_var", |
| " %in_var %out_var %combined_var_image %combined_var_sampler"}, |
| }; |
| } |
| |
| TEST_P(SplitCombinedImageSamplerPassEntryPointRemapTest, |
| EntryPoint_Combined_UsedInShader) { |
| const std::string kTest = PreambleFragment(GetParam().initial_interface) + |
| R"( |
| OpName %combined "combined" |
| OpName %combined_var "combined_var" |
| OpName %in_var "in_var" |
| OpName %out_var "out_var" |
| OpDecorate %combined_var DescriptorSet 0 |
| OpDecorate %combined_var Binding 0 |
| OpDecorate %in_var BuiltIn FragCoord |
| OpDecorate %out_var Location 0 |
| |
| ; CHECK: OpEntryPoint Fragment %main "main")" + |
| GetParam().expected_interface + R"( |
| ; These clauses ensure the expected interface is the whole interface. |
| ; CHECK-NOT: %{{\d+}} |
| ; CHECK-NOT: %in_var |
| ; CHECK-NOT: %out_var |
| ; CHECK-NOT: %combined_var |
| ; CHECK: OpExecutionMode %main OriginUpperLeft |
| |
| ; Check the var names, tracing up through the types. |
| ; CHECK: %10 = OpTypeImage %float 2D 0 0 0 1 Unknown |
| ; CHECK: %[[image_ptr_ty:\w+]] = OpTypePointer UniformConstant %10 |
| ; CHECK: %[[sampler_ty:\d+]] = OpTypeSampler |
| ; CHECK: %[[sampler_ptr_ty:\w+]] = OpTypePointer UniformConstant %[[sampler_ty]] |
| ; The combined image variable is replaced by an image variable and a sampler variable. |
| ; CHECK-DAG: %combined_var_sampler = OpVariable %[[sampler_ptr_ty]] UniformConstant |
| ; CHECK-DAG: %combined_var_image = OpVariable %[[image_ptr_ty]] UniformConstant |
| ; CHECK: = OpFunction |
| |
| %bool = OpTypeBool |
| )" + BasicTypes() + R"( %10 = OpTypeImage %float 2D 0 0 0 1 Unknown |
| %11 = OpTypeSampledImage %10 |
| %_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11 |
| %in_ptr_v4f = OpTypePointer Input %v4float |
| %in_var = OpVariable %in_ptr_v4f Input |
| %out_ptr_v4f = OpTypePointer Output %v4float |
| %out_var = OpVariable %out_ptr_v4f Output |
| |
| %combined_var = OpVariable %_ptr_UniformConstant_11 UniformConstant |
| %main = OpFunction %void None %voidfn |
| ;CHECK: %main_0 = OpLabel |
| ;CHECK: OpLoad |
| |
| %main_0 = OpLabel |
| %combined = OpLoad %11 %combined_var |
| OpReturn |
| OpFunctionEnd |
| )"; |
| auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( |
| kTest, /* do_validation= */ true); |
| EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; |
| } |
| |
| TEST_P(SplitCombinedImageSamplerPassEntryPointRemapTest, |
| EntryPoint_Combined_UsedOnlyInEntryPointInstruction) { |
| // If the combined var is in the interface, that is enough to trigger |
| // its replacement. Otherwise the entry point interface is untouched |
| // when the combined var is not otherwise used. |
| const bool combined_var_in_interface = |
| std::string(GetParam().initial_interface).find("%combined_var") != |
| std::string::npos; |
| if (combined_var_in_interface) { |
| const std::string kTest = PreambleFragment(GetParam().initial_interface) + |
| R"( |
| OpName %combined_var "combined_var" |
| OpName %in_var "in_var" |
| OpName %out_var "out_var" |
| OpDecorate %combined_var DescriptorSet 0 |
| OpDecorate %combined_var Binding 0 |
| OpDecorate %in_var BuiltIn FragCoord |
| OpDecorate %out_var Location 0 |
| |
| ; CHECK: OpEntryPoint Fragment %main "main")" + |
| GetParam().expected_interface + R"( |
| ; These clauses ensure the expected interface is the whole interface. |
| ; CHECK-NOT: %{{\d+}} |
| ; CHECK-NOT: %in_var |
| ; CHECK-NOT: %out_var |
| ; CHECK-NOT: %combined_var |
| ; CHECK: OpExecutionMode %main OriginUpperLeft |
| |
| %bool = OpTypeBool |
| )" + BasicTypes() + R"( %10 = OpTypeImage %float 2D 0 0 0 1 Unknown |
| %11 = OpTypeSampledImage %10 |
| %_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11 |
| %in_ptr_v4f = OpTypePointer Input %v4float |
| %in_var = OpVariable %in_ptr_v4f Input |
| %out_ptr_v4f = OpTypePointer Output %v4float |
| %out_var = OpVariable %out_ptr_v4f Output |
| |
| ; %combined_var is not used! |
| %combined_var = OpVariable %_ptr_UniformConstant_11 UniformConstant |
| %main = OpFunction %void None %voidfn |
| %main_0 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| auto [disasm, status] = |
| SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( |
| kTest, /* do_validation= */ true); |
| EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; |
| } |
| } |
| |
| TEST_P(SplitCombinedImageSamplerPassEntryPointRemapTest, |
| EntryPoint_Combined_Unused) { |
| // If the combined var is in the interface, that is enough to trigger |
| // its replacement. Otherwise the entry point interface is untouched |
| // when the combined var is not otherwise used. |
| const bool combined_var_in_interface = |
| std::string(GetParam().initial_interface).find("%combined_var") != |
| std::string::npos; |
| if (!combined_var_in_interface) { |
| const std::string kTest = PreambleFragment(GetParam().initial_interface) + |
| R"( |
| ; CHECK: OpEntryPoint Fragment %main "main")" + |
| GetParam().initial_interface // Note this is the |
| // intial interface |
| + R"( |
| ; These clauses ensure the expected interface is the whole interface. |
| ; CHECK-NOT: %{{\d+}} |
| ; CHECK-NOT: %in_var |
| ; CHECK-NOT: %out_var |
| ; CHECK-NOT: %combined_var |
| ; CHECK: OpExecutionMode %main OriginUpperLeft |
| |
| ; The variable disappears. |
| ; CHECK-NOT: %combined_var = |
| ; CHECK: OpFunctionEnd |
| OpName %combined_var "combined_var" |
| OpName %in_var "in_var" |
| OpName %out_var "out_var" |
| OpDecorate %combined_var DescriptorSet 0 |
| OpDecorate %combined_var Binding 0 |
| OpDecorate %in_var BuiltIn FragCoord |
| OpDecorate %out_var Location 0 |
| |
| |
| %bool = OpTypeBool |
| )" + BasicTypes() + R"( %10 = OpTypeImage %float 2D 0 0 0 1 Unknown |
| %11 = OpTypeSampledImage %10 |
| %_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11 |
| %in_ptr_v4f = OpTypePointer Input %v4float |
| %in_var = OpVariable %in_ptr_v4f Input |
| %out_ptr_v4f = OpTypePointer Output %v4float |
| %out_var = OpVariable %out_ptr_v4f Output |
| |
| ; %combined_var is not used! |
| %combined_var = OpVariable %_ptr_UniformConstant_11 UniformConstant |
| %main = OpFunction %void None %voidfn |
| %main_0 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| auto [disasm, status] = |
| SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( |
| kTest, /* do_validation= */ true); |
| EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(EntryPointRemap, |
| SplitCombinedImageSamplerPassEntryPointRemapTest, |
| ::testing::ValuesIn(EntryPointInterfaceCases())); |
| |
| // Remap function types |
| |
| struct FunctionTypeCase { |
| const char* initial_type_params = ""; |
| const char* expected_type_params = ""; |
| }; |
| |
| std::ostream& operator<<(std::ostream& os, const FunctionTypeCase& ftc) { |
| os << "(init " << ftc.initial_type_params << " -> expect " |
| << ftc.expected_type_params << ")"; |
| return os; |
| } |
| |
| struct SplitCombinedImageSamplerPassFunctionTypeTest |
| : public PassTest<::testing::TestWithParam<FunctionTypeCase>> { |
| virtual void SetUp() override { |
| SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); |
| SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES | |
| SPV_BINARY_TO_TEXT_OPTION_INDENT | |
| SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); |
| } |
| }; |
| |
| std::vector<FunctionTypeCase> FunctionTypeCases() { |
| return std::vector<FunctionTypeCase>{ |
| {"", ""}, |
| {" %image_ty", " %image_ty"}, |
| {" %sampler_ty", " %sampler_ty"}, |
| {" %sampled_image_ty", " %image_ty %sampler_ty"}, |
| {" %uint %sampled_image_ty %float", |
| " %uint %image_ty %sampler_ty %float"}, |
| {" %ptr_sampled_image_ty", |
| " %_ptr_UniformConstant_image_ty %_ptr_UniformConstant_sampler_ty"}, |
| {" %uint %ptr_sampled_image_ty %float", |
| " %uint %_ptr_UniformConstant_image_ty %_ptr_UniformConstant_sampler_ty " |
| "%float"}, |
| {" %uint %ptr_sampled_image_ty %ptr_sampled_image_ty %float", |
| " %uint %_ptr_UniformConstant_image_ty %_ptr_UniformConstant_sampler_ty " |
| "%_ptr_UniformConstant_image_ty %_ptr_UniformConstant_sampler_ty " |
| "%float"}, |
| }; |
| } |
| |
| TEST_P(SplitCombinedImageSamplerPassFunctionTypeTest, |
| ReplaceCombinedImageSamplersOnly) { |
| const std::string kTest = Preamble() + +R"( |
| OpName %f_ty "f_ty" |
| OpName %sampler_ty "sampler_ty" |
| OpName %image_ty "image_ty" |
| OpName %sampled_image_ty "sampled_image_ty" |
| OpName %ptr_sampled_image_ty "sampled_image_ty" |
| |
| )" + BasicTypes() + R"( |
| |
| %sampler_ty = OpTypeSampler |
| %image_ty = OpTypeImage %float 2D 0 0 0 1 Unknown |
| %sampled_image_ty = OpTypeSampledImage %image_ty |
| %ptr_sampled_image_ty = OpTypePointer UniformConstant %sampled_image_ty |
| |
| %f_ty = OpTypeFunction %float)" + |
| GetParam().initial_type_params + R"( |
| |
| ; CHECK: %f_ty = OpTypeFunction %float)" + |
| GetParam().expected_type_params + R"( |
| )" + Main(); |
| |
| auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( |
| kTest, /* do_validation= */ true); |
| EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; |
| } |
| |
| TEST_P(SplitCombinedImageSamplerPassFunctionTypeTest, AvoidDuplicateType) { |
| // SPIR-V does not allow duplicate non-aggregate types. That includes function |
| // types. Test that when replacing function type parameters would cause a |
| // collision, that the original function type is replaced with the new one. |
| const std::string initial_params(GetParam().initial_type_params); |
| const std::string expected_params(GetParam().expected_type_params); |
| const std::string kTest = Preamble() + +R"( |
| OpName %sampler_ty "sampler_ty" |
| OpName %image_ty "image_ty" |
| OpName %sampled_image_ty "sampled_image_ty" |
| OpName %_ptr_UniformConstant_sampler_ty "_ptr_UniformConstant_sampler_ty" |
| OpName %_ptr_UniformConstant_image_ty "_ptr_UniformConstant_image_ty" |
| OpName %ptr_sampled_image_ty "sampled_image_ty" |
| OpName %dest_ty "dest_ty" |
| |
| )" + BasicTypes() + R"( |
| |
| %sampler_ty = OpTypeSampler |
| %image_ty = OpTypeImage %float 2D 0 0 0 1 Unknown |
| %sampled_image_ty = OpTypeSampledImage %image_ty |
| %ptr_sampled_image_ty = OpTypePointer UniformConstant %sampled_image_ty |
| %_ptr_UniformConstant_image_ty = OpTypePointer UniformConstant %image_ty |
| %_ptr_UniformConstant_sampler_ty = OpTypePointer UniformConstant %sampler_ty |
| |
| %100 = OpTypeFunction %float)" + |
| initial_params + R"( |
| %dest_ty = OpTypeFunction %float)" + |
| expected_params + R"( |
| |
| ; CHECK: OpTypeSampler |
| ; CHECK-NOT: %100 = |
| ; CHECK: %dest_ty = OpTypeFunction %float)" + |
| expected_params + R"( |
| ; CHECK-NOT: %100 = |
| ; CHECK: %main = OpFunction |
| )" + Main(); |
| // The original source is invalid if initial and expected params are the same, |
| // because the type is already duplicated. |
| // Only test when they are different. |
| if (initial_params != expected_params) { |
| auto [disasm, status] = |
| SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( |
| kTest, /* do_validation= */ true); |
| EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(FunctionTypeRemap, |
| SplitCombinedImageSamplerPassFunctionTypeTest, |
| ::testing::ValuesIn(FunctionTypeCases())); |
| |
| // Test array and runtime-array cases for function type replacement. |
| |
| TEST_F(SplitCombinedImageSamplerPassTest, FunctionType_ReplaceSampledImageArg) { |
| // The original module has a sampled image type, used only as a function |
| // parameter. We still want to replace it. But no other sampled-image types |
| // exist. This proves that the pass needs a sampled_image_used_as_param_ |
| // state variable. |
| const std::string kTest = Preamble() + +R"( |
| OpName %f_ty "f_ty" |
| OpName %sampler_ty "sampler_ty" |
| OpName %image_ty "image_ty" |
| OpName %sampled_image_ty "sampled_image_ty" |
| |
| )" + BasicTypes() + R"( |
| |
| %sampler_ty = OpTypeSampler |
| %image_ty = OpTypeImage %float 2D 0 0 0 1 Unknown |
| %sampled_image_ty = OpTypeSampledImage %image_ty |
| |
| %f_ty = OpTypeFunction %float %sampled_image_ty %float |
| ; CHECK: %f_ty = OpTypeFunction %float %image_ty %sampler_ty %float |
| )" + Main(); |
| |
| auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( |
| kTest, /* do_validation= */ true); |
| EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; |
| } |
| |
| TEST_F(SplitCombinedImageSamplerPassTest, FunctionType_ReplaceArrayArg) { |
| const std::string kTest = Preamble() + +R"( |
| OpName %f_ty "f_ty" |
| OpName %sampler_ty "sampler_ty" |
| OpName %image_ty "image_ty" |
| OpName %sampled_image_ty "sampled_image_ty" |
| OpName %ptr_array_si_ty "ptr_array_si_ty" |
| |
| )" + BasicTypes() + R"( |
| |
| %sampler_ty = OpTypeSampler |
| %image_ty = OpTypeImage %float 2D 0 0 0 1 Unknown |
| %sampled_image_ty = OpTypeSampledImage %image_ty |
| %array_si_ty = OpTypeArray %sampled_image_ty %uint_3 |
| %ptr_array_si_ty = OpTypePointer UniformConstant %array_si_ty |
| |
| ; CHECK: %[[array_i_ty:\w+]] = OpTypeArray %image_ty %uint_3 |
| ; CHECK: %[[ptr_array_i_ty:\w+]] = OpTypePointer UniformConstant %[[array_i_ty]] |
| ; CHECK: %[[array_s_ty:\w+]] = OpTypeArray %sampler_ty %uint_3 |
| ; CHECK: %[[ptr_array_s_ty:\w+]] = OpTypePointer UniformConstant %[[array_s_ty]] |
| |
| %f_ty = OpTypeFunction %float %uint %ptr_array_si_ty %float |
| ; CHECK: %f_ty = OpTypeFunction %float %uint %[[ptr_array_i_ty]] %[[ptr_array_s_ty]] %float |
| )" + Main(); |
| |
| auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( |
| kTest, /* do_validation= */ true); |
| EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; |
| } |
| |
| TEST_F(SplitCombinedImageSamplerPassTest, FunctionType_ReplaceRtArrayArg) { |
| const std::string kTest = Preamble() + +R"( |
| OpName %f_ty "f_ty" |
| OpName %sampler_ty "sampler_ty" |
| OpName %image_ty "image_ty" |
| OpName %sampled_image_ty "sampled_image_ty" |
| OpName %ptr_array_si_ty "ptr_array_si_ty" |
| |
| )" + BasicTypes() + R"( |
| |
| %sampler_ty = OpTypeSampler |
| %image_ty = OpTypeImage %float 2D 0 0 0 1 Unknown |
| %sampled_image_ty = OpTypeSampledImage %image_ty |
| %array_si_ty = OpTypeRuntimeArray %sampled_image_ty |
| %ptr_array_si_ty = OpTypePointer UniformConstant %array_si_ty |
| |
| ; CHECK: %[[array_i_ty:\w+]] = OpTypeRuntimeArray %image_ty |
| ; CHECK: %[[ptr_array_i_ty:\w+]] = OpTypePointer UniformConstant %[[array_i_ty]] |
| ; CHECK: %[[array_s_ty:\w+]] = OpTypeRuntimeArray %sampler_ty |
| ; CHECK: %[[ptr_array_s_ty:\w+]] = OpTypePointer UniformConstant %[[array_s_ty]] |
| |
| %f_ty = OpTypeFunction %float %uint %ptr_array_si_ty %float |
| ; CHECK: %f_ty = OpTypeFunction %float %uint %[[ptr_array_i_ty]] %[[ptr_array_s_ty]] %float |
| )" + Main(); |
| |
| auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( |
| kTest, /* do_validation= */ true); |
| EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; |
| } |
| |
| // Remap function bodies |
| |
| std::string NamedITypes() { |
| return R"( |
| OpName %f "f" |
| OpName %f_ty "f_ty" |
| OpName %i_ty "i_ty" |
| OpName %s_ty "s_ty" |
| OpName %p_i_ty "p_i_ty" |
| OpName %p_s_ty "p_s_ty" |
| )"; |
| } |
| |
| std::string NamedCombinedTypes() { |
| return R"( |
| OpName %si_ty "si_ty" |
| OpName %p_si_ty "p_si_ty" |
| OpName %array_si_ty "array_si_ty" |
| OpName %rtarray_si_ty "rtarray_si_ty" |
| OpName %p_array_si_ty "p_array_si_ty" |
| OpName %p_rtarray_si_ty "p_rtarray_si_ty" |
| )"; |
| } |
| |
| std::string NamedCaller() { |
| return R"( |
| OpName %caller_ty "caller_ty" |
| OpName %caller "caller" |
| OpName %caller_entry "caller_entry" |
| OpName %caller_call "caller_call" |
| OpName %caller_arg "caller_arg" |
| )"; |
| } |
| |
| std::string ITypes() { |
| return R"( |
| %i_ty = OpTypeImage %float 2D 0 0 0 1 Unknown |
| %s_ty = OpTypeSampler |
| %p_i_ty = OpTypePointer UniformConstant %i_ty |
| %p_s_ty = OpTypePointer UniformConstant %s_ty |
| )"; |
| } |
| |
| std::string CombinedTypes() { |
| return R"( |
| %si_ty = OpTypeSampledImage %i_ty |
| %p_si_ty = OpTypePointer UniformConstant %si_ty |
| %array_si_ty = OpTypeArray %si_ty %uint_3 |
| %p_array_si_ty = OpTypePointer UniformConstant %array_si_ty |
| %rtarray_si_ty = OpTypeRuntimeArray %si_ty |
| %p_rtarray_si_ty = OpTypePointer UniformConstant %rtarray_si_ty |
| )"; |
| } |
| |
| TEST_F(SplitCombinedImageSamplerPassTest, FunctionBody_ScalarNoChange) { |
| const std::string kTest = Preamble() + NamedITypes() + NamedCombinedTypes() + |
| BasicTypes() + ITypes() + CombinedTypes() + R"( |
| |
| ; CHECK: %f_ty = OpTypeFunction %float %i_ty %s_ty %p_i_ty %p_s_ty |
| %f_ty = OpTypeFunction %float %i_ty %s_ty %p_i_ty %p_s_ty |
| |
| ; CHECK: %f = OpFunction %float None %f_ty |
| ; CHECK-NEXT: OpFunctionParameter %i_ty |
| ; CHECK-NEXT: OpFunctionParameter %s_ty |
| ; CHECK-NEXT: OpFunctionParameter %p_i_ty |
| ; CHECK-NEXT: OpFunctionParameter %p_s_ty |
| ; CHECK-NEXT: OpLabel |
| %f = OpFunction %float None %f_ty |
| %100 = OpFunctionParameter %i_ty |
| %101 = OpFunctionParameter %s_ty |
| %102 = OpFunctionParameter %p_i_ty |
| %103 = OpFunctionParameter %p_s_ty |
| %110 = OpLabel |
| OpReturnValue %float_0 |
| OpFunctionEnd |
| )" + Main(); |
| |
| auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( |
| kTest, /* do_validation= */ true); |
| EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; |
| } |
| |
| TEST_F(SplitCombinedImageSamplerPassTest, |
| FunctionBody_SampledImage_OpImageSample) { |
| const std::string kTest = Preamble() + NamedITypes() + NamedCombinedTypes() + |
| BasicTypes() + ITypes() + CombinedTypes() + R"( |
| |
| ; CHECK: %f_ty = OpTypeFunction %v4float %uint %i_ty %s_ty %float |
| %f_ty = OpTypeFunction %v4float %uint %si_ty %float |
| |
| ; CHECK: %f = OpFunction %v4float None %f_ty |
| ; CHECK: OpFunctionParameter %uint |
| ; CHECK-NEXT: %[[i:\w+]] = OpFunctionParameter %i_ty |
| ; CHECK-NEXT: %[[s:\w+]] = OpFunctionParameter %s_ty |
| ; CHECK-NEXT: OpFunctionParameter %float |
| ; CHECK-NEXT: OpLabel |
| ; CHECK-NEXT: %[[si:\w+]] = OpSampledImage %si_ty %[[i]] %[[s]] |
| ; CHECK-NEXT: %200 = OpImageSampleExplicitLod %v4float %[[si]] %13 Lod %float_0 |
| ; CHECK-NEXT: OpReturnValue %200 |
| |
| %f = OpFunction %v4float None %f_ty |
| %100 = OpFunctionParameter %uint |
| %101 = OpFunctionParameter %si_ty ; replace this |
| %110 = OpFunctionParameter %float |
| %120 = OpLabel |
| %200 = OpImageSampleExplicitLod %v4float %101 %13 Lod %float_0 |
| OpReturnValue %200 |
| OpFunctionEnd |
| |
| )" + Main(); |
| |
| auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( |
| kTest, /* do_validation= */ true); |
| EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; |
| } |
| |
| TEST_F(SplitCombinedImageSamplerPassTest, FunctionBody_SampledImage_OpImage) { |
| const std::string kTest = Preamble() + NamedITypes() + NamedCombinedTypes() + |
| BasicTypes() + ITypes() + CombinedTypes() + R"( |
| |
| ; CHECK: %f_ty = OpTypeFunction %void %uint %i_ty %s_ty %float |
| %f_ty = OpTypeFunction %void %uint %si_ty %float |
| |
| ; CHECK: %f = OpFunction %void None %f_ty |
| ; CHECK: OpFunctionParameter %uint |
| ; CHECK-NEXT: %[[i:\w+]] = OpFunctionParameter %i_ty |
| ; CHECK-NEXT: %[[s:\w+]] = OpFunctionParameter %s_ty |
| ; CHECK-NEXT: OpFunctionParameter %float |
| ; CHECK-NEXT: OpLabel |
| ; CHECK-NEXT: %[[si:\w+]] = OpSampledImage %si_ty %[[i]] %[[s]] |
| ; CHECK-NEXT: %200 = OpImage %i_ty %[[si]] |
| ; CHECK-NEXT: OpReturn |
| |
| %f = OpFunction %void None %f_ty |
| %100 = OpFunctionParameter %uint |
| %101 = OpFunctionParameter %si_ty ; replace this |
| %110 = OpFunctionParameter %float |
| %120 = OpLabel |
| %200 = OpImage %i_ty %101 |
| OpReturn |
| OpFunctionEnd |
| |
| )" + Main(); |
| |
| auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( |
| kTest, /* do_validation= */ true); |
| EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; |
| } |
| |
| TEST_F(SplitCombinedImageSamplerPassTest, FunctionBody_PtrSampledImage) { |
| const std::string kTest = Preamble() + NamedITypes() + NamedCombinedTypes() + |
| BasicTypes() + ITypes() + CombinedTypes() + R"( |
| |
| ; CHECK: %f_ty = OpTypeFunction %v4float %uint %p_i_ty %p_s_ty %float |
| %f_ty = OpTypeFunction %v4float %uint %p_si_ty %float |
| |
| ; CHECK: %f = OpFunction %v4float None %f_ty |
| ; CHECK-NEXT: OpFunctionParameter %uint |
| ; CHECK-NEXT: %[[pi:\w+]] = OpFunctionParameter %p_i_ty |
| ; CHECK-NEXT: %[[ps:\w+]] = OpFunctionParameter %p_s_ty |
| ; CHECK-NEXT: OpFunctionParameter %float |
| ; CHECK-NEXT: OpLabel |
| ; CHECK-NEXT: %[[i:\w+]] = OpLoad %i_ty %[[pi]] |
| ; CHECK-NEXT: %[[s:\w+]] = OpLoad %s_ty %[[ps]] |
| ; CHECK-NEXT: %[[si:\w+]] = OpSampledImage %si_ty %[[i]] %[[s]] |
| ; CHECK-NEXT: %200 = OpImageSampleExplicitLod %v4float %[[si]] %13 Lod %float_0 |
| ; CHECK-NEXT: OpReturnValue %200 |
| |
| %f = OpFunction %v4float None %f_ty |
| %100 = OpFunctionParameter %uint |
| %101 = OpFunctionParameter %p_si_ty ; replace this |
| %110 = OpFunctionParameter %float |
| %120 = OpLabel |
| %121 = OpLoad %si_ty %101 |
| %200 = OpImageSampleExplicitLod %v4float %121 %13 Lod %float_0 |
| OpReturnValue %200 |
| OpFunctionEnd |
| )" + Main(); |
| |
| auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( |
| kTest, /* do_validation= */ true); |
| EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; |
| } |
| |
| TEST_F(SplitCombinedImageSamplerPassTest, |
| FunctionCall_NoImageOrSampler_NoChange) { |
| const std::string kTest = Preamble() + NamedITypes() + NamedCaller() + |
| BasicTypes() + ITypes() + CombinedTypes() + R"( |
| |
| ; CHECK: %f_ty = OpTypeFunction %void %uint %float |
| %f_ty = OpTypeFunction %void %uint %float |
| %caller_ty = OpTypeFunction %float ; make it return non-void otherwise it's just like main |
| |
| ; The called function does not change |
| ; CHECK: %f = OpFunction %void None %f_ty |
| ; CHECK-NEXT: = OpFunctionParameter %uint |
| ; CHECK-NEXT: = OpFunctionParameter %float |
| ; CHECK-NEXT: = OpLabel |
| ; CHECK-NEXT: OpReturn |
| ; CHECK-NEXT: OpFunctionEnd |
| |
| %f = OpFunction %void None %f_ty |
| %100 = OpFunctionParameter %uint |
| %101 = OpFunctionParameter %float |
| %110 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| |
| ; The caller does not change |
| ; CHECK: %caller = OpFunction %float None %caller_ty |
| ; CHECK-NEXT: %caller_entry = OpLabel |
| ; CHECK-NEXT: %caller_arg = OpCopyObject %uint %uint_0 |
| ; CHECK-NEXT: OpFunctionCall %void %f %caller_arg %float_0 |
| ; CHECK-NEXT: OpReturnValue %float_0 |
| ; CHECK-NEXT: OpFunctionEnd |
| |
| %caller = OpFunction %float None %caller_ty |
| %caller_entry = OpLabel |
| %caller_arg = OpCopyObject %uint %uint_0 |
| %caller_call = OpFunctionCall %void %f %caller_arg %float_0 |
| OpReturnValue %float_0 |
| OpFunctionEnd |
| |
| )" + Main(); |
| |
| auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( |
| kTest, /* do_validation= */ true); |
| // We still get a success-with-change result because the boilerplate included |
| // combined types, which were removed. |
| EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; |
| } |
| |
| TEST_F(SplitCombinedImageSamplerPassTest, FunctionCall_Image_NoChange) { |
| const std::string kTest = Preamble() + NamedITypes() + NamedCaller() + |
| BasicTypes() + ITypes() + R"( |
| |
| ; CHECK: %f_ty = OpTypeFunction %void %i_ty |
| %f_ty = OpTypeFunction %void %i_ty |
| %caller_ty = OpTypeFunction %float %i_ty |
| |
| ; The called function does not change |
| ; CHECK: %f = OpFunction %void None %f_ty |
| ; CHECK-NEXT: = OpFunctionParameter %i_ty |
| ; CHECK-NEXT: = OpLabel |
| ; CHECK-NEXT: OpReturn |
| ; CHECK-NEXT: OpFunctionEnd |
| |
| %f = OpFunction %void None %f_ty |
| %100 = OpFunctionParameter %i_ty |
| %110 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| |
| ; The caller does not change |
| ; CHECK: %caller = OpFunction %float None %caller_ty |
| ; CHECK-NEXT: %caller_arg = OpFunctionParameter %i_ty |
| ; CHECK-NEXT: %caller_entry = OpLabel |
| ; CHECK-NEXT: OpFunctionCall %void %f %caller_arg |
| ; CHECK-NEXT: OpReturnValue %float_0 |
| ; CHECK-NEXT: OpFunctionEnd |
| |
| %caller = OpFunction %float None %caller_ty |
| %caller_arg = OpFunctionParameter %i_ty |
| %caller_entry = OpLabel |
| %caller_call = OpFunctionCall %void %f %caller_arg |
| OpReturnValue %float_0 |
| OpFunctionEnd |
| |
| )" + Main(); |
| |
| auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( |
| kTest, /* do_validation= */ true); |
| EXPECT_EQ(status, Pass::Status::SuccessWithoutChange) << disasm; |
| } |
| |
| TEST_F(SplitCombinedImageSamplerPassTest, FunctionCall_Sampler_NoChange) { |
| const std::string kTest = Preamble() + NamedITypes() + NamedCaller() + |
| BasicTypes() + ITypes() + R"( |
| |
| ; CHECK: %f_ty = OpTypeFunction %void %s_ty |
| %f_ty = OpTypeFunction %void %s_ty |
| %caller_ty = OpTypeFunction %float %s_ty |
| |
| ; The called function does not change |
| ; CHECK: %f = OpFunction %void None %f_ty |
| ; CHECK-NEXT: = OpFunctionParameter %s_ty |
| ; CHECK-NEXT: = OpLabel |
| ; CHECK-NEXT: OpReturn |
| ; CHECK-NEXT: OpFunctionEnd |
| |
| %f = OpFunction %void None %f_ty |
| %100 = OpFunctionParameter %s_ty |
| %110 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| |
| ; The caller does not change |
| ; CHECK: %caller = OpFunction %float None %caller_ty |
| ; CHECK-NEXT: %caller_arg = OpFunctionParameter %s_ty |
| ; CHECK-NEXT: %caller_entry = OpLabel |
| ; CHECK-NEXT: OpFunctionCall %void %f %caller_arg |
| ; CHECK-NEXT: OpReturnValue %float_0 |
| ; CHECK-NEXT: OpFunctionEnd |
| |
| %caller = OpFunction %float None %caller_ty |
| %caller_arg = OpFunctionParameter %s_ty |
| %caller_entry = OpLabel |
| %caller_call = OpFunctionCall %void %f %caller_arg |
| OpReturnValue %float_0 |
| OpFunctionEnd |
| |
| )" + Main(); |
| |
| auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( |
| kTest, /* do_validation= */ true); |
| EXPECT_EQ(status, Pass::Status::SuccessWithoutChange) << disasm; |
| } |
| |
| TEST_F(SplitCombinedImageSamplerPassTest, FunctionCall_PtrImage_NoChange) { |
| const std::string kTest = Preamble() + NamedITypes() + NamedCaller() + |
| BasicTypes() + ITypes() + R"( |
| |
| ; CHECK: %f_ty = OpTypeFunction %void %p_i_ty |
| %f_ty = OpTypeFunction %void %p_i_ty |
| %caller_ty = OpTypeFunction %float %p_i_ty |
| |
| ; The called function does not change |
| ; CHECK: %f = OpFunction %void None %f_ty |
| ; CHECK-NEXT: = OpFunctionParameter %p_i_ty |
| ; CHECK-NEXT: = OpLabel |
| ; CHECK-NEXT: OpReturn |
| ; CHECK-NEXT: OpFunctionEnd |
| |
| %f = OpFunction %void None %f_ty |
| %100 = OpFunctionParameter %p_i_ty |
| %110 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| |
| ; The caller does not change |
| ; CHECK: %caller = OpFunction %float None %caller_ty |
| ; CHECK-NEXT: %caller_arg = OpFunctionParameter %p_i_ty |
| ; CHECK-NEXT: %caller_entry = OpLabel |
| ; CHECK-NEXT: OpFunctionCall %void %f %caller_arg |
| ; CHECK-NEXT: OpReturnValue %float_0 |
| ; CHECK-NEXT: OpFunctionEnd |
| |
| %caller = OpFunction %float None %caller_ty |
| %caller_arg = OpFunctionParameter %p_i_ty |
| %caller_entry = OpLabel |
| %caller_call = OpFunctionCall %void %f %caller_arg |
| OpReturnValue %float_0 |
| OpFunctionEnd |
| |
| )" + Main(); |
| |
| auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( |
| kTest, /* do_validation= */ true); |
| EXPECT_EQ(status, Pass::Status::SuccessWithoutChange) << disasm; |
| } |
| |
| TEST_F(SplitCombinedImageSamplerPassTest, FunctionCall_PtrSampler_NoChange) { |
| const std::string kTest = Preamble() + NamedITypes() + NamedCaller() + |
| BasicTypes() + ITypes() + R"( |
| |
| ; CHECK: %f_ty = OpTypeFunction %void %p_s_ty |
| %f_ty = OpTypeFunction %void %p_s_ty |
| %caller_ty = OpTypeFunction %float %p_s_ty |
| |
| ; The called function does not change |
| ; CHECK: %f = OpFunction %void None %f_ty |
| ; CHECK-NEXT: = OpFunctionParameter %p_s_ty |
| ; CHECK-NEXT: = OpLabel |
| ; CHECK-NEXT: OpReturn |
| ; CHECK-NEXT: OpFunctionEnd |
| |
| %f = OpFunction %void None %f_ty |
| %100 = OpFunctionParameter %p_s_ty |
| %110 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| |
| ; The caller does not change |
| ; CHECK: %caller = OpFunction %float None %caller_ty |
| ; CHECK-NEXT: %caller_arg = OpFunctionParameter %p_s_ty |
| ; CHECK-NEXT: %caller_entry = OpLabel |
| ; CHECK-NEXT: OpFunctionCall %void %f %caller_arg |
| ; CHECK-NEXT: OpReturnValue %float_0 |
| ; CHECK-NEXT: OpFunctionEnd |
| |
| %caller = OpFunction %float None %caller_ty |
| %caller_arg = OpFunctionParameter %p_s_ty |
| %caller_entry = OpLabel |
| %caller_call = OpFunctionCall %void %f %caller_arg |
| OpReturnValue %float_0 |
| OpFunctionEnd |
| |
| )" + Main(); |
| |
| auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( |
| kTest, /* do_validation= */ true); |
| EXPECT_EQ(status, Pass::Status::SuccessWithoutChange) << disasm; |
| } |
| |
| TEST_F(SplitCombinedImageSamplerPassTest, FunctionCall_SampledImage_Split) { |
| const std::string kTest = Preamble() + NamedITypes() + NamedCombinedTypes() + |
| NamedCaller() + BasicTypes() + ITypes() + |
| CombinedTypes() + R"( |
| |
| ; CHECK: %f_ty = OpTypeFunction %void %i_ty %s_ty |
| %f_ty = OpTypeFunction %void %si_ty |
| %caller_ty = OpTypeFunction %float %si_ty |
| |
| ; Call function arg is split. We've checked these details in other tests. |
| ; CHECK: %f = OpFunction %void None %f_ty |
| ; CHECK-NEXT: %[[callee_i:\w+]] = OpFunctionParameter %i_ty |
| ; CHECK-NEXT: %[[callee_s:\w+]] = OpFunctionParameter %s_ty |
| ; CHECK-NEXT: = OpLabel |
| ; CHECK-NEXT: OpReturn |
| ; CHECK-NEXT: OpFunctionEnd |
| |
| %f = OpFunction %void None %f_ty |
| %100 = OpFunctionParameter %si_ty |
| %110 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| |
| ; CHECK: %caller = OpFunction %float None %caller_ty |
| ; CHECK-NEXT: %[[caller_i:\w+]] = OpFunctionParameter %i_ty |
| ; CHECK-NEXT: %[[caller_s:\w+]] = OpFunctionParameter %s_ty |
| ; CHECK-NEXT: %caller_entry = OpLabel |
| ; CHECK-NEXT: %caller_call = OpFunctionCall %void %f %[[caller_i]] %[[caller_s]] |
| ; CHECK-NEXT: OpReturnValue %float_0 |
| ; CHECK-NEXT: OpFunctionEnd |
| |
| %caller = OpFunction %float None %caller_ty |
| %caller_arg = OpFunctionParameter %si_ty |
| %caller_entry = OpLabel |
| %caller_call = OpFunctionCall %void %f %caller_arg |
| OpReturnValue %float_0 |
| OpFunctionEnd |
| |
| )" + Main(); |
| |
| auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( |
| kTest, /* do_validation= */ true); |
| EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; |
| } |
| |
| TEST_F(SplitCombinedImageSamplerPassTest, |
| FunctionCall_SampledImageDuplicatedArg_Split) { |
| const std::string kTest = Preamble() + NamedITypes() + NamedCombinedTypes() + |
| NamedCaller() + BasicTypes() + ITypes() + |
| CombinedTypes() + R"( |
| |
| ; CHECK: %f_ty = OpTypeFunction %void %i_ty %s_ty %i_ty %s_ty |
| %f_ty = OpTypeFunction %void %si_ty %si_ty |
| %caller_ty = OpTypeFunction %float %si_ty |
| |
| ; Call function arg is split. We've checked these details in other tests. |
| ; CHECK: %f = OpFunction %void None %f_ty |
| ; CHECK-NEXT: %[[callee_i_0:\w+]] = OpFunctionParameter %i_ty |
| ; CHECK-NEXT: %[[callee_s_0:\w+]] = OpFunctionParameter %s_ty |
| ; CHECK-NEXT: %[[callee_i_1:\w+]] = OpFunctionParameter %i_ty |
| ; CHECK-NEXT: %[[callee_s_1:\w+]] = OpFunctionParameter %s_ty |
| ; CHECK-NEXT: = OpLabel |
| ; CHECK-NEXT: OpReturn |
| ; CHECK-NEXT: OpFunctionEnd |
| |
| %f = OpFunction %void None %f_ty |
| %100 = OpFunctionParameter %si_ty |
| %101 = OpFunctionParameter %si_ty |
| %110 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| |
| ; CHECK: %caller = OpFunction %float None %caller_ty |
| ; CHECK-NEXT: %[[caller_i:\w+]] = OpFunctionParameter %i_ty |
| ; CHECK-NEXT: %[[caller_s:\w+]] = OpFunctionParameter %s_ty |
| ; CHECK-NEXT: %caller_entry = OpLabel |
| ; CHECK-NEXT: %caller_call = OpFunctionCall %void %f %[[caller_i]] %[[caller_s]] %[[caller_i]] %[[caller_s]] |
| ; CHECK-NEXT: OpReturnValue %float_0 |
| ; CHECK-NEXT: OpFunctionEnd |
| |
| %caller = OpFunction %float None %caller_ty |
| %caller_arg = OpFunctionParameter %si_ty |
| %caller_entry = OpLabel |
| %caller_call = OpFunctionCall %void %f %caller_arg %caller_arg |
| OpReturnValue %float_0 |
| OpFunctionEnd |
| |
| )" + Main(); |
| |
| auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( |
| kTest, /* do_validation= */ true); |
| EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; |
| } |
| |
| TEST_F(SplitCombinedImageSamplerPassTest, |
| FunctionCall_SampledImageTwoDistinct_Split) { |
| const std::string kTest = Preamble() + NamedITypes() + NamedCombinedTypes() + |
| NamedCaller() + BasicTypes() + ITypes() + |
| CombinedTypes() + R"( |
| |
| ; CHECK: %f_ty = OpTypeFunction %void %i_ty %s_ty %i_ty %s_ty |
| %f_ty = OpTypeFunction %void %si_ty %si_ty |
| %caller_ty = OpTypeFunction %float %si_ty %si_ty |
| |
| ; Call function arg is split. We've checked these details in other tests. |
| ; CHECK: %f = OpFunction %void None %f_ty |
| ; CHECK-NEXT: %[[callee_i_0:\w+]] = OpFunctionParameter %i_ty |
| ; CHECK-NEXT: %[[callee_s_0:\w+]] = OpFunctionParameter %s_ty |
| ; CHECK-NEXT: %[[callee_i_1:\w+]] = OpFunctionParameter %i_ty |
| ; CHECK-NEXT: %[[callee_s_1:\w+]] = OpFunctionParameter %s_ty |
| ; CHECK-NEXT: = OpLabel |
| ; CHECK-NEXT: OpReturn |
| ; CHECK-NEXT: OpFunctionEnd |
| |
| %f = OpFunction %void None %f_ty |
| %100 = OpFunctionParameter %si_ty |
| %101 = OpFunctionParameter %si_ty |
| %110 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| |
| ; CHECK: %caller = OpFunction %float None %caller_ty |
| ; CHECK-NEXT: %[[caller_i_0:\w+]] = OpFunctionParameter %i_ty |
| ; CHECK-NEXT: %[[caller_s_0:\w+]] = OpFunctionParameter %s_ty |
| ; CHECK-NEXT: %[[caller_i_1:\w+]] = OpFunctionParameter %i_ty |
| ; CHECK-NEXT: %[[caller_s_1:\w+]] = OpFunctionParameter %s_ty |
| ; CHECK-NEXT: %caller_entry = OpLabel |
| ; CHECK-NEXT: %caller_call = OpFunctionCall %void %f %[[caller_i_0]] %[[caller_s_0]] %[[caller_i_1]] %[[caller_s_1]] |
| ; CHECK-NEXT: OpReturnValue %float_0 |
| ; CHECK-NEXT: OpFunctionEnd |
| |
| %caller = OpFunction %float None %caller_ty |
| %caller_arg = OpFunctionParameter %si_ty |
| %201 = OpFunctionParameter %si_ty |
| %caller_entry = OpLabel |
| %caller_call = OpFunctionCall %void %f %caller_arg %201 |
| OpReturnValue %float_0 |
| OpFunctionEnd |
| |
| )" + Main(); |
| |
| auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( |
| kTest, /* do_validation= */ true); |
| EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; |
| } |
| |
| TEST_F(SplitCombinedImageSamplerPassTest, |
| FunctionCall_SampledImageAndCopy_Split) { |
| const std::string kTest = Preamble() + NamedITypes() + NamedCombinedTypes() + |
| NamedCaller() + BasicTypes() + ITypes() + |
| CombinedTypes() + R"( |
| |
| ; CHECK: %f_ty = OpTypeFunction %void %i_ty %s_ty %i_ty %s_ty |
| %f_ty = OpTypeFunction %void %si_ty %si_ty |
| ; CHECK: %caller_ty = OpTypeFunction %float %i_ty %s_ty |
| %caller_ty = OpTypeFunction %float %si_ty |
| |
| ; Call function arg is split. We've checked these details in other tests. |
| ; CHECK: %f = OpFunction %void None %f_ty |
| ; CHECK-NEXT: %[[callee_i_0:\w+]] = OpFunctionParameter %i_ty |
| ; CHECK-NEXT: %[[callee_s_0:\w+]] = OpFunctionParameter %s_ty |
| ; CHECK-NEXT: %[[callee_i_1:\w+]] = OpFunctionParameter %i_ty |
| ; CHECK-NEXT: %[[callee_s_1:\w+]] = OpFunctionParameter %s_ty |
| ; CHECK-NEXT: = OpLabel |
| ; CHECK-NEXT: OpReturn |
| ; CHECK-NEXT: OpFunctionEnd |
| |
| %f = OpFunction %void None %f_ty |
| %100 = OpFunctionParameter %si_ty |
| %101 = OpFunctionParameter %si_ty |
| %110 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| |
| ; CHECK: %caller = OpFunction %float None %caller_ty |
| ; CHECK-NEXT: %[[caller_i:\w+]] = OpFunctionParameter %i_ty |
| ; CHECK-NEXT: %[[caller_s:\w+]] = OpFunctionParameter %s_ty |
| ; CHECK-NEXT: %caller_entry = OpLabel |
| ; CHECK-NEXT: %caller_call = OpFunctionCall %void %f %[[caller_i]] %[[caller_s]] %[[caller_i]] %[[caller_s]] |
| ; CHECK-NEXT: OpReturnValue %float_0 |
| ; CHECK-NEXT: OpFunctionEnd |
| |
| %caller = OpFunction %float None %caller_ty |
| %caller_arg = OpFunctionParameter %si_ty |
| %caller_entry = OpLabel |
| %copy = OpCopyObject %si_ty %caller_arg |
| %caller_call = OpFunctionCall %void %f %caller_arg %copy |
| OpReturnValue %float_0 |
| OpFunctionEnd |
| |
| )" + Main(); |
| |
| auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( |
| kTest, /* do_validation= */ true); |
| EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; |
| } |
| |
| TEST_F(SplitCombinedImageSamplerPassTest, |
| FunctionCall_SampledImageSurrounded_Split) { |
| // Test indexing by surrounding the sampled image parameter with other |
| // arguments that should not be touched. |
| const std::string kTest = Preamble() + NamedITypes() + NamedCombinedTypes() + |
| NamedCaller() + BasicTypes() + ITypes() + |
| CombinedTypes() + R"( |
| |
| ; CHECK: %f_ty = OpTypeFunction %void %float %i_ty %s_ty %uint |
| %f_ty = OpTypeFunction %void %float %si_ty %uint |
| ; CHECK: %caller_ty = OpTypeFunction %float %uint %i_ty %s_ty %float |
| %caller_ty = OpTypeFunction %float %uint %si_ty %float |
| |
| ; Call function arg is split. We've checked these details in other tests. |
| ; CHECK: %f = OpFunction %void None %f_ty |
| ; CHECK-NEXT: %[[callee_f:\w+]] = OpFunctionParameter %float |
| ; CHECK-NEXT: %[[callee_i:\w+]] = OpFunctionParameter %i_ty |
| ; CHECK-NEXT: %[[callee_s:\w+]] = OpFunctionParameter %s_ty |
| ; CHECK-NEXT: %[[callee_u:\w+]] = OpFunctionParameter %uint |
| ; CHECK-NEXT: = OpLabel |
| ; CHECK-NEXT: OpReturn |
| ; CHECK-NEXT: OpFunctionEnd |
| |
| %f = OpFunction %void None %f_ty |
| %99 = OpFunctionParameter %float |
| %100 = OpFunctionParameter %si_ty |
| %101 = OpFunctionParameter %uint |
| %110 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| |
| ; CHECK: %caller = OpFunction %float None %caller_ty |
| ; CHECK-NEXT: %[[u_param:\w+]] = OpFunctionParameter %uint |
| ; CHECK-NEXT: %[[caller_i:\w+]] = OpFunctionParameter %i_ty |
| ; CHECK-NEXT: %[[caller_s:\w+]] = OpFunctionParameter %s_ty |
| ; CHECK-NEXT: %[[f_param:\w+]] = OpFunctionParameter %float |
| ; CHECK-NEXT: %caller_entry = OpLabel |
| ; CHECK-NEXT: %caller_call = OpFunctionCall %void %f %[[f_param]] %[[caller_i]] %[[caller_s]] %[[u_param]] |
| ; CHECK-NEXT: OpReturnValue %float_0 |
| ; CHECK-NEXT: OpFunctionEnd |
| |
| %caller = OpFunction %float None %caller_ty |
| %200 = OpFunctionParameter %uint |
| %caller_arg = OpFunctionParameter %si_ty |
| %201 = OpFunctionParameter %float |
| %caller_entry = OpLabel |
| %caller_call = OpFunctionCall %void %f %201 %caller_arg %200 |
| OpReturnValue %float_0 |
| OpFunctionEnd |
| |
| )" + Main(); |
| |
| auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( |
| kTest, /* do_validation= */ true); |
| EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; |
| } |
| |
| TEST_F(SplitCombinedImageSamplerPassTest, FunctionCall_PtrSampledImage_Split) { |
| const std::string kTest = Preamble() + NamedITypes() + NamedCombinedTypes() + |
| NamedCaller() + BasicTypes() + ITypes() + |
| CombinedTypes() + R"( |
| |
| ; CHECK: %f_ty = OpTypeFunction %void %p_i_ty %p_s_ty |
| %f_ty = OpTypeFunction %void %p_si_ty |
| %caller_ty = OpTypeFunction %float %p_si_ty |
| |
| ; Call function arg is split. We've checked these details in other tests. |
| ; CHECK: %f = OpFunction %void None %f_ty |
| ; CHECK-NEXT: %[[callee_i:\w+]] = OpFunctionParameter %p_i_ty |
| ; CHECK-NEXT: %[[callee_s:\w+]] = OpFunctionParameter %p_s_ty |
| ; CHECK-NEXT: = OpLabel |
| ; CHECK-NEXT: OpReturn |
| ; CHECK-NEXT: OpFunctionEnd |
| |
| %f = OpFunction %void None %f_ty |
| %100 = OpFunctionParameter %p_si_ty |
| %110 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| |
| ; CHECK: %caller = OpFunction %float None %caller_ty |
| ; CHECK-NEXT: %[[caller_i:\w+]] = OpFunctionParameter %p_i_ty |
| ; CHECK-NEXT: %[[caller_s:\w+]] = OpFunctionParameter %p_s_ty |
| ; CHECK-NEXT: %caller_entry = OpLabel |
| ; CHECK-NEXT: %caller_call = OpFunctionCall %void %f %[[caller_i]] %[[caller_s]] |
| ; CHECK-NEXT: OpReturnValue %float_0 |
| ; CHECK-NEXT: OpFunctionEnd |
| |
| %caller = OpFunction %float None %caller_ty |
| %caller_arg = OpFunctionParameter %p_si_ty |
| %caller_entry = OpLabel |
| %caller_call = OpFunctionCall %void %f %caller_arg |
| OpReturnValue %float_0 |
| OpFunctionEnd |
| |
| )" + Main(); |
| |
| auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( |
| kTest, /* do_validation= */ true); |
| EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; |
| } |
| |
| TEST_F(SplitCombinedImageSamplerPassTest, |
| FunctionCall_PtrArraySampledImage_Split) { |
| const std::string kTest = Preamble() + NamedITypes() + NamedCombinedTypes() + |
| NamedCaller() + BasicTypes() + ITypes() + |
| CombinedTypes() + R"( |
| |
| ; CHECK: %[[array_i_ty:\w+]] = OpTypeArray %i_ty %uint_3 |
| ; CHECK: %[[p_array_i_ty:\w+]] = OpTypePointer UniformConstant %[[array_i_ty]] |
| ; CHECK: %[[array_s_ty:\w+]] = OpTypeArray %s_ty %uint_3 |
| ; CHECK: %[[p_array_s_ty:\w+]] = OpTypePointer UniformConstant %[[array_s_ty]] |
| |
| ; CHECK: %f_ty = OpTypeFunction %void %[[p_array_i_ty]] %[[p_array_s_ty]] |
| %f_ty = OpTypeFunction %void %p_array_si_ty |
| %caller_ty = OpTypeFunction %float %p_array_si_ty |
| |
| ; Call function arg is split. We've checked these details in other tests. |
| ; CHECK: %f = OpFunction %void None %f_ty |
| ; CHECK-NEXT: %[[callee_i:\w+]] = OpFunctionParameter %[[p_array_i_ty]] |
| ; CHECK-NEXT: %[[callee_s:\w+]] = OpFunctionParameter %[[p_array_s_ty]] |
| ; CHECK-NEXT: = OpLabel |
| ; CHECK-NEXT: OpReturn |
| ; CHECK-NEXT: OpFunctionEnd |
| |
| %f = OpFunction %void None %f_ty |
| %100 = OpFunctionParameter %p_array_si_ty |
| %110 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| |
| ; CHECK: %caller = OpFunction %float None %caller_ty |
| ; CHECK-NEXT: %[[caller_i:\w+]] = OpFunctionParameter %[[p_array_i_ty]] |
| ; CHECK-NEXT: %[[caller_s:\w+]] = OpFunctionParameter %[[p_array_s_ty]] |
| ; CHECK-NEXT: %caller_entry = OpLabel |
| ; CHECK-NEXT: %caller_call = OpFunctionCall %void %f %[[caller_i]] %[[caller_s]] |
| ; CHECK-NEXT: OpReturnValue %float_0 |
| ; CHECK-NEXT: OpFunctionEnd |
| |
| %caller = OpFunction %float None %caller_ty |
| %caller_arg = OpFunctionParameter %p_array_si_ty |
| %caller_entry = OpLabel |
| %caller_call = OpFunctionCall %void %f %caller_arg |
| OpReturnValue %float_0 |
| OpFunctionEnd |
| |
| )" + Main(); |
| |
| auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( |
| kTest, /* do_validation= */ true); |
| EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; |
| } |
| |
| TEST_F(SplitCombinedImageSamplerPassTest, |
| FunctionCall_PtrRtArraySampledImage_Split) { |
| const std::string kTest = Preamble() + NamedITypes() + NamedCombinedTypes() + |
| NamedCaller() + BasicTypes() + ITypes() + |
| CombinedTypes() + R"( |
| |
| ; CHECK: %[[array_i_ty:\w+]] = OpTypeRuntimeArray %i_ty |
| ; CHECK: %[[p_array_i_ty:\w+]] = OpTypePointer UniformConstant %[[array_i_ty]] |
| ; CHECK: %[[array_s_ty:\w+]] = OpTypeRuntimeArray |
| ; CHECK: %[[p_array_s_ty:\w+]] = OpTypePointer UniformConstant %[[array_s_ty]] |
| |
| ; CHECK: %f_ty = OpTypeFunction %void %[[p_array_i_ty]] %[[p_array_s_ty]] |
| %f_ty = OpTypeFunction %void %p_rtarray_si_ty |
| %caller_ty = OpTypeFunction %float %p_rtarray_si_ty |
| |
| ; Call function arg is split. We've checked these details in other tests. |
| ; CHECK: %f = OpFunction %void None %f_ty |
| ; CHECK-NEXT: %[[callee_i:\w+]] = OpFunctionParameter %[[p_array_i_ty]] |
| ; CHECK-NEXT: %[[callee_s:\w+]] = OpFunctionParameter %[[p_array_s_ty]] |
| ; CHECK-NEXT: = OpLabel |
| ; CHECK-NEXT: OpReturn |
| ; CHECK-NEXT: OpFunctionEnd |
| |
| %f = OpFunction %void None %f_ty |
| %100 = OpFunctionParameter %p_rtarray_si_ty |
| %110 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| |
| ; CHECK: %caller = OpFunction %float None %caller_ty |
| ; CHECK-NEXT: %[[caller_i:\w+]] = OpFunctionParameter %[[p_array_i_ty]] |
| ; CHECK-NEXT: %[[caller_s:\w+]] = OpFunctionParameter %[[p_array_s_ty]] |
| ; CHECK-NEXT: %caller_entry = OpLabel |
| ; CHECK-NEXT: %caller_call = OpFunctionCall %void %f %[[caller_i]] %[[caller_s]] |
| ; CHECK-NEXT: OpReturnValue %float_0 |
| ; CHECK-NEXT: OpFunctionEnd |
| |
| %caller = OpFunction %float None %caller_ty |
| %caller_arg = OpFunctionParameter %p_rtarray_si_ty |
| %caller_entry = OpLabel |
| %caller_call = OpFunctionCall %void %f %caller_arg |
| OpReturnValue %float_0 |
| OpFunctionEnd |
| |
| )" + Main(); |
| |
| auto [disasm, status] = SinglePassRunAndMatch<SplitCombinedImageSamplerPass>( |
| kTest, /* do_validation= */ true); |
| EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; |
| } |
| |
| } // namespace |
| } // namespace opt |
| } // namespace spvtools |