// 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 #include #include "spirv-tools/optimizer.hpp" #include "test/opt/pass_fixture.h" #include "test/opt/pass_utils.h" namespace spvtools { namespace opt { namespace { struct ResolveBindingConflictsTest : public PassTest<::testing::Test> { virtual void SetUp() override { SetTargetEnv(SPV_ENV_VULKAN_1_1); // allow storage buffer storage class // without extension. 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); } }; using StringList = std::vector; std::string EntryPointDecls(const StringList& names) { std::ostringstream os; for (auto& name : names) { os << " OpEntryPoint GLCompute %" + name + " \"" + name + "\"\n"; } for (auto& name : names) { os << " OpExecutionMode %" + name + " LocalSize 1 1 1\n"; } for (auto& name : names) { os << " OpName %" + name + " \"" + name + "\"\n"; } return os.str(); } std::string Preamble(const StringList& names = {"main"}) { return R"( OpCapability Shader OpMemoryModel Logical GLSL450 )" + EntryPointDecls(names) + R"( OpName %voidfn "voidfn" OpName %s_ty "s_ty" OpName %i_ty "i_ty" OpName %si_ty "si_ty" OpName %p_s_ty "p_s_ty" OpName %p_i_ty "p_i_ty" OpName %p_si_ty "p_si_ty" OpName %st_ty "st_ty" OpName %pu_st_ty "pu_st_ty" OpName %pb_st_ty "pb_st_ty" )"; } std::string BasicTypes() { return R"( OpDecorate %st_ty Block OpMemberDecorate %st_ty 0 Offset 0 %float = OpTypeFloat 32 %uint = OpTypeInt 32 0 %uint_0 = OpConstant %uint 0 %uint_3 = OpConstant %uint 3 %void = OpTypeVoid %voidfn = OpTypeFunction %void %s_ty = OpTypeSampler %i_ty = OpTypeImage %float 2D 0 0 0 1 Unknown %si_ty = OpTypeSampledImage %i_ty %p_i_ty = OpTypePointer UniformConstant %i_ty %p_s_ty = OpTypePointer UniformConstant %s_ty %p_si_ty = OpTypePointer UniformConstant %si_ty %st_ty = OpTypeStruct %uint %pu_st_ty = OpTypePointer Uniform %st_ty %pb_st_ty = OpTypePointer StorageBuffer %st_ty )"; } std::string NoCheck() { return "; CHECK-NOT: nothing to see"; } TEST_F(ResolveBindingConflictsTest, NoBindings_NoChange) { const std::string kTest = Preamble() + BasicTypes() + R"( %main = OpFunction %void None %voidfn %100 = OpLabel OpReturn OpFunctionEnd )"; auto [disasm, status] = SinglePassRunAndMatch( kTest + NoCheck(), /* do_validation= */ true); EXPECT_EQ(status, Pass::Status::SuccessWithoutChange); EXPECT_EQ(kTest, disasm); } TEST_F(ResolveBindingConflictsTest, NoConflict_UnusedVars_NoChange) { const std::string kTest = Preamble() + R"( OpDecorate %100 DescriptorSet 0 OpDecorate %100 Binding 0 OpDecorate %101 DescriptorSet 0 OpDecorate %101 Binding 0 OpDecorate %102 DescriptorSet 0 OpDecorate %102 Binding 0 OpDecorate %103 DescriptorSet 0 OpDecorate %103 Binding 0 OpDecorate %104 DescriptorSet 0 OpDecorate %104 Binding 0 ; CHECK: OpDecorate %100 DescriptorSet 0 ; CHECK: OpDecorate %100 Binding 0 ; CHECK: OpDecorate %101 DescriptorSet 0 ; CHECK: OpDecorate %101 Binding 0 ; CHECK: OpDecorate %102 DescriptorSet 0 ; CHECK: OpDecorate %102 Binding 0 ; CHECK: OpDecorate %103 DescriptorSet 0 ; CHECK: OpDecorate %103 Binding 0 ; CHECK: OpDecorate %104 DescriptorSet 0 ; CHECK: OpDecorate %104 Binding 0 )" + BasicTypes() + R"( ; Unused variables %100 = OpVariable %p_i_ty UniformConstant ; image %101 = OpVariable %p_s_ty UniformConstant ; sampler %102 = OpVariable %pu_st_ty Uniform ; UBO %103 = OpVariable %pb_st_ty StorageBuffer ; SSBO %104 = OpVariable %p_si_ty UniformConstant ; combined sampled image %main = OpFunction %void None %voidfn %10 = OpLabel OpReturn OpFunctionEnd )"; auto [disasm, status] = SinglePassRunAndMatch( kTest, /* do_validation= */ true); EXPECT_EQ(status, Pass::Status::SuccessWithoutChange) << disasm; } TEST_F(ResolveBindingConflictsTest, NoConflict_UsedVars_NoChange) { const std::string kTest = Preamble() + R"( OpDecorate %100 DescriptorSet 0 OpDecorate %100 Binding 0 OpDecorate %101 DescriptorSet 0 OpDecorate %101 Binding 1 OpDecorate %102 DescriptorSet 0 OpDecorate %102 Binding 2 OpDecorate %103 DescriptorSet 0 OpDecorate %103 Binding 3 OpDecorate %110 DescriptorSet 1 OpDecorate %110 Binding 0 ; CHECK: OpDecorate %100 DescriptorSet 0 ; CHECK: OpDecorate %100 Binding 0 ; CHECK: OpDecorate %101 DescriptorSet 0 ; CHECK: OpDecorate %101 Binding 1 ; CHECK: OpDecorate %102 DescriptorSet 0 ; CHECK: OpDecorate %102 Binding 2 ; CHECK: OpDecorate %103 DescriptorSet 0 ; CHECK: OpDecorate %103 Binding 3 ; CHECK: OpDecorate %110 DescriptorSet 1 ; CHECK: OpDecorate %110 Binding 0 )" + BasicTypes() + R"( %100 = OpVariable %p_i_ty UniformConstant ; image %101 = OpVariable %p_s_ty UniformConstant ; sampler %102 = OpVariable %pu_st_ty Uniform ; UBO %103 = OpVariable %pb_st_ty StorageBuffer ; SSBO %110 = OpVariable %p_si_ty UniformConstant ; combined sampled image %main = OpFunction %void None %voidfn %10 = OpLabel %11 = OpCopyObject %p_i_ty %100 %12 = OpCopyObject %p_s_ty %101 %13 = OpCopyObject %pu_st_ty %102 %14 = OpCopyObject %pb_st_ty %103 %15 = OpCopyObject %p_si_ty %110 OpReturn OpFunctionEnd )"; auto [disasm, status] = SinglePassRunAndMatch( kTest, /* do_validation= */ true); EXPECT_EQ(status, Pass::Status::SuccessWithoutChange) << disasm; } TEST_F(ResolveBindingConflictsTest, OneEntryPoint_SamplerFirstConflict_Resolves) { const std::string kTest = Preamble() + R"( OpDecorate %100 DescriptorSet 0 OpDecorate %100 Binding 0 OpDecorate %101 DescriptorSet 0 OpDecorate %101 Binding 0 ; The sampler's binding number is incremented, even when listed first. ; CHECK: OpDecorate %100 DescriptorSet 0 ; CHECK: OpDecorate %100 Binding 1 ; CHECK: OpDecorate %101 DescriptorSet 0 ; CHECK: OpDecorate %101 Binding 0 )" + BasicTypes() + R"( %100 = OpVariable %p_s_ty UniformConstant ; sampler listed first %101 = OpVariable %p_i_ty UniformConstant %main = OpFunction %void None %voidfn %10 = OpLabel %11 = OpCopyObject %p_s_ty %100 %12 = OpCopyObject %p_i_ty %101 OpReturn OpFunctionEnd )"; auto [disasm, status] = SinglePassRunAndMatch( kTest, /* do_validation= */ true); EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; } TEST_F(ResolveBindingConflictsTest, OneEntryPoint_SamplerSecondConflict_Resolves) { const std::string kTest = Preamble() + R"( OpDecorate %100 DescriptorSet 0 OpDecorate %100 Binding 0 OpDecorate %101 DescriptorSet 0 OpDecorate %101 Binding 0 ; The sampler's binding number is incremented, even when listed second. ; CHECK: OpDecorate %100 DescriptorSet 0 ; CHECK: OpDecorate %100 Binding 0 ; CHECK: OpDecorate %101 DescriptorSet 0 ; CHECK: OpDecorate %101 Binding 1 )" + BasicTypes() + R"( %100 = OpVariable %p_i_ty UniformConstant %101 = OpVariable %p_s_ty UniformConstant ; sampler listed second %main = OpFunction %void None %voidfn %10 = OpLabel %11 = OpCopyObject %p_i_ty %100 %12 = OpCopyObject %p_s_ty %101 OpReturn OpFunctionEnd )"; auto [disasm, status] = SinglePassRunAndMatch( kTest, /* do_validation= */ true); EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; } TEST_F(ResolveBindingConflictsTest, OneEntryPoint_Conflict_Ripples) { const std::string kTest = Preamble() + R"( OpDecorate %100 DescriptorSet 0 OpDecorate %100 Binding 0 OpDecorate %101 DescriptorSet 0 OpDecorate %101 Binding 0 OpDecorate %102 DescriptorSet 0 OpDecorate %102 Binding 1 OpDecorate %103 DescriptorSet 0 OpDecorate %103 Binding 2 OpDecorate %104 DescriptorSet 0 OpDecorate %104 Binding 3 ; The sampler's binding number is incremented, and later ; bindings move out of the way. ; CHECK: OpDecorate %100 DescriptorSet 0 ; CHECK: OpDecorate %100 Binding 1 ; CHECK: OpDecorate %101 DescriptorSet 0 ; CHECK: OpDecorate %101 Binding 0 ; CHECK: OpDecorate %102 DescriptorSet 0 ; CHECK: OpDecorate %102 Binding 2 ; CHECK: OpDecorate %103 DescriptorSet 0 ; CHECK: OpDecorate %103 Binding 3 ; CHECK: OpDecorate %104 DescriptorSet 0 ; CHECK: OpDecorate %104 Binding 4 )" + BasicTypes() + R"( %100 = OpVariable %p_s_ty UniformConstant ; sampler comes first %101 = OpVariable %p_i_ty UniformConstant %102 = OpVariable %pu_st_ty Uniform %103 = OpVariable %pb_st_ty StorageBuffer %104 = OpVariable %p_si_ty UniformConstant %main = OpFunction %void None %voidfn %10 = OpLabel %11 = OpCopyObject %p_s_ty %100 %12 = OpCopyObject %p_i_ty %101 %13 = OpCopyObject %pu_st_ty %102 %14 = OpCopyObject %pb_st_ty %103 %15 = OpCopyObject %p_si_ty %104 OpReturn OpFunctionEnd )"; auto [disasm, status] = SinglePassRunAndMatch( kTest, /* do_validation= */ true); EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; } TEST_F(ResolveBindingConflictsTest, OneEntryPoint_Conflict_RippleStopsAtFirstHole) { const std::string kTest = Preamble() + R"( OpDecorate %100 DescriptorSet 0 OpDecorate %100 Binding 0 OpDecorate %101 DescriptorSet 0 OpDecorate %101 Binding 0 OpDecorate %102 DescriptorSet 0 OpDecorate %102 Binding 1 ; Leave a hole at (0, 2) OpDecorate %103 DescriptorSet 0 OpDecorate %103 Binding 3 OpDecorate %104 DescriptorSet 0 OpDecorate %104 Binding 4 ; There was a hole at binding 2. The ripple stops there. ; CHECK: OpDecorate %100 DescriptorSet 0 ; CHECK: OpDecorate %100 Binding 1 ; CHECK: OpDecorate %101 DescriptorSet 0 ; CHECK: OpDecorate %101 Binding 0 ; CHECK: OpDecorate %102 DescriptorSet 0 ; CHECK: OpDecorate %102 Binding 2 ; CHECK: OpDecorate %103 DescriptorSet 0 ; CHECK: OpDecorate %103 Binding 3 ; CHECK: OpDecorate %104 DescriptorSet 0 ; CHECK: OpDecorate %104 Binding 4 )" + BasicTypes() + R"( %100 = OpVariable %p_s_ty UniformConstant ; sampler comes first %101 = OpVariable %p_i_ty UniformConstant %102 = OpVariable %pu_st_ty Uniform %103 = OpVariable %pb_st_ty StorageBuffer %104 = OpVariable %p_si_ty UniformConstant %main = OpFunction %void None %voidfn %10 = OpLabel %11 = OpCopyObject %p_s_ty %100 %12 = OpCopyObject %p_i_ty %101 %13 = OpCopyObject %pu_st_ty %102 %14 = OpCopyObject %pb_st_ty %103 %15 = OpCopyObject %p_si_ty %104 OpReturn OpFunctionEnd )"; auto [disasm, status] = SinglePassRunAndMatch( kTest, /* do_validation= */ true); EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; } TEST_F(ResolveBindingConflictsTest, OneEntryPoint_MultiConflict_Resolves) { const std::string kTest = Preamble() + R"( ; Two conflicts: at Bindings 0, and 1 OpDecorate %100 DescriptorSet 0 OpDecorate %100 Binding 0 OpDecorate %101 DescriptorSet 0 OpDecorate %101 Binding 0 OpDecorate %102 DescriptorSet 0 OpDecorate %102 Binding 1 OpDecorate %103 DescriptorSet 0 OpDecorate %103 Binding 1 OpDecorate %104 DescriptorSet 0 OpDecorate %104 Binding 2 ; CHECK: OpDecorate %100 DescriptorSet 0 ; CHECK: OpDecorate %100 Binding 1 ; CHECK: OpDecorate %101 DescriptorSet 0 ; CHECK: OpDecorate %101 Binding 0 ; CHECK: OpDecorate %102 DescriptorSet 0 ; CHECK: OpDecorate %102 Binding 2 ; CHECK: OpDecorate %103 DescriptorSet 0 ; CHECK: OpDecorate %103 Binding 3 ; CHECK: OpDecorate %104 DescriptorSet 0 ; CHECK: OpDecorate %104 Binding 4 )" + BasicTypes() + R"( %100 = OpVariable %p_s_ty UniformConstant ; sampler first %101 = OpVariable %p_i_ty UniformConstant %102 = OpVariable %p_i_ty UniformConstant %103 = OpVariable %p_s_ty UniformConstant ; sampler second %104 = OpVariable %pu_st_ty Uniform %main = OpFunction %void None %voidfn %10 = OpLabel %11 = OpCopyObject %p_s_ty %100 %12 = OpCopyObject %p_i_ty %101 %13 = OpCopyObject %p_i_ty %102 %14 = OpCopyObject %p_s_ty %103 %15 = OpCopyObject %pu_st_ty %104 OpReturn OpFunctionEnd )"; auto [disasm, status] = SinglePassRunAndMatch( kTest, /* do_validation= */ true); EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; } TEST_F(ResolveBindingConflictsTest, OneEntryPoint_MultiConflict_ComplexCallGraph_Resolves) { // Check that uses are seen even when used at various points in a complex call // graph. const std::string kTest = Preamble() + R"( OpDecorate %100 DescriptorSet 0 OpDecorate %100 Binding 0 OpDecorate %101 DescriptorSet 0 OpDecorate %101 Binding 0 OpDecorate %102 DescriptorSet 0 OpDecorate %102 Binding 1 OpDecorate %103 DescriptorSet 0 OpDecorate %103 Binding 1 OpDecorate %104 DescriptorSet 0 OpDecorate %104 Binding 2 ; CHECK: OpDecorate %100 DescriptorSet 0 ; CHECK: OpDecorate %100 Binding 1 ; CHECK: OpDecorate %101 DescriptorSet 0 ; CHECK: OpDecorate %101 Binding 0 ; CHECK: OpDecorate %102 DescriptorSet 0 ; CHECK: OpDecorate %102 Binding 2 ; CHECK: OpDecorate %103 DescriptorSet 0 ; CHECK: OpDecorate %103 Binding 3 ; CHECK: OpDecorate %104 DescriptorSet 0 ; CHECK: OpDecorate %104 Binding 4 )" + BasicTypes() + R"( %100 = OpVariable %p_s_ty UniformConstant ; used in %200 %101 = OpVariable %p_i_ty UniformConstant ; used in %300, %400 %102 = OpVariable %p_i_ty UniformConstant ; used in %500 %103 = OpVariable %p_s_ty UniformConstant ; used in %400 twice %104 = OpVariable %pu_st_ty Uniform ; used in %600 %200 = OpFunction %void None %voidfn %201 = OpLabel %202 = OpCopyObject %p_s_ty %100 OpReturn OpFunctionEnd %300 = OpFunction %void None %voidfn %301 = OpLabel %302 = OpCopyObject %p_i_ty %101 OpReturn OpFunctionEnd %400 = OpFunction %void None %voidfn %401 = OpLabel %402 = OpFunctionCall %void %200 %403 = OpCopyObject %p_s_ty %103 %404 = OpCopyObject %p_i_ty %101 %405 = OpCopyObject %p_s_ty %103 %406 = OpFunctionCall %void %300 OpReturn OpFunctionEnd %500 = OpFunction %void None %voidfn %501 = OpLabel %502 = OpFunctionCall %void %400 %503 = OpCopyObject %p_i_ty %102 %504 = OpFunctionCall %void %300 OpReturn OpFunctionEnd %600 = OpFunction %void None %voidfn %601 = OpLabel %602 = OpFunctionCall %void %300 %603 = OpFunctionCall %void %500 %604 = OpCopyObject %pu_st_ty %104 OpReturn OpFunctionEnd %main = OpFunction %void None %voidfn %1000 = OpLabel %1001 = OpFunctionCall %void %600 OpReturn OpFunctionEnd )"; auto [disasm, status] = SinglePassRunAndMatch( kTest, /* do_validation= */ true); EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; } TEST_F(ResolveBindingConflictsTest, MultiEntryPoint_DuplicatConflicts_ResolvesOnlyOnce) { // Before: // // Binding: 0 1 // Alpha: %100,%101 // Beta: %100,%101 // // After: // // Binding: 0 1 // Alpha: %101 %100 // Beta: %101 %100 const std::string kTest = Preamble({"alpha", "beta"}) + R"( OpDecorate %100 DescriptorSet 0 OpDecorate %100 Binding 0 OpDecorate %101 DescriptorSet 0 OpDecorate %101 Binding 0 ; CHECK: OpDecorate %100 DescriptorSet 0 ; CHECK: OpDecorate %100 Binding 1 ; CHECK: OpDecorate %101 DescriptorSet 0 ; CHECK: OpDecorate %101 Binding 0 )" + BasicTypes() + R"( %100 = OpVariable %p_s_ty UniformConstant %101 = OpVariable %p_i_ty UniformConstant %alpha = OpFunction %void None %voidfn %1000 = OpLabel %1001 = OpCopyObject %p_s_ty %100 %1002 = OpCopyObject %p_i_ty %101 OpReturn OpFunctionEnd %beta = OpFunction %void None %voidfn %2000 = OpLabel %2001 = OpCopyObject %p_s_ty %100 %2002 = OpCopyObject %p_i_ty %101 OpReturn OpFunctionEnd )"; auto [disasm, status] = SinglePassRunAndMatch( kTest, /* do_validation= */ true); EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; } TEST_F(ResolveBindingConflictsTest, MultiEntryPoint_IndependentConflicts_Resolves) { // Before: // // Binding: 0 1 // Alpha: %100,%101 // Beta: %102,%103 // // After: // // Binding: 0 1 // Alpha: %101 %100 // Beta: %102 %103 const std::string kTest = Preamble({"alpha", "beta"}) + R"( OpDecorate %100 DescriptorSet 0 OpDecorate %100 Binding 0 OpDecorate %101 DescriptorSet 0 OpDecorate %101 Binding 0 OpDecorate %102 DescriptorSet 0 OpDecorate %102 Binding 0 OpDecorate %103 DescriptorSet 0 OpDecorate %103 Binding 0 ; CHECK: OpDecorate %100 DescriptorSet 0 ; CHECK: OpDecorate %100 Binding 1 ; CHECK: OpDecorate %101 DescriptorSet 0 ; CHECK: OpDecorate %101 Binding 0 ; CHECK: OpDecorate %102 DescriptorSet 0 ; CHECK: OpDecorate %102 Binding 0 ; CHECK: OpDecorate %103 DescriptorSet 0 ; CHECK: OpDecorate %103 Binding 1 )" + BasicTypes() + R"( %100 = OpVariable %p_s_ty UniformConstant %101 = OpVariable %p_i_ty UniformConstant %102 = OpVariable %p_i_ty UniformConstant %103 = OpVariable %p_s_ty UniformConstant %alpha = OpFunction %void None %voidfn %1000 = OpLabel %1001 = OpCopyObject %p_s_ty %100 %1002 = OpCopyObject %p_i_ty %101 OpReturn OpFunctionEnd %beta = OpFunction %void None %voidfn %2000 = OpLabel %2001 = OpCopyObject %p_i_ty %102 %2002 = OpCopyObject %p_s_ty %103 OpReturn OpFunctionEnd )"; auto [disasm, status] = SinglePassRunAndMatch( kTest, /* do_validation= */ true); EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; } TEST_F(ResolveBindingConflictsTest, MultiEntryPoint_SameVarConflictsAcrossMultiEntryPoints_Resolves) { // A sampler variable is bumped, causing potential conflicts in other shaders. // // Before: // // Binding: 0 1 2 // Alpha: %100,%101 // Beta: %100 %102 // Gamma: %100 %103 // // After: // // Binding: 0 1 2 // Alpha: %101 %100 // Beta: %100 %102 // Gamma: %100 %103 // const std::string kTest = Preamble({"alpha", "beta", "gamma"}) + R"( OpDecorate %100 DescriptorSet 0 ; The sampler OpDecorate %100 Binding 0 OpDecorate %101 DescriptorSet 0 OpDecorate %101 Binding 0 OpDecorate %102 DescriptorSet 0 OpDecorate %102 Binding 1 OpDecorate %103 DescriptorSet 0 OpDecorate %103 Binding 2 ; bumped once ; CHECK: OpDecorate %100 DescriptorSet 0 ; CHECK: OpDecorate %100 Binding 1 ; CHECK: OpDecorate %101 DescriptorSet 0 ; CHECK: OpDecorate %101 Binding 0 ; pushed back from bump of %100 ; CHECK: OpDecorate %102 DescriptorSet 0 ; CHECK: OpDecorate %102 Binding 2 ; does not need to be bumped ; CHECK: OpDecorate %103 DescriptorSet 0 ; CHECK: OpDecorate %103 Binding 2 )" + BasicTypes() + R"( %100 = OpVariable %p_s_ty UniformConstant ; used in alpha, beta, gamma %101 = OpVariable %p_i_ty UniformConstant ; used in alpha %102 = OpVariable %pu_st_ty Uniform ; used in beta %103 = OpVariable %pb_st_ty StorageBuffer ; used in gamma %alpha = OpFunction %void None %voidfn %1000 = OpLabel %1001 = OpCopyObject %p_s_ty %100 %1002 = OpCopyObject %p_i_ty %101 OpReturn OpFunctionEnd %beta = OpFunction %void None %voidfn %2000 = OpLabel %2001 = OpCopyObject %p_s_ty %100 %2002 = OpCopyObject %pu_st_ty %102 OpReturn OpFunctionEnd %gamma = OpFunction %void None %voidfn %3000 = OpLabel %3001 = OpCopyObject %p_s_ty %100 %3002 = OpCopyObject %pb_st_ty %103 OpReturn OpFunctionEnd )"; auto [disasm, status] = SinglePassRunAndMatch( kTest, /* do_validation= */ true); EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; } TEST_F(ResolveBindingConflictsTest, MultiEntryPoint_ConflictCascade_Resolves) { // Before: // // Binding: 0 1 2 3 // Alpha: %100,%101 // Beta: %100 %102 // Gamma: %102 %103 // Delta: %103 %104 // // After: // // Binding: 0 1 2 3 4 // Alpha: %101 %100 // Beta: %100 %102 // Gamma: %102 %103 // Delta: %103 %104 // const std::string kTest = Preamble({"alpha", "beta", "gamma", "delta"}) + R"( OpDecorate %100 DescriptorSet 0 ; The sampler OpDecorate %100 Binding 0 OpDecorate %101 DescriptorSet 0 OpDecorate %101 Binding 0 OpDecorate %102 DescriptorSet 0 OpDecorate %102 Binding 1 OpDecorate %103 DescriptorSet 0 OpDecorate %103 Binding 2 OpDecorate %104 DescriptorSet 0 OpDecorate %104 Binding 3 ; %100 is bumped once: ; CHECK: OpDecorate %100 DescriptorSet 0 ; CHECK: OpDecorate %100 Binding 1 ; CHECK: OpDecorate %101 DescriptorSet 0 ; CHECK: OpDecorate %101 Binding 0 ; pushed back from bump of %100 ; CHECK: OpDecorate %102 DescriptorSet 0 ; CHECK: OpDecorate %102 Binding 2 ; pushed back from bump of %102 ; CHECK: OpDecorate %103 DescriptorSet 0 ; CHECK: OpDecorate %103 Binding 3 ; pushed back from bump of %103 ; CHECK: OpDecorate %104 DescriptorSet 0 ; CHECK: OpDecorate %104 Binding 4 )" + BasicTypes() + R"( %100 = OpVariable %p_s_ty UniformConstant ; used in alpha, beta %101 = OpVariable %p_i_ty UniformConstant ; used in alpha %102 = OpVariable %pu_st_ty Uniform ; used in beta, gamma %103 = OpVariable %pb_st_ty StorageBuffer ; used in gamma, delta %104 = OpVariable %p_si_ty UniformConstant ; used delta %alpha = OpFunction %void None %voidfn %1000 = OpLabel %1001 = OpCopyObject %p_s_ty %100 %1002 = OpCopyObject %p_i_ty %101 OpReturn OpFunctionEnd %beta = OpFunction %void None %voidfn %2000 = OpLabel %2001 = OpCopyObject %p_s_ty %100 %2002 = OpCopyObject %pu_st_ty %102 OpReturn OpFunctionEnd %gamma = OpFunction %void None %voidfn %3000 = OpLabel %3001 = OpCopyObject %pu_st_ty %102 %3002 = OpCopyObject %pb_st_ty %103 OpReturn OpFunctionEnd %delta = OpFunction %void None %voidfn %4000 = OpLabel %4001 = OpCopyObject %pb_st_ty %103 %4002 = OpCopyObject %p_si_ty %104 OpReturn OpFunctionEnd )"; auto [disasm, status] = SinglePassRunAndMatch( kTest, /* do_validation= */ true); EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; } TEST_F(ResolveBindingConflictsTest, MultiEntryPoint_ConflictCascade_RevisitEntryPoint) { // Prove that the settling algorithm knows to revisit entry points that // already had all their own conflicts resolved. // // Before: // // Binding: 0 1 2 3 4 // Alpha: %100,%101 %103 %104 %105 // Beta: %101 %102 %103 %105 // // After: // // Binding: 0 1 2 3 4 5 // Alpha: %100 %101 %103 %104 %105 // Beta: %101 %102 %103 %105 const std::string kTest = Preamble({"alpha", "beta"}) + R"( OpDecorate %100 DescriptorSet 0 OpDecorate %100 Binding 0 OpDecorate %101 DescriptorSet 0 ; the sampler OpDecorate %101 Binding 0 OpDecorate %102 DescriptorSet 0 OpDecorate %102 Binding 1 OpDecorate %103 DescriptorSet 0 OpDecorate %103 Binding 2 OpDecorate %104 DescriptorSet 0 OpDecorate %104 Binding 3 OpDecorate %105 DescriptorSet 0 OpDecorate %105 Binding 4 ; CHECK: OpDecorate %100 DescriptorSet 0 ; CHECK: OpDecorate %100 Binding 0 ; CHECK: OpDecorate %101 DescriptorSet 0 ; CHECK: OpDecorate %101 Binding 1 ; CHECK: OpDecorate %102 DescriptorSet 0 ; CHECK: OpDecorate %102 Binding 2 ; CHECK: OpDecorate %103 DescriptorSet 0 ; CHECK: OpDecorate %103 Binding 3 ; CHECK: OpDecorate %104 DescriptorSet 0 ; CHECK: OpDecorate %104 Binding 4 ; CHECK: OpDecorate %105 DescriptorSet 0 ; CHECK: OpDecorate %105 Binding 5 )" + BasicTypes() + R"( %100 = OpVariable %p_i_ty UniformConstant %101 = OpVariable %p_s_ty UniformConstant %102 = OpVariable %pu_st_ty Uniform %103 = OpVariable %pb_st_ty StorageBuffer %104 = OpVariable %p_si_ty UniformConstant %105 = OpVariable %p_s_ty UniformConstant %alpha = OpFunction %void None %voidfn %1000 = OpLabel %1001 = OpCopyObject %p_i_ty %100 %1002 = OpCopyObject %p_s_ty %101 %1003 = OpCopyObject %pb_st_ty %103 %1004 = OpCopyObject %p_si_ty %104 %1005 = OpCopyObject %p_s_ty %105 OpReturn OpFunctionEnd %beta = OpFunction %void None %voidfn %2000 = OpLabel %2001 = OpCopyObject %p_s_ty %101 %2002 = OpCopyObject %pu_st_ty %102 %2003 = OpCopyObject %pb_st_ty %103 %2004 = OpCopyObject %p_s_ty %105 OpReturn OpFunctionEnd )"; auto [disasm, status] = SinglePassRunAndMatch( kTest, /* do_validation= */ true); EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm; } } // namespace } // namespace opt } // namespace spvtools