blob: d6ad6bb5f2174270553e42529a73e252cfa95e58 [file]
// 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