blob: 0171c09b9c12a1f78383efc168f70ce22de26add [file] [log] [blame]
// 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 <string>
#include <vector>
#include "gmock/gmock.h"
#include "spirv-tools/libspirv.hpp"
#include "spirv-tools/optimizer.hpp"
#include "test/opt/pass_fixture.h"
namespace spvtools {
namespace opt {
namespace {
using ::testing::Eq;
// Return a string that contains the minimum instructions needed to form
// a valid module. Other instructions can be appended to this string.
std::string Header() {
return R"(OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
)";
}
TEST(Optimizer, CanRunNullPassWithDistinctInputOutputVectors) {
SpirvTools tools(SPV_ENV_UNIVERSAL_1_0);
std::vector<uint32_t> binary_in;
tools.Assemble(Header() + "OpName %foo \"foo\"\n%foo = OpTypeVoid",
&binary_in);
Optimizer opt(SPV_ENV_UNIVERSAL_1_0);
opt.RegisterPass(CreateNullPass());
std::vector<uint32_t> binary_out;
opt.Run(binary_in.data(), binary_in.size(), &binary_out);
std::string disassembly;
tools.Disassemble(binary_out.data(), binary_out.size(), &disassembly);
EXPECT_THAT(disassembly,
Eq(Header() + "OpName %foo \"foo\"\n%foo = OpTypeVoid\n"));
}
TEST(Optimizer, CanRunTransformingPassWithDistinctInputOutputVectors) {
SpirvTools tools(SPV_ENV_UNIVERSAL_1_0);
std::vector<uint32_t> binary_in;
tools.Assemble(Header() + "OpName %foo \"foo\"\n%foo = OpTypeVoid",
&binary_in);
Optimizer opt(SPV_ENV_UNIVERSAL_1_0);
opt.RegisterPass(CreateStripDebugInfoPass());
std::vector<uint32_t> binary_out;
opt.Run(binary_in.data(), binary_in.size(), &binary_out);
std::string disassembly;
tools.Disassemble(binary_out.data(), binary_out.size(), &disassembly);
EXPECT_THAT(disassembly, Eq(Header() + "%void = OpTypeVoid\n"));
}
TEST(Optimizer, CanRunNullPassWithAliasedVectors) {
SpirvTools tools(SPV_ENV_UNIVERSAL_1_0);
std::vector<uint32_t> binary;
tools.Assemble("OpName %foo \"foo\"\n%foo = OpTypeVoid", &binary);
Optimizer opt(SPV_ENV_UNIVERSAL_1_0);
opt.RegisterPass(CreateNullPass());
opt.Run(binary.data(), binary.size(), &binary); // This is the key.
std::string disassembly;
tools.Disassemble(binary.data(), binary.size(), &disassembly);
EXPECT_THAT(disassembly, Eq("OpName %foo \"foo\"\n%foo = OpTypeVoid\n"));
}
TEST(Optimizer, CanRunNullPassWithAliasedVectorDataButDifferentSize) {
SpirvTools tools(SPV_ENV_UNIVERSAL_1_0);
std::vector<uint32_t> binary;
tools.Assemble(Header() + "OpName %foo \"foo\"\n%foo = OpTypeVoid", &binary);
Optimizer opt(SPV_ENV_UNIVERSAL_1_0);
opt.RegisterPass(CreateNullPass());
auto orig_size = binary.size();
// Now change the size. Add a word that will be ignored
// by the optimizer.
binary.push_back(42);
EXPECT_THAT(orig_size + 1, Eq(binary.size()));
opt.Run(binary.data(), orig_size, &binary); // This is the key.
// The binary vector should have been rewritten.
EXPECT_THAT(binary.size(), Eq(orig_size));
std::string disassembly;
tools.Disassemble(binary.data(), binary.size(), &disassembly);
EXPECT_THAT(disassembly,
Eq(Header() + "OpName %foo \"foo\"\n%foo = OpTypeVoid\n"));
}
TEST(Optimizer, CanRunTransformingPassWithAliasedVectors) {
SpirvTools tools(SPV_ENV_UNIVERSAL_1_0);
std::vector<uint32_t> binary;
tools.Assemble(Header() + "OpName %foo \"foo\"\n%foo = OpTypeVoid", &binary);
Optimizer opt(SPV_ENV_UNIVERSAL_1_0);
opt.RegisterPass(CreateStripDebugInfoPass());
opt.Run(binary.data(), binary.size(), &binary); // This is the key
std::string disassembly;
tools.Disassemble(binary.data(), binary.size(), &disassembly);
EXPECT_THAT(disassembly, Eq(Header() + "%void = OpTypeVoid\n"));
}
TEST(Optimizer, CanValidateFlags) {
Optimizer opt(SPV_ENV_UNIVERSAL_1_0);
EXPECT_FALSE(opt.FlagHasValidForm("bad-flag"));
EXPECT_TRUE(opt.FlagHasValidForm("-O"));
EXPECT_TRUE(opt.FlagHasValidForm("-Os"));
EXPECT_FALSE(opt.FlagHasValidForm("-O2"));
EXPECT_TRUE(opt.FlagHasValidForm("--this_flag"));
}
TEST(Optimizer, CanRegisterPassesFromFlags) {
SpirvTools tools(SPV_ENV_UNIVERSAL_1_0);
Optimizer opt(SPV_ENV_UNIVERSAL_1_0);
spv_message_level_t msg_level;
const char* msg_fname;
spv_position_t msg_position;
const char* msg;
auto examine_message = [&msg_level, &msg_fname, &msg_position, &msg](
spv_message_level_t ml, const char* f,
const spv_position_t& p, const char* m) {
msg_level = ml;
msg_fname = f;
msg_position = p;
msg = m;
};
opt.SetMessageConsumer(examine_message);
std::vector<std::string> pass_flags = {
"--strip-debug",
"--strip-nonsemantic",
"--set-spec-const-default-value=23:42 21:12",
"--if-conversion",
"--freeze-spec-const",
"--inline-entry-points-exhaustive",
"--inline-entry-points-opaque",
"--convert-local-access-chains",
"--eliminate-dead-code-aggressive",
"--eliminate-insert-extract",
"--eliminate-local-single-block",
"--eliminate-local-single-store",
"--merge-blocks",
"--merge-return",
"--eliminate-dead-branches",
"--eliminate-dead-functions",
"--eliminate-local-multi-store",
"--eliminate-dead-const",
"--eliminate-dead-inserts",
"--eliminate-dead-variables",
"--fold-spec-const-op-composite",
"--loop-unswitch",
"--scalar-replacement=300",
"--scalar-replacement",
"--strength-reduction",
"--unify-const",
"--flatten-decorations",
"--compact-ids",
"--cfg-cleanup",
"--local-redundancy-elimination",
"--loop-invariant-code-motion",
"--reduce-load-size",
"--redundancy-elimination",
"--private-to-local",
"--remove-duplicates",
"--workaround-1209",
"--replace-invalid-opcode",
"--simplify-instructions",
"--ssa-rewrite",
"--copy-propagate-arrays",
"--loop-fission=20",
"--loop-fusion=2",
"--loop-unroll",
"--vector-dce",
"--loop-unroll-partial=3",
"--loop-peeling",
"--ccp",
"-O",
"-Os",
"--legalize-hlsl"};
EXPECT_TRUE(opt.RegisterPassesFromFlags(pass_flags));
// Test some invalid flags.
EXPECT_FALSE(opt.RegisterPassFromFlag("-O2"));
EXPECT_EQ(msg_level, SPV_MSG_ERROR);
EXPECT_FALSE(opt.RegisterPassFromFlag("-loop-unroll"));
EXPECT_EQ(msg_level, SPV_MSG_ERROR);
EXPECT_FALSE(opt.RegisterPassFromFlag("--set-spec-const-default-value"));
EXPECT_EQ(msg_level, SPV_MSG_ERROR);
EXPECT_FALSE(opt.RegisterPassFromFlag("--scalar-replacement=s"));
EXPECT_EQ(msg_level, SPV_MSG_ERROR);
EXPECT_FALSE(opt.RegisterPassFromFlag("--loop-fission=-4"));
EXPECT_EQ(msg_level, SPV_MSG_ERROR);
EXPECT_FALSE(opt.RegisterPassFromFlag("--loop-fusion=xx"));
EXPECT_EQ(msg_level, SPV_MSG_ERROR);
EXPECT_FALSE(opt.RegisterPassFromFlag("--loop-unroll-partial"));
EXPECT_EQ(msg_level, SPV_MSG_ERROR);
}
TEST(Optimizer, RemoveNop) {
// Test that OpNops are removed even if no optimizations are run.
const std::string before = R"(OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
%void = OpTypeVoid
%2 = OpTypeFunction %void
%3 = OpFunction %void None %2
%4 = OpLabel
OpNop
OpReturn
OpFunctionEnd
)";
const std::string after = R"(OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
%void = OpTypeVoid
%2 = OpTypeFunction %void
%3 = OpFunction %void None %2
%4 = OpLabel
OpReturn
OpFunctionEnd
)";
std::vector<uint32_t> binary;
{
SpirvTools tools(SPV_ENV_VULKAN_1_1);
tools.Assemble(before, &binary);
}
Optimizer opt(SPV_ENV_VULKAN_1_1);
std::vector<uint32_t> optimized;
class ValidatorOptions validator_options;
ASSERT_TRUE(opt.Run(binary.data(), binary.size(), &optimized,
validator_options, true))
<< before << "\n";
std::string disassembly;
{
SpirvTools tools(SPV_ENV_VULKAN_1_1);
tools.Disassemble(optimized.data(), optimized.size(), &disassembly);
}
EXPECT_EQ(after, disassembly)
<< "Was expecting the OpNop to have been removed.";
}
TEST(Optimizer, AvoidIntegrityCheckForExtraLineInfo) {
// Test that it avoids the integrity check when no optimizations are run and
// OpLines are propagated.
const std::string before = R"(OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
%1 = OpString "Test"
%void = OpTypeVoid
%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%_ptr_Function_uint = OpTypePointer Function %uint
%6 = OpFunction %void None %3
%7 = OpLabel
OpLine %1 10 0
%8 = OpVariable %_ptr_Function_uint Function
OpLine %1 10 0
%9 = OpVariable %_ptr_Function_uint Function
OpLine %1 20 0
OpReturn
OpFunctionEnd
)";
const std::string after = R"(OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
%1 = OpString "Test"
%void = OpTypeVoid
%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%_ptr_Function_uint = OpTypePointer Function %uint
%6 = OpFunction %void None %3
%7 = OpLabel
OpLine %1 10 0
%8 = OpVariable %_ptr_Function_uint Function
%9 = OpVariable %_ptr_Function_uint Function
OpLine %1 20 0
OpReturn
OpFunctionEnd
)";
std::vector<uint32_t> binary;
SpirvTools tools(SPV_ENV_VULKAN_1_1);
tools.Assemble(before, &binary);
Optimizer opt(SPV_ENV_VULKAN_1_1);
std::vector<uint32_t> optimized;
class ValidatorOptions validator_options;
ASSERT_TRUE(opt.Run(binary.data(), binary.size(), &optimized,
validator_options, true))
<< before << "\n";
std::string disassembly;
tools.Disassemble(optimized.data(), optimized.size(), &disassembly);
EXPECT_EQ(after, disassembly)
<< "Was expecting the OpLine to have been propagated.";
}
TEST(Optimizer, AvoidIntegrityCheckForDebugScope) {
// Test that it avoids the integrity check when the code contains DebugScope.
const std::string before = R"(OpCapability Shader
%1 = OpExtInstImport "OpenCL.DebugInfo.100"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
%3 = OpString "simple_vs.hlsl"
OpSource HLSL 600 %3
OpName %main "main"
%void = OpTypeVoid
%5 = OpTypeFunction %void
%6 = OpExtInst %void %1 DebugSource %3
%7 = OpExtInst %void %1 DebugCompilationUnit 2 4 %6 HLSL
%main = OpFunction %void None %5
%14 = OpLabel
%26 = OpExtInst %void %1 DebugScope %7
OpReturn
%27 = OpExtInst %void %1 DebugNoScope
OpFunctionEnd
)";
const std::string after = R"(OpCapability Shader
%1 = OpExtInstImport "OpenCL.DebugInfo.100"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
%3 = OpString "simple_vs.hlsl"
OpSource HLSL 600 %3
OpName %main "main"
%void = OpTypeVoid
%5 = OpTypeFunction %void
%6 = OpExtInst %void %1 DebugSource %3
%7 = OpExtInst %void %1 DebugCompilationUnit 2 4 %6 HLSL
%main = OpFunction %void None %5
%8 = OpLabel
%11 = OpExtInst %void %1 DebugScope %7
OpReturn
%12 = OpExtInst %void %1 DebugNoScope
OpFunctionEnd
)";
std::vector<uint32_t> binary;
SpirvTools tools(SPV_ENV_VULKAN_1_1);
tools.Assemble(before, &binary);
Optimizer opt(SPV_ENV_VULKAN_1_1);
std::vector<uint32_t> optimized;
ASSERT_TRUE(opt.Run(binary.data(), binary.size(), &optimized))
<< before << "\n";
std::string disassembly;
tools.Disassemble(optimized.data(), optimized.size(), &disassembly);
EXPECT_EQ(after, disassembly)
<< "Was expecting the result id of DebugScope to have been changed.";
}
} // namespace
} // namespace opt
} // namespace spvtools