blob: 31378b9dbcb30cb1d76311929016ae1e28834623 [file] [log] [blame]
// 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 <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 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>;
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<ResolveBindingConflictsPass>(
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<ResolveBindingConflictsPass>(
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<ResolveBindingConflictsPass>(
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<ResolveBindingConflictsPass>(
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<ResolveBindingConflictsPass>(
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<ResolveBindingConflictsPass>(
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<ResolveBindingConflictsPass>(
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<ResolveBindingConflictsPass>(
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<ResolveBindingConflictsPass>(
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<ResolveBindingConflictsPass>(
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<ResolveBindingConflictsPass>(
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<ResolveBindingConflictsPass>(
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<ResolveBindingConflictsPass>(
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<ResolveBindingConflictsPass>(
kTest, /* do_validation= */ true);
EXPECT_EQ(status, Pass::Status::SuccessWithChange) << disasm;
}
} // namespace
} // namespace opt
} // namespace spvtools