mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-12-27 10:20:14 +00:00
0741f42738
If the body of the module does not have any ids change, compact ids will not change the id bound. This can cause problems because the id bound could be much higher than the largest id in that is used. It should be reset any time it is not the larger id used + 1. Fixes #4604
351 lines
10 KiB
C++
351 lines
10 KiB
C++
// Copyright (c) 2017 Google Inc.
|
|
//
|
|
// 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 <memory>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "gmock/gmock.h"
|
|
#include "spirv-tools/libspirv.hpp"
|
|
#include "spirv-tools/optimizer.hpp"
|
|
#include "test/opt/pass_fixture.h"
|
|
#include "test/opt/pass_utils.h"
|
|
|
|
namespace spvtools {
|
|
namespace opt {
|
|
namespace {
|
|
|
|
using CompactIdsTest = PassTest<::testing::Test>;
|
|
|
|
TEST_F(CompactIdsTest, PassOff) {
|
|
const std::string before =
|
|
R"(OpCapability Addresses
|
|
OpCapability Kernel
|
|
OpCapability GenericPointer
|
|
OpCapability Linkage
|
|
OpMemoryModel Physical32 OpenCL
|
|
%99 = OpTypeInt 32 0
|
|
%10 = OpTypeVector %99 2
|
|
%20 = OpConstant %99 2
|
|
%30 = OpTypeArray %99 %20
|
|
)";
|
|
|
|
const std::string after = before;
|
|
|
|
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
|
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
|
|
SinglePassRunAndCheck<NullPass>(before, after, false, false);
|
|
}
|
|
|
|
TEST_F(CompactIdsTest, PassOn) {
|
|
const std::string before =
|
|
R"(OpCapability Addresses
|
|
OpCapability Kernel
|
|
OpCapability GenericPointer
|
|
OpCapability Linkage
|
|
OpMemoryModel Physical32 OpenCL
|
|
OpEntryPoint Kernel %3 "simple_kernel"
|
|
%99 = OpTypeInt 32 0
|
|
%10 = OpTypeVector %99 2
|
|
%20 = OpConstant %99 2
|
|
%30 = OpTypeArray %99 %20
|
|
%40 = OpTypeVoid
|
|
%50 = OpTypeFunction %40
|
|
%3 = OpFunction %40 None %50
|
|
%70 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
const std::string after =
|
|
R"(OpCapability Addresses
|
|
OpCapability Kernel
|
|
OpCapability GenericPointer
|
|
OpCapability Linkage
|
|
OpMemoryModel Physical32 OpenCL
|
|
OpEntryPoint Kernel %1 "simple_kernel"
|
|
%2 = OpTypeInt 32 0
|
|
%3 = OpTypeVector %2 2
|
|
%4 = OpConstant %2 2
|
|
%5 = OpTypeArray %2 %4
|
|
%6 = OpTypeVoid
|
|
%7 = OpTypeFunction %6
|
|
%1 = OpFunction %6 None %7
|
|
%8 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
|
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
|
|
SinglePassRunAndCheck<CompactIdsPass>(before, after, false, false);
|
|
}
|
|
|
|
TEST_F(CompactIdsTest, DebugScope) {
|
|
const std::string text =
|
|
R"(OpCapability Addresses
|
|
OpCapability Kernel
|
|
OpCapability GenericPointer
|
|
OpCapability Linkage
|
|
%5 = OpExtInstImport "OpenCL.DebugInfo.100"
|
|
OpMemoryModel Physical32 OpenCL
|
|
OpEntryPoint Kernel %3 "simple_kernel"
|
|
%2 = OpString "test"
|
|
%99 = OpTypeInt 32 0
|
|
%10 = OpTypeVector %99 2
|
|
%20 = OpConstant %99 2
|
|
%30 = OpTypeArray %99 %20
|
|
%40 = OpTypeVoid
|
|
%50 = OpTypeFunction %40
|
|
%11 = OpExtInst %40 %5 DebugSource %2
|
|
%12 = OpExtInst %40 %5 DebugCompilationUnit 1 4 %11 HLSL
|
|
%13 = OpExtInst %40 %5 DebugTypeFunction FlagIsProtected|FlagIsPrivate %40
|
|
|
|
; CHECK: [[fn:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugFunction
|
|
%14 = OpExtInst %40 %5 DebugFunction %2 %13 %11 0 0 %12 %2 FlagIsProtected|FlagIsPrivate 0 %3
|
|
%3 = OpFunction %40 None %50
|
|
%70 = OpLabel
|
|
|
|
; CHECK: DebugScope [[fn]]
|
|
%19 = OpExtInst %40 %5 DebugScope %14
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
|
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
|
|
SinglePassRunAndMatch<CompactIdsPass>(text, true);
|
|
}
|
|
|
|
TEST(CompactIds, InstructionResultIsUpdated) {
|
|
// For https://github.com/KhronosGroup/SPIRV-Tools/issues/827
|
|
// In that bug, the compact Ids pass was directly updating the result Id
|
|
// word for an OpFunction instruction, but not updating the cached
|
|
// result_id_ in that Instruction object.
|
|
//
|
|
// This test is a bit cheesy. We don't expose internal interfaces enough
|
|
// to see the inconsistency. So reproduce the original scenario, with
|
|
// compact ids followed by a pass that trips up on the inconsistency.
|
|
|
|
const std::string input(R"(OpCapability Shader
|
|
OpMemoryModel Logical Simple
|
|
OpEntryPoint GLCompute %100 "main"
|
|
%200 = OpTypeVoid
|
|
%300 = OpTypeFunction %200
|
|
%100 = OpFunction %200 None %300
|
|
%400 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)");
|
|
|
|
std::vector<uint32_t> binary;
|
|
const spv_target_env env = SPV_ENV_UNIVERSAL_1_0;
|
|
spvtools::SpirvTools tools(env);
|
|
auto assembled = tools.Assemble(
|
|
input, &binary, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
|
EXPECT_TRUE(assembled);
|
|
|
|
spvtools::Optimizer optimizer(env);
|
|
optimizer.RegisterPass(CreateCompactIdsPass());
|
|
// The exhaustive inliner will use the result_id
|
|
optimizer.RegisterPass(CreateInlineExhaustivePass());
|
|
|
|
// This should not crash!
|
|
optimizer.Run(binary.data(), binary.size(), &binary);
|
|
|
|
std::string disassembly;
|
|
tools.Disassemble(binary, &disassembly, SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
|
|
|
|
const std::string expected(R"(OpCapability Shader
|
|
OpMemoryModel Logical Simple
|
|
OpEntryPoint GLCompute %1 "main"
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%1 = OpFunction %2 None %3
|
|
%4 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)");
|
|
|
|
EXPECT_THAT(disassembly, ::testing::Eq(expected));
|
|
}
|
|
|
|
TEST(CompactIds, HeaderIsUpdated) {
|
|
const std::string input(R"(OpCapability Shader
|
|
OpMemoryModel Logical Simple
|
|
OpEntryPoint GLCompute %100 "main"
|
|
%200 = OpTypeVoid
|
|
%300 = OpTypeFunction %200
|
|
%100 = OpFunction %200 None %300
|
|
%400 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)");
|
|
|
|
std::vector<uint32_t> binary;
|
|
const spv_target_env env = SPV_ENV_UNIVERSAL_1_0;
|
|
spvtools::SpirvTools tools(env);
|
|
auto assembled = tools.Assemble(
|
|
input, &binary, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
|
EXPECT_TRUE(assembled);
|
|
|
|
spvtools::Optimizer optimizer(env);
|
|
optimizer.RegisterPass(CreateCompactIdsPass());
|
|
// The exhaustive inliner will use the result_id
|
|
optimizer.RegisterPass(CreateInlineExhaustivePass());
|
|
|
|
// This should not crash!
|
|
optimizer.Run(binary.data(), binary.size(), &binary);
|
|
|
|
std::string disassembly;
|
|
tools.Disassemble(binary, &disassembly, SPV_BINARY_TO_TEXT_OPTION_NONE);
|
|
|
|
const std::string expected(R"(; SPIR-V
|
|
; Version: 1.0
|
|
; Generator: Khronos SPIR-V Tools Assembler; 0
|
|
; Bound: 5
|
|
; Schema: 0
|
|
OpCapability Shader
|
|
OpMemoryModel Logical Simple
|
|
OpEntryPoint GLCompute %1 "main"
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%1 = OpFunction %2 None %3
|
|
%4 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)");
|
|
|
|
EXPECT_THAT(disassembly, ::testing::Eq(expected));
|
|
}
|
|
|
|
// Test context consistency check after invalidating
|
|
// CFG and others by compact IDs Pass.
|
|
// Uses a GLSL shader with named labels for variety
|
|
TEST(CompactIds, ConsistentCheck) {
|
|
const std::string input(R"(OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main" %in_var_A %out_var_SV_TARGET
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource HLSL 600
|
|
OpName %main "main"
|
|
OpName %in_var_A "in.var.A"
|
|
OpName %out_var_SV_TARGET "out.var.SV_TARGET"
|
|
OpDecorate %in_var_A Location 0
|
|
OpDecorate %out_var_SV_TARGET Location 0
|
|
%void = OpTypeVoid
|
|
%3 = OpTypeFunction %void
|
|
%float = OpTypeFloat 32
|
|
%v4float = OpTypeVector %float 4
|
|
%_ptr_Input_v4float = OpTypePointer Input %v4float
|
|
%_ptr_Output_v4float = OpTypePointer Output %v4float
|
|
%in_var_A = OpVariable %_ptr_Input_v4float Input
|
|
%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output
|
|
%main = OpFunction %void None %3
|
|
%5 = OpLabel
|
|
%12 = OpLoad %v4float %in_var_A
|
|
%23 = OpVectorShuffle %v4float %12 %12 0 0 0 1
|
|
OpStore %out_var_SV_TARGET %23
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)");
|
|
|
|
spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_1);
|
|
std::unique_ptr<IRContext> context =
|
|
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, input,
|
|
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
|
ASSERT_NE(context, nullptr);
|
|
|
|
CompactIdsPass compact_id_pass;
|
|
context->BuildInvalidAnalyses(compact_id_pass.GetPreservedAnalyses());
|
|
const auto status = compact_id_pass.Run(context.get());
|
|
ASSERT_NE(status, Pass::Status::Failure);
|
|
EXPECT_TRUE(context->IsConsistent());
|
|
|
|
// Test output just in case
|
|
std::vector<uint32_t> binary;
|
|
context->module()->ToBinary(&binary, false);
|
|
std::string disassembly;
|
|
tools.Disassemble(binary, &disassembly,
|
|
SpirvTools::kDefaultDisassembleOption);
|
|
|
|
const std::string expected(R"(OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main" %in_var_A %out_var_SV_TARGET
|
|
OpExecutionMode %main OriginUpperLeft
|
|
OpSource HLSL 600
|
|
OpName %main "main"
|
|
OpName %in_var_A "in.var.A"
|
|
OpName %out_var_SV_TARGET "out.var.SV_TARGET"
|
|
OpDecorate %in_var_A Location 0
|
|
OpDecorate %out_var_SV_TARGET Location 0
|
|
%void = OpTypeVoid
|
|
%5 = OpTypeFunction %void
|
|
%float = OpTypeFloat 32
|
|
%v4float = OpTypeVector %float 4
|
|
%_ptr_Input_v4float = OpTypePointer Input %v4float
|
|
%_ptr_Output_v4float = OpTypePointer Output %v4float
|
|
%in_var_A = OpVariable %_ptr_Input_v4float Input
|
|
%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output
|
|
%main = OpFunction %void None %5
|
|
%10 = OpLabel
|
|
%11 = OpLoad %v4float %in_var_A
|
|
%12 = OpVectorShuffle %v4float %11 %11 0 0 0 1
|
|
OpStore %out_var_SV_TARGET %12
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)");
|
|
|
|
EXPECT_THAT(disassembly, ::testing::Eq(expected));
|
|
}
|
|
|
|
TEST(CompactIds, ResetIdBound) {
|
|
const std::string input(R"(OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %1 "main"
|
|
OpExecutionMode %1 OriginUpperLeft
|
|
%void = OpTypeVoid
|
|
%3 = OpTypeFunction %void
|
|
%1 = OpFunction %void None %3
|
|
%4 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)");
|
|
|
|
spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_1);
|
|
std::unique_ptr<IRContext> context =
|
|
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, input,
|
|
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
|
ASSERT_NE(context, nullptr);
|
|
|
|
CompactIdsPass compact_id_pass;
|
|
context->module()->SetIdBound(20000);
|
|
const auto status = compact_id_pass.Run(context.get());
|
|
EXPECT_EQ(status, Pass::Status::SuccessWithChange);
|
|
EXPECT_EQ(context->module()->id_bound(), 5);
|
|
|
|
// Test output just in case
|
|
std::vector<uint32_t> binary;
|
|
context->module()->ToBinary(&binary, false);
|
|
std::string disassembly;
|
|
tools.Disassemble(binary, &disassembly,
|
|
SpirvTools::kDefaultDisassembleOption);
|
|
|
|
EXPECT_THAT(disassembly, ::testing::Eq(input));
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace opt
|
|
} // namespace spvtools
|