Removed spirv-tools tests.

This commit is contained in:
Бранимир Караџић
2019-12-11 21:03:51 -08:00
parent 66cb9dc91d
commit 06d6c6abdd
335 changed files with 0 additions and 233242 deletions

View File

@@ -1,192 +0,0 @@
# Copyright (c) 2015-2016 The Khronos Group 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.
# Add a SPIR-V Tools unit test. Signature:
# add_spvtools_unittest(
# TARGET target_name
# SRCS src_file.h src_file.cpp
# LIBS lib1 lib2
# )
if (NOT "${SPIRV_SKIP_TESTS}")
if (TARGET gmock_main)
message(STATUS "Found Google Mock, building tests.")
else()
message(STATUS "Did not find googletest, tests will not be built. "
"To enable tests place googletest in '<spirv-dir>/external/googletest'.")
endif()
endif()
function(add_spvtools_unittest)
if (NOT "${SPIRV_SKIP_TESTS}" AND TARGET gmock_main)
set(one_value_args TARGET PCH_FILE)
set(multi_value_args SRCS LIBS ENVIRONMENT)
cmake_parse_arguments(
ARG "" "${one_value_args}" "${multi_value_args}" ${ARGN})
set(target test_${ARG_TARGET})
set(SRC_COPY ${ARG_SRCS})
if (DEFINED ARG_PCH_FILE)
spvtools_pch(SRC_COPY ${ARG_PCH_FILE})
endif()
add_executable(${target} ${SRC_COPY})
spvtools_default_compile_options(${target})
if(${COMPILER_IS_LIKE_GNU})
target_compile_options(${target} PRIVATE -Wno-undef)
# Effcee and RE2 headers exhibit shadowing.
target_compile_options(${target} PRIVATE -Wno-shadow)
endif()
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# Disable C4503 "decorated name length exceeded" warning,
# triggered by some heavily templated types.
# We don't care much about that in test code.
# Important to do since we have warnings-as-errors.
target_compile_options(${target} PRIVATE /wd4503)
# Googletest accidentally turns off support for ::testing::Combine
# in VS 2017. See https://github.com/google/googletest/issues/1352
# Forcibly turn it on again.
target_compile_options(${target} PRIVATE /DGTEST_HAS_COMBINE=1)
endif()
target_include_directories(${target} PRIVATE
${SPIRV_HEADER_INCLUDE_DIR}
${spirv-tools_SOURCE_DIR}
${spirv-tools_SOURCE_DIR}/include
${spirv-tools_SOURCE_DIR}/test
${spirv-tools_BINARY_DIR}
${gtest_SOURCE_DIR}/include
${gmock_SOURCE_DIR}/include
)
if (TARGET effcee)
# If using Effcee for testing, then add its include directory.
target_include_directories(${target} PRIVATE ${effcee_SOURCE_DIR})
endif()
target_link_libraries(${target} PRIVATE ${ARG_LIBS})
if (TARGET effcee)
target_link_libraries(${target} PRIVATE effcee)
endif()
target_link_libraries(${target} PRIVATE gmock_main)
add_test(NAME spirv-tools-${target} COMMAND ${target})
if (DEFINED ARG_ENVIRONMENT)
set_tests_properties(spirv-tools-${target} PROPERTIES ENVIRONMENT ${ARG_ENVIRONMENT})
endif()
set_property(TARGET ${target} PROPERTY FOLDER "SPIRV-Tools tests")
endif()
endfunction()
set(TEST_SOURCES
test_fixture.h
unit_spirv.h
assembly_context_test.cpp
assembly_format_test.cpp
binary_destroy_test.cpp
binary_endianness_test.cpp
binary_header_get_test.cpp
binary_parse_test.cpp
binary_strnlen_s_test.cpp
binary_to_text_test.cpp
binary_to_text.literal_test.cpp
comment_test.cpp
diagnostic_test.cpp
enum_string_mapping_test.cpp
enum_set_test.cpp
ext_inst.debuginfo_test.cpp
ext_inst.glsl_test.cpp
ext_inst.opencl_test.cpp
fix_word_test.cpp
generator_magic_number_test.cpp
hex_float_test.cpp
immediate_int_test.cpp
libspirv_macros_test.cpp
log_test.cpp
named_id_test.cpp
name_mapper_test.cpp
opcode_make_test.cpp
opcode_require_capabilities_test.cpp
opcode_split_test.cpp
opcode_table_get_test.cpp
operand_capabilities_test.cpp
operand_test.cpp
operand_pattern_test.cpp
parse_number_test.cpp
preserve_numeric_ids_test.cpp
software_version_test.cpp
string_utils_test.cpp
target_env_test.cpp
text_advance_test.cpp
text_destroy_test.cpp
text_literal_test.cpp
text_start_new_inst_test.cpp
text_to_binary.annotation_test.cpp
text_to_binary.barrier_test.cpp
text_to_binary.composite_test.cpp
text_to_binary.constant_test.cpp
text_to_binary.control_flow_test.cpp
text_to_binary_test.cpp
text_to_binary.debug_test.cpp
text_to_binary.device_side_enqueue_test.cpp
text_to_binary.extension_test.cpp
text_to_binary.function_test.cpp
text_to_binary.group_test.cpp
text_to_binary.image_test.cpp
text_to_binary.literal_test.cpp
text_to_binary.memory_test.cpp
text_to_binary.misc_test.cpp
text_to_binary.mode_setting_test.cpp
text_to_binary.pipe_storage_test.cpp
text_to_binary.type_declaration_test.cpp
text_to_binary.subgroup_dispatch_test.cpp
text_to_binary.reserved_sampling_test.cpp
text_word_get_test.cpp
unit_spirv.cpp
)
spvtools_pch(TEST_SOURCES pch_test)
add_spvtools_unittest(
TARGET spirv_unit_tests
SRCS ${TEST_SOURCES}
LIBS ${SPIRV_TOOLS})
add_spvtools_unittest(
TARGET c_interface
SRCS c_interface_test.cpp
LIBS ${SPIRV_TOOLS})
add_spvtools_unittest(
TARGET c_interface_shared
SRCS c_interface_test.cpp
LIBS ${SPIRV_TOOLS}-shared
ENVIRONMENT PATH=$<TARGET_FILE_DIR:${SPIRV_TOOLS}-shared>)
add_spvtools_unittest(
TARGET cpp_interface
SRCS cpp_interface_test.cpp
LIBS SPIRV-Tools-opt)
if (${SPIRV_TIMER_ENABLED})
add_spvtools_unittest(
TARGET timer
SRCS timer_test.cpp
LIBS ${SPIRV_TOOLS})
endif()
add_subdirectory(link)
add_subdirectory(opt)
add_subdirectory(reduce)
add_subdirectory(fuzz)
add_subdirectory(tools)
add_subdirectory(util)
add_subdirectory(val)

View File

@@ -1,77 +0,0 @@
// Copyright (c) 2015-2016 The Khronos Group 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 "source/instruction.h"
#include "source/util/string_utils.h"
#include "test/unit_spirv.h"
namespace spvtools {
namespace {
using spvtest::AutoText;
using spvtest::Concatenate;
using ::testing::Eq;
struct EncodeStringCase {
std::string str;
std::vector<uint32_t> initial_contents;
};
using EncodeStringTest = ::testing::TestWithParam<EncodeStringCase>;
TEST_P(EncodeStringTest, Sample) {
AssemblyContext context(AutoText(""), nullptr);
spv_instruction_t inst;
inst.words = GetParam().initial_contents;
ASSERT_EQ(SPV_SUCCESS,
context.binaryEncodeString(GetParam().str.c_str(), &inst));
// We already trust MakeVector
EXPECT_THAT(inst.words, Eq(Concatenate({GetParam().initial_contents,
utils::MakeVector(GetParam().str)})));
}
// clang-format off
INSTANTIATE_TEST_SUITE_P(
BinaryEncodeString, EncodeStringTest,
::testing::ValuesIn(std::vector<EncodeStringCase>{
// Use cases that exercise at least one to two words,
// and both empty and non-empty initial contents.
{"", {}},
{"", {1,2,3}},
{"a", {}},
{"a", {4}},
{"ab", {4}},
{"abc", {}},
{"abc", {18}},
{"abcd", {}},
{"abcd", {22}},
{"abcde", {4}},
{"abcdef", {}},
{"abcdef", {99,42}},
{"abcdefg", {}},
{"abcdefg", {101}},
{"abcdefgh", {}},
{"abcdefgh", {102, 103, 104}},
// A very long string, encoded after an initial word.
// SPIR-V limits strings to 65535 characters.
{std::string(65535, 'a'), {1}},
}));
// clang-format on
} // namespace
} // namespace spvtools

View File

@@ -1,50 +0,0 @@
// Copyright (c) 2015-2016 The Khronos Group 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 "test/test_fixture.h"
namespace svptools {
namespace {
using spvtest::ScopedContext;
using spvtest::TextToBinaryTest;
TEST_F(TextToBinaryTest, InstOpcodeProducesResultIDButNoIDDefinedFails) {
SetText("OpTypeMatrix %1 %2 1000");
EXPECT_EQ(SPV_ERROR_INVALID_TEXT,
spvTextToBinary(ScopedContext().context, text.str, text.length,
&binary, &diagnostic));
ASSERT_NE(nullptr, diagnostic);
EXPECT_STREQ(
"Expected <result-id> at the beginning of an instruction, found "
"'OpTypeMatrix'.",
diagnostic->error);
EXPECT_EQ(0u, diagnostic->position.line);
}
TEST_F(TextToBinaryTest,
InstDefinesResultIDButOpcodeDoesNotProduceAResultFails) {
SetText("\n\n%foo = OpName %1 \"bar\"");
EXPECT_EQ(SPV_ERROR_INVALID_TEXT,
spvTextToBinary(ScopedContext().context, text.str, text.length,
&binary, &diagnostic));
ASSERT_NE(nullptr, diagnostic);
EXPECT_STREQ(
"Cannot set ID %foo because OpName does not produce a result ID.",
diagnostic->error);
EXPECT_EQ(2u, diagnostic->position.line);
}
} // namespace
} // namespace svptools

View File

@@ -1,44 +0,0 @@
// Copyright (c) 2015-2016 The Khronos Group 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 "test/unit_spirv.h"
#include "test/test_fixture.h"
namespace spvtools {
namespace {
using spvtest::ScopedContext;
TEST(BinaryDestroy, Null) {
// There is no state or return value to check. Just check
// for the ability to call the API without abnormal termination.
spvBinaryDestroy(nullptr);
}
using BinaryDestroySomething = spvtest::TextToBinaryTest;
// Checks safety of destroying a validly constructed binary.
TEST_F(BinaryDestroySomething, Default) {
// Use a binary object constructed by the API instead of rolling our own.
SetText("OpSource OpenCL_C 120");
spv_binary my_binary = nullptr;
ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(ScopedContext().context, text.str,
text.length, &my_binary, &diagnostic));
ASSERT_NE(nullptr, my_binary);
spvBinaryDestroy(my_binary);
}
} // namespace
} // namespace spvtools

View File

@@ -1,54 +0,0 @@
// Copyright (c) 2015-2016 The Khronos Group 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 "test/unit_spirv.h"
namespace spvtools {
namespace {
TEST(BinaryEndianness, InvalidCode) {
uint32_t invalidMagicNumber[] = {0};
spv_const_binary_t binary = {invalidMagicNumber, 1};
spv_endianness_t endian;
ASSERT_EQ(SPV_ERROR_INVALID_BINARY, spvBinaryEndianness(&binary, &endian));
}
TEST(BinaryEndianness, Little) {
uint32_t magicNumber;
if (I32_ENDIAN_HOST == I32_ENDIAN_LITTLE) {
magicNumber = 0x07230203;
} else {
magicNumber = 0x03022307;
}
spv_const_binary_t binary = {&magicNumber, 1};
spv_endianness_t endian;
ASSERT_EQ(SPV_SUCCESS, spvBinaryEndianness(&binary, &endian));
ASSERT_EQ(SPV_ENDIANNESS_LITTLE, endian);
}
TEST(BinaryEndianness, Big) {
uint32_t magicNumber;
if (I32_ENDIAN_HOST == I32_ENDIAN_BIG) {
magicNumber = 0x07230203;
} else {
magicNumber = 0x03022307;
}
spv_const_binary_t binary = {&magicNumber, 1};
spv_endianness_t endian;
ASSERT_EQ(SPV_SUCCESS, spvBinaryEndianness(&binary, &endian));
ASSERT_EQ(SPV_ENDIANNESS_BIG, endian);
}
} // namespace
} // namespace spvtools

View File

@@ -1,85 +0,0 @@
// Copyright (c) 2015-2016 The Khronos Group 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 "source/spirv_constant.h"
#include "test/unit_spirv.h"
namespace spvtools {
namespace {
class BinaryHeaderGet : public ::testing::Test {
public:
BinaryHeaderGet() { memset(code, 0, sizeof(code)); }
virtual void SetUp() {
code[0] = SpvMagicNumber;
code[1] = SpvVersion;
code[2] = SPV_GENERATOR_CODEPLAY;
code[3] = 1; // NOTE: Bound
code[4] = 0; // NOTE: Schema; reserved
code[5] = 0; // NOTE: Instructions
binary.code = code;
binary.wordCount = 6;
}
spv_const_binary_t get_const_binary() {
return spv_const_binary_t{binary.code, binary.wordCount};
}
virtual void TearDown() {}
uint32_t code[6];
spv_binary_t binary;
};
TEST_F(BinaryHeaderGet, Default) {
spv_endianness_t endian;
spv_const_binary_t const_bin = get_const_binary();
ASSERT_EQ(SPV_SUCCESS, spvBinaryEndianness(&const_bin, &endian));
spv_header_t header;
ASSERT_EQ(SPV_SUCCESS, spvBinaryHeaderGet(&const_bin, endian, &header));
ASSERT_EQ(static_cast<uint32_t>(SpvMagicNumber), header.magic);
// Expect SPIRV-Headers updated to SPIR-V 1.5.
ASSERT_EQ(0x00010500u, header.version);
ASSERT_EQ(static_cast<uint32_t>(SPV_GENERATOR_CODEPLAY), header.generator);
ASSERT_EQ(1u, header.bound);
ASSERT_EQ(0u, header.schema);
ASSERT_EQ(&code[5], header.instructions);
}
TEST_F(BinaryHeaderGet, InvalidCode) {
spv_const_binary_t my_binary = {nullptr, 0};
spv_header_t header;
ASSERT_EQ(SPV_ERROR_INVALID_BINARY,
spvBinaryHeaderGet(&my_binary, SPV_ENDIANNESS_LITTLE, &header));
}
TEST_F(BinaryHeaderGet, InvalidPointerHeader) {
spv_const_binary_t const_bin = get_const_binary();
ASSERT_EQ(SPV_ERROR_INVALID_POINTER,
spvBinaryHeaderGet(&const_bin, SPV_ENDIANNESS_LITTLE, nullptr));
}
TEST_F(BinaryHeaderGet, TruncatedHeader) {
for (uint8_t i = 1; i < SPV_INDEX_INSTRUCTION; i++) {
binary.wordCount = i;
spv_const_binary_t const_bin = get_const_binary();
ASSERT_EQ(SPV_ERROR_INVALID_BINARY,
spvBinaryHeaderGet(&const_bin, SPV_ENDIANNESS_LITTLE, nullptr));
}
}
} // namespace
} // namespace spvtools

View File

@@ -1,893 +0,0 @@
// Copyright (c) 2015-2016 The Khronos Group 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 <algorithm>
#include <limits>
#include <sstream>
#include <string>
#include <vector>
#include "gmock/gmock.h"
#include "source/latest_version_opencl_std_header.h"
#include "source/table.h"
#include "source/util/string_utils.h"
#include "test/test_fixture.h"
#include "test/unit_spirv.h"
// Returns true if two spv_parsed_operand_t values are equal.
// To use this operator, this definition must appear in the same namespace
// as spv_parsed_operand_t.
static bool operator==(const spv_parsed_operand_t& a,
const spv_parsed_operand_t& b) {
return a.offset == b.offset && a.num_words == b.num_words &&
a.type == b.type && a.number_kind == b.number_kind &&
a.number_bit_width == b.number_bit_width;
}
namespace spvtools {
namespace {
using ::spvtest::Concatenate;
using ::spvtest::MakeInstruction;
using utils::MakeVector;
using ::spvtest::ScopedContext;
using ::testing::_;
using ::testing::AnyOf;
using ::testing::Eq;
using ::testing::InSequence;
using ::testing::Return;
// An easily-constructible and comparable object for the contents of an
// spv_parsed_instruction_t. Unlike spv_parsed_instruction_t, owns the memory
// of its components.
struct ParsedInstruction {
explicit ParsedInstruction(const spv_parsed_instruction_t& inst)
: words(inst.words, inst.words + inst.num_words),
opcode(static_cast<SpvOp>(inst.opcode)),
ext_inst_type(inst.ext_inst_type),
type_id(inst.type_id),
result_id(inst.result_id),
operands(inst.operands, inst.operands + inst.num_operands) {}
std::vector<uint32_t> words;
SpvOp opcode;
spv_ext_inst_type_t ext_inst_type;
uint32_t type_id;
uint32_t result_id;
std::vector<spv_parsed_operand_t> operands;
bool operator==(const ParsedInstruction& b) const {
return words == b.words && opcode == b.opcode &&
ext_inst_type == b.ext_inst_type && type_id == b.type_id &&
result_id == b.result_id && operands == b.operands;
}
};
// Prints a ParsedInstruction object to the given output stream, and returns
// the stream.
std::ostream& operator<<(std::ostream& os, const ParsedInstruction& inst) {
os << "\nParsedInstruction( {";
spvtest::PrintTo(spvtest::WordVector(inst.words), &os);
os << "}, opcode: " << int(inst.opcode)
<< " ext_inst_type: " << int(inst.ext_inst_type)
<< " type_id: " << inst.type_id << " result_id: " << inst.result_id;
for (const auto& operand : inst.operands) {
os << " { offset: " << operand.offset << " num_words: " << operand.num_words
<< " type: " << int(operand.type)
<< " number_kind: " << int(operand.number_kind)
<< " number_bit_width: " << int(operand.number_bit_width) << "}";
}
os << ")";
return os;
}
// Sanity check for the equality operator on ParsedInstruction.
TEST(ParsedInstruction, ZeroInitializedAreEqual) {
spv_parsed_instruction_t pi = {};
ParsedInstruction a(pi);
ParsedInstruction b(pi);
EXPECT_THAT(a, ::testing::TypedEq<ParsedInstruction>(b));
}
// Googlemock class receiving Header/Instruction calls from spvBinaryParse().
class MockParseClient {
public:
MOCK_METHOD6(Header, spv_result_t(spv_endianness_t endian, uint32_t magic,
uint32_t version, uint32_t generator,
uint32_t id_bound, uint32_t reserved));
MOCK_METHOD1(Instruction, spv_result_t(const ParsedInstruction&));
};
// Casts user_data as MockParseClient and invokes its Header().
spv_result_t invoke_header(void* user_data, spv_endianness_t endian,
uint32_t magic, uint32_t version, uint32_t generator,
uint32_t id_bound, uint32_t reserved) {
return static_cast<MockParseClient*>(user_data)->Header(
endian, magic, version, generator, id_bound, reserved);
}
// Casts user_data as MockParseClient and invokes its Instruction().
spv_result_t invoke_instruction(
void* user_data, const spv_parsed_instruction_t* parsed_instruction) {
return static_cast<MockParseClient*>(user_data)->Instruction(
ParsedInstruction(*parsed_instruction));
}
// The SPIR-V module header words for the Khronos Assembler generator,
// for a module with an ID bound of 1.
const uint32_t kHeaderForBound1[] = {
SpvMagicNumber, SpvVersion,
SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS_ASSEMBLER, 0), 1 /*bound*/,
0 /*schema*/};
// Returns the expected SPIR-V module header words for the Khronos
// Assembler generator, and with a given Id bound.
std::vector<uint32_t> ExpectedHeaderForBound(uint32_t bound) {
return {SpvMagicNumber, 0x10000,
SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS_ASSEMBLER, 0), bound, 0};
}
// Returns a parsed operand for a non-number value at the given word offset
// within an instruction.
spv_parsed_operand_t MakeSimpleOperand(uint16_t offset,
spv_operand_type_t type) {
return {offset, 1, type, SPV_NUMBER_NONE, 0};
}
// Returns a parsed operand for a literal unsigned integer value at the given
// word offset within an instruction.
spv_parsed_operand_t MakeLiteralNumberOperand(uint16_t offset) {
return {offset, 1, SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_NUMBER_UNSIGNED_INT,
32};
}
// Returns a parsed operand for a literal string value at the given
// word offset within an instruction.
spv_parsed_operand_t MakeLiteralStringOperand(uint16_t offset,
uint16_t length) {
return {offset, length, SPV_OPERAND_TYPE_LITERAL_STRING, SPV_NUMBER_NONE, 0};
}
// Returns a ParsedInstruction for an OpTypeVoid instruction that would
// generate the given result Id.
ParsedInstruction MakeParsedVoidTypeInstruction(uint32_t result_id) {
const auto void_inst = MakeInstruction(SpvOpTypeVoid, {result_id});
const auto void_operands = std::vector<spv_parsed_operand_t>{
MakeSimpleOperand(1, SPV_OPERAND_TYPE_RESULT_ID)};
const spv_parsed_instruction_t parsed_void_inst = {
void_inst.data(),
static_cast<uint16_t>(void_inst.size()),
SpvOpTypeVoid,
SPV_EXT_INST_TYPE_NONE,
0, // type id
result_id,
void_operands.data(),
static_cast<uint16_t>(void_operands.size())};
return ParsedInstruction(parsed_void_inst);
}
// Returns a ParsedInstruction for an OpTypeInt instruction that generates
// the given result Id for a 32-bit signed integer scalar type.
ParsedInstruction MakeParsedInt32TypeInstruction(uint32_t result_id) {
const auto i32_inst = MakeInstruction(SpvOpTypeInt, {result_id, 32, 1});
const auto i32_operands = std::vector<spv_parsed_operand_t>{
MakeSimpleOperand(1, SPV_OPERAND_TYPE_RESULT_ID),
MakeLiteralNumberOperand(2), MakeLiteralNumberOperand(3)};
spv_parsed_instruction_t parsed_i32_inst = {
i32_inst.data(),
static_cast<uint16_t>(i32_inst.size()),
SpvOpTypeInt,
SPV_EXT_INST_TYPE_NONE,
0, // type id
result_id,
i32_operands.data(),
static_cast<uint16_t>(i32_operands.size())};
return ParsedInstruction(parsed_i32_inst);
}
class BinaryParseTest : public spvtest::TextToBinaryTestBase<::testing::Test> {
protected:
~BinaryParseTest() { spvDiagnosticDestroy(diagnostic_); }
void Parse(const SpirvVector& words, spv_result_t expected_result,
bool flip_words = false) {
SpirvVector flipped_words(words);
SCOPED_TRACE(flip_words ? "Flipped Endianness" : "Normal Endianness");
if (flip_words) {
std::transform(flipped_words.begin(), flipped_words.end(),
flipped_words.begin(), [](const uint32_t raw_word) {
return spvFixWord(raw_word,
I32_ENDIAN_HOST == I32_ENDIAN_BIG
? SPV_ENDIANNESS_LITTLE
: SPV_ENDIANNESS_BIG);
});
}
EXPECT_EQ(expected_result,
spvBinaryParse(ScopedContext().context, &client_,
flipped_words.data(), flipped_words.size(),
invoke_header, invoke_instruction, &diagnostic_));
}
spv_diagnostic diagnostic_ = nullptr;
MockParseClient client_;
};
// Adds an EXPECT_CALL to client_->Header() with appropriate parameters,
// including bound. Returns the EXPECT_CALL result.
#define EXPECT_HEADER(bound) \
EXPECT_CALL( \
client_, \
Header(AnyOf(SPV_ENDIANNESS_LITTLE, SPV_ENDIANNESS_BIG), SpvMagicNumber, \
0x10000, SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS_ASSEMBLER, 0), \
bound, 0 /*reserved*/))
static const bool kSwapEndians[] = {false, true};
TEST_F(BinaryParseTest, EmptyModuleHasValidHeaderAndNoInstructionCallbacks) {
for (bool endian_swap : kSwapEndians) {
const auto words = CompileSuccessfully("");
EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS));
EXPECT_CALL(client_, Instruction(_)).Times(0); // No instruction callback.
Parse(words, SPV_SUCCESS, endian_swap);
EXPECT_EQ(nullptr, diagnostic_);
}
}
TEST_F(BinaryParseTest, NullDiagnosticsIsOkForGoodParse) {
const auto words = CompileSuccessfully("");
EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS));
EXPECT_CALL(client_, Instruction(_)).Times(0); // No instruction callback.
EXPECT_EQ(
SPV_SUCCESS,
spvBinaryParse(ScopedContext().context, &client_, words.data(),
words.size(), invoke_header, invoke_instruction, nullptr));
}
TEST_F(BinaryParseTest, NullDiagnosticsIsOkForBadParse) {
auto words = CompileSuccessfully("");
words.push_back(0xffffffff); // Certainly invalid instruction header.
EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS));
EXPECT_CALL(client_, Instruction(_)).Times(0); // No instruction callback.
EXPECT_EQ(
SPV_ERROR_INVALID_BINARY,
spvBinaryParse(ScopedContext().context, &client_, words.data(),
words.size(), invoke_header, invoke_instruction, nullptr));
}
// Make sure that we don't blow up when both the consumer and the diagnostic are
// null.
TEST_F(BinaryParseTest, NullConsumerNullDiagnosticsForBadParse) {
auto words = CompileSuccessfully("");
auto ctx = spvtools::Context(SPV_ENV_UNIVERSAL_1_1);
ctx.SetMessageConsumer(nullptr);
words.push_back(0xffffffff); // Certainly invalid instruction header.
EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS));
EXPECT_CALL(client_, Instruction(_)).Times(0); // No instruction callback.
EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
spvBinaryParse(ctx.CContext(), &client_, words.data(), words.size(),
invoke_header, invoke_instruction, nullptr));
}
TEST_F(BinaryParseTest, SpecifyConsumerNullDiagnosticsForGoodParse) {
const auto words = CompileSuccessfully("");
auto ctx = spvtools::Context(SPV_ENV_UNIVERSAL_1_1);
int invocation = 0;
ctx.SetMessageConsumer([&invocation](spv_message_level_t, const char*,
const spv_position_t&,
const char*) { ++invocation; });
EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS));
EXPECT_CALL(client_, Instruction(_)).Times(0); // No instruction callback.
EXPECT_EQ(SPV_SUCCESS,
spvBinaryParse(ctx.CContext(), &client_, words.data(), words.size(),
invoke_header, invoke_instruction, nullptr));
EXPECT_EQ(0, invocation);
}
TEST_F(BinaryParseTest, SpecifyConsumerNullDiagnosticsForBadParse) {
auto words = CompileSuccessfully("");
auto ctx = spvtools::Context(SPV_ENV_UNIVERSAL_1_1);
int invocation = 0;
ctx.SetMessageConsumer(
[&invocation](spv_message_level_t level, const char* source,
const spv_position_t& position, const char* message) {
++invocation;
EXPECT_EQ(SPV_MSG_ERROR, level);
EXPECT_STREQ("input", source);
EXPECT_EQ(0u, position.line);
EXPECT_EQ(0u, position.column);
EXPECT_EQ(1u, position.index);
EXPECT_STREQ("Invalid opcode: 65535", message);
});
words.push_back(0xffffffff); // Certainly invalid instruction header.
EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS));
EXPECT_CALL(client_, Instruction(_)).Times(0); // No instruction callback.
EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
spvBinaryParse(ctx.CContext(), &client_, words.data(), words.size(),
invoke_header, invoke_instruction, nullptr));
EXPECT_EQ(1, invocation);
}
TEST_F(BinaryParseTest, SpecifyConsumerSpecifyDiagnosticsForGoodParse) {
const auto words = CompileSuccessfully("");
auto ctx = spvtools::Context(SPV_ENV_UNIVERSAL_1_1);
int invocation = 0;
ctx.SetMessageConsumer([&invocation](spv_message_level_t, const char*,
const spv_position_t&,
const char*) { ++invocation; });
EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS));
EXPECT_CALL(client_, Instruction(_)).Times(0); // No instruction callback.
EXPECT_EQ(SPV_SUCCESS,
spvBinaryParse(ctx.CContext(), &client_, words.data(), words.size(),
invoke_header, invoke_instruction, &diagnostic_));
EXPECT_EQ(0, invocation);
EXPECT_EQ(nullptr, diagnostic_);
}
TEST_F(BinaryParseTest, SpecifyConsumerSpecifyDiagnosticsForBadParse) {
auto words = CompileSuccessfully("");
auto ctx = spvtools::Context(SPV_ENV_UNIVERSAL_1_1);
int invocation = 0;
ctx.SetMessageConsumer([&invocation](spv_message_level_t, const char*,
const spv_position_t&,
const char*) { ++invocation; });
words.push_back(0xffffffff); // Certainly invalid instruction header.
EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS));
EXPECT_CALL(client_, Instruction(_)).Times(0); // No instruction callback.
EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
spvBinaryParse(ctx.CContext(), &client_, words.data(), words.size(),
invoke_header, invoke_instruction, &diagnostic_));
EXPECT_EQ(0, invocation);
EXPECT_STREQ("Invalid opcode: 65535", diagnostic_->error);
}
TEST_F(BinaryParseTest,
ModuleWithSingleInstructionHasValidHeaderAndInstructionCallback) {
for (bool endian_swap : kSwapEndians) {
const auto words = CompileSuccessfully("%1 = OpTypeVoid");
InSequence calls_expected_in_specific_order;
EXPECT_HEADER(2).WillOnce(Return(SPV_SUCCESS));
EXPECT_CALL(client_, Instruction(MakeParsedVoidTypeInstruction(1)))
.WillOnce(Return(SPV_SUCCESS));
Parse(words, SPV_SUCCESS, endian_swap);
EXPECT_EQ(nullptr, diagnostic_);
}
}
TEST_F(BinaryParseTest, NullHeaderCallbackIsIgnored) {
const auto words = CompileSuccessfully("%1 = OpTypeVoid");
EXPECT_CALL(client_, Header(_, _, _, _, _, _))
.Times(0); // No header callback.
EXPECT_CALL(client_, Instruction(MakeParsedVoidTypeInstruction(1)))
.WillOnce(Return(SPV_SUCCESS));
EXPECT_EQ(SPV_SUCCESS, spvBinaryParse(ScopedContext().context, &client_,
words.data(), words.size(), nullptr,
invoke_instruction, &diagnostic_));
EXPECT_EQ(nullptr, diagnostic_);
}
TEST_F(BinaryParseTest, NullInstructionCallbackIsIgnored) {
const auto words = CompileSuccessfully("%1 = OpTypeVoid");
EXPECT_HEADER((2)).WillOnce(Return(SPV_SUCCESS));
EXPECT_CALL(client_, Instruction(_)).Times(0); // No instruction callback.
EXPECT_EQ(SPV_SUCCESS,
spvBinaryParse(ScopedContext().context, &client_, words.data(),
words.size(), invoke_header, nullptr, &diagnostic_));
EXPECT_EQ(nullptr, diagnostic_);
}
// Check the result of multiple instruction callbacks.
//
// This test exercises non-default values for the following members of the
// spv_parsed_instruction_t struct: words, num_words, opcode, result_id,
// operands, num_operands.
TEST_F(BinaryParseTest, TwoScalarTypesGenerateTwoInstructionCallbacks) {
for (bool endian_swap : kSwapEndians) {
const auto words = CompileSuccessfully(
"%1 = OpTypeVoid "
"%2 = OpTypeInt 32 1");
InSequence calls_expected_in_specific_order;
EXPECT_HEADER(3).WillOnce(Return(SPV_SUCCESS));
EXPECT_CALL(client_, Instruction(MakeParsedVoidTypeInstruction(1)))
.WillOnce(Return(SPV_SUCCESS));
EXPECT_CALL(client_, Instruction(MakeParsedInt32TypeInstruction(2)))
.WillOnce(Return(SPV_SUCCESS));
Parse(words, SPV_SUCCESS, endian_swap);
EXPECT_EQ(nullptr, diagnostic_);
}
}
TEST_F(BinaryParseTest, EarlyReturnWithZeroPassingCallbacks) {
for (bool endian_swap : kSwapEndians) {
const auto words = CompileSuccessfully(
"%1 = OpTypeVoid "
"%2 = OpTypeInt 32 1");
InSequence calls_expected_in_specific_order;
EXPECT_HEADER(3).WillOnce(Return(SPV_ERROR_INVALID_BINARY));
// Early exit means no calls to Instruction().
EXPECT_CALL(client_, Instruction(_)).Times(0);
Parse(words, SPV_ERROR_INVALID_BINARY, endian_swap);
// On error, the binary parser doesn't generate its own diagnostics.
EXPECT_EQ(nullptr, diagnostic_);
}
}
TEST_F(BinaryParseTest,
EarlyReturnWithZeroPassingCallbacksAndSpecifiedResultCode) {
for (bool endian_swap : kSwapEndians) {
const auto words = CompileSuccessfully(
"%1 = OpTypeVoid "
"%2 = OpTypeInt 32 1");
InSequence calls_expected_in_specific_order;
EXPECT_HEADER(3).WillOnce(Return(SPV_REQUESTED_TERMINATION));
// Early exit means no calls to Instruction().
EXPECT_CALL(client_, Instruction(_)).Times(0);
Parse(words, SPV_REQUESTED_TERMINATION, endian_swap);
// On early termination, the binary parser doesn't generate its own
// diagnostics.
EXPECT_EQ(nullptr, diagnostic_);
}
}
TEST_F(BinaryParseTest, EarlyReturnWithOnePassingCallback) {
for (bool endian_swap : kSwapEndians) {
const auto words = CompileSuccessfully(
"%1 = OpTypeVoid "
"%2 = OpTypeInt 32 1 "
"%3 = OpTypeFloat 32");
InSequence calls_expected_in_specific_order;
EXPECT_HEADER(4).WillOnce(Return(SPV_SUCCESS));
EXPECT_CALL(client_, Instruction(MakeParsedVoidTypeInstruction(1)))
.WillOnce(Return(SPV_REQUESTED_TERMINATION));
Parse(words, SPV_REQUESTED_TERMINATION, endian_swap);
// On early termination, the binary parser doesn't generate its own
// diagnostics.
EXPECT_EQ(nullptr, diagnostic_);
}
}
TEST_F(BinaryParseTest, EarlyReturnWithTwoPassingCallbacks) {
for (bool endian_swap : kSwapEndians) {
const auto words = CompileSuccessfully(
"%1 = OpTypeVoid "
"%2 = OpTypeInt 32 1 "
"%3 = OpTypeFloat 32");
InSequence calls_expected_in_specific_order;
EXPECT_HEADER(4).WillOnce(Return(SPV_SUCCESS));
EXPECT_CALL(client_, Instruction(MakeParsedVoidTypeInstruction(1)))
.WillOnce(Return(SPV_SUCCESS));
EXPECT_CALL(client_, Instruction(MakeParsedInt32TypeInstruction(2)))
.WillOnce(Return(SPV_REQUESTED_TERMINATION));
Parse(words, SPV_REQUESTED_TERMINATION, endian_swap);
// On early termination, the binary parser doesn't generate its own
// diagnostics.
EXPECT_EQ(nullptr, diagnostic_);
}
}
TEST_F(BinaryParseTest, InstructionWithStringOperand) {
const std::string str =
"the future is already here, it's just not evenly distributed";
const auto str_words = MakeVector(str);
const auto instruction = MakeInstruction(SpvOpName, {99}, str_words);
const auto words = Concatenate({ExpectedHeaderForBound(100), instruction});
InSequence calls_expected_in_specific_order;
EXPECT_HEADER(100).WillOnce(Return(SPV_SUCCESS));
const auto operands = std::vector<spv_parsed_operand_t>{
MakeSimpleOperand(1, SPV_OPERAND_TYPE_ID),
MakeLiteralStringOperand(2, static_cast<uint16_t>(str_words.size()))};
EXPECT_CALL(client_,
Instruction(ParsedInstruction(spv_parsed_instruction_t{
instruction.data(), static_cast<uint16_t>(instruction.size()),
SpvOpName, SPV_EXT_INST_TYPE_NONE, 0 /*type id*/,
0 /* No result id for OpName*/, operands.data(),
static_cast<uint16_t>(operands.size())})))
.WillOnce(Return(SPV_SUCCESS));
// Since we are actually checking the output, don't test the
// endian-swapped version.
Parse(words, SPV_SUCCESS, false);
EXPECT_EQ(nullptr, diagnostic_);
}
// Checks for non-zero values for the result_id and ext_inst_type members
// spv_parsed_instruction_t.
TEST_F(BinaryParseTest, ExtendedInstruction) {
const auto words = CompileSuccessfully(
"%extcl = OpExtInstImport \"OpenCL.std\" "
"%result = OpExtInst %float %extcl sqrt %x");
EXPECT_HEADER(5).WillOnce(Return(SPV_SUCCESS));
EXPECT_CALL(client_, Instruction(_)).WillOnce(Return(SPV_SUCCESS));
// We're only interested in the second call to Instruction():
const auto operands = std::vector<spv_parsed_operand_t>{
MakeSimpleOperand(1, SPV_OPERAND_TYPE_TYPE_ID),
MakeSimpleOperand(2, SPV_OPERAND_TYPE_RESULT_ID),
MakeSimpleOperand(3,
SPV_OPERAND_TYPE_ID), // Extended instruction set Id
MakeSimpleOperand(4, SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER),
MakeSimpleOperand(5, SPV_OPERAND_TYPE_ID), // Id of the argument
};
const auto instruction = MakeInstruction(
SpvOpExtInst,
{2, 3, 1, static_cast<uint32_t>(OpenCLLIB::Entrypoints::Sqrt), 4});
EXPECT_CALL(client_,
Instruction(ParsedInstruction(spv_parsed_instruction_t{
instruction.data(), static_cast<uint16_t>(instruction.size()),
SpvOpExtInst, SPV_EXT_INST_TYPE_OPENCL_STD, 2 /*type id*/,
3 /*result id*/, operands.data(),
static_cast<uint16_t>(operands.size())})))
.WillOnce(Return(SPV_SUCCESS));
// Since we are actually checking the output, don't test the
// endian-swapped version.
Parse(words, SPV_SUCCESS, false);
EXPECT_EQ(nullptr, diagnostic_);
}
// A binary parser diagnostic test case where we provide the words array
// pointer and word count explicitly.
struct WordsAndCountDiagnosticCase {
const uint32_t* words;
size_t num_words;
std::string expected_diagnostic;
};
using BinaryParseWordsAndCountDiagnosticTest = spvtest::TextToBinaryTestBase<
::testing::TestWithParam<WordsAndCountDiagnosticCase>>;
TEST_P(BinaryParseWordsAndCountDiagnosticTest, WordAndCountCases) {
EXPECT_EQ(
SPV_ERROR_INVALID_BINARY,
spvBinaryParse(ScopedContext().context, nullptr, GetParam().words,
GetParam().num_words, nullptr, nullptr, &diagnostic));
ASSERT_NE(nullptr, diagnostic);
EXPECT_THAT(diagnostic->error, Eq(GetParam().expected_diagnostic));
}
INSTANTIATE_TEST_SUITE_P(
BinaryParseDiagnostic, BinaryParseWordsAndCountDiagnosticTest,
::testing::ValuesIn(std::vector<WordsAndCountDiagnosticCase>{
{nullptr, 0, "Missing module."},
{kHeaderForBound1, 0,
"Module has incomplete header: only 0 words instead of 5"},
{kHeaderForBound1, 1,
"Module has incomplete header: only 1 words instead of 5"},
{kHeaderForBound1, 2,
"Module has incomplete header: only 2 words instead of 5"},
{kHeaderForBound1, 3,
"Module has incomplete header: only 3 words instead of 5"},
{kHeaderForBound1, 4,
"Module has incomplete header: only 4 words instead of 5"},
}));
// A binary parser diagnostic test case where a vector of words is
// provided. We'll use this to express cases that can't be created
// via the assembler. Either we want to make a malformed instruction,
// or an invalid case the assembler would reject.
struct WordVectorDiagnosticCase {
std::vector<uint32_t> words;
std::string expected_diagnostic;
};
using BinaryParseWordVectorDiagnosticTest = spvtest::TextToBinaryTestBase<
::testing::TestWithParam<WordVectorDiagnosticCase>>;
TEST_P(BinaryParseWordVectorDiagnosticTest, WordVectorCases) {
const auto& words = GetParam().words;
EXPECT_THAT(spvBinaryParse(ScopedContext().context, nullptr, words.data(),
words.size(), nullptr, nullptr, &diagnostic),
AnyOf(SPV_ERROR_INVALID_BINARY, SPV_ERROR_INVALID_ID));
ASSERT_NE(nullptr, diagnostic);
EXPECT_THAT(diagnostic->error, Eq(GetParam().expected_diagnostic));
}
INSTANTIATE_TEST_SUITE_P(
BinaryParseDiagnostic, BinaryParseWordVectorDiagnosticTest,
::testing::ValuesIn(std::vector<WordVectorDiagnosticCase>{
{Concatenate({ExpectedHeaderForBound(1), {spvOpcodeMake(0, SpvOpNop)}}),
"Invalid instruction word count: 0"},
{Concatenate(
{ExpectedHeaderForBound(1),
{spvOpcodeMake(1, static_cast<SpvOp>(
std::numeric_limits<uint16_t>::max()))}}),
"Invalid opcode: 65535"},
{Concatenate({ExpectedHeaderForBound(1),
MakeInstruction(SpvOpNop, {42})}),
"Invalid instruction OpNop starting at word 5: expected "
"no more operands after 1 words, but stated word count is 2."},
// Supply several more unexpectd words.
{Concatenate({ExpectedHeaderForBound(1),
MakeInstruction(SpvOpNop, {42, 43, 44, 45, 46, 47})}),
"Invalid instruction OpNop starting at word 5: expected "
"no more operands after 1 words, but stated word count is 7."},
{Concatenate({ExpectedHeaderForBound(1),
MakeInstruction(SpvOpTypeVoid, {1, 2})}),
"Invalid instruction OpTypeVoid starting at word 5: expected "
"no more operands after 2 words, but stated word count is 3."},
{Concatenate({ExpectedHeaderForBound(1),
MakeInstruction(SpvOpTypeVoid, {1, 2, 5, 9, 10})}),
"Invalid instruction OpTypeVoid starting at word 5: expected "
"no more operands after 2 words, but stated word count is 6."},
{Concatenate({ExpectedHeaderForBound(1),
MakeInstruction(SpvOpTypeInt, {1, 32, 1, 9})}),
"Invalid instruction OpTypeInt starting at word 5: expected "
"no more operands after 4 words, but stated word count is 5."},
{Concatenate({ExpectedHeaderForBound(1),
MakeInstruction(SpvOpTypeInt, {1})}),
"End of input reached while decoding OpTypeInt starting at word 5:"
" expected more operands after 2 words."},
// Check several cases for running off the end of input.
// Detect a missing single word operand.
{Concatenate({ExpectedHeaderForBound(1),
{spvOpcodeMake(2, SpvOpTypeStruct)}}),
"End of input reached while decoding OpTypeStruct starting at word"
" 5: missing result ID operand at word offset 1."},
// Detect this a missing a multi-word operand to OpConstant.
// We also lie and say the OpConstant instruction has 5 words when
// it only has 3. Corresponds to something like this:
// %1 = OpTypeInt 64 0
// %2 = OpConstant %1 <missing>
{Concatenate({ExpectedHeaderForBound(3),
{MakeInstruction(SpvOpTypeInt, {1, 64, 0})},
{spvOpcodeMake(5, SpvOpConstant), 1, 2}}),
"End of input reached while decoding OpConstant starting at word"
" 9: missing possibly multi-word literal number operand at word "
"offset 3."},
// Detect when we provide only one word from the 64-bit literal,
// and again lie about the number of words in the instruction.
{Concatenate({ExpectedHeaderForBound(3),
{MakeInstruction(SpvOpTypeInt, {1, 64, 0})},
{spvOpcodeMake(5, SpvOpConstant), 1, 2, 42}}),
"End of input reached while decoding OpConstant starting at word"
" 9: truncated possibly multi-word literal number operand at word "
"offset 3."},
// Detect when a required string operand is missing.
// Also, lie about the length of the instruction.
{Concatenate({ExpectedHeaderForBound(3),
{spvOpcodeMake(3, SpvOpString), 1}}),
"End of input reached while decoding OpString starting at word"
" 5: missing literal string operand at word offset 2."},
// Detect when a required string operand is truncated: it's missing
// a null terminator. Catching the error avoids a buffer overrun.
{Concatenate({ExpectedHeaderForBound(3),
{spvOpcodeMake(4, SpvOpString), 1, 0x41414141,
0x41414141}}),
"End of input reached while decoding OpString starting at word"
" 5: truncated literal string operand at word offset 2."},
// Detect when an optional string operand is truncated: it's missing
// a null terminator. Catching the error avoids a buffer overrun.
// (It is valid for an optional string operand to be absent.)
{Concatenate({ExpectedHeaderForBound(3),
{spvOpcodeMake(6, SpvOpSource),
static_cast<uint32_t>(SpvSourceLanguageOpenCL_C), 210,
1 /* file id */,
/*start of string*/ 0x41414141, 0x41414141}}),
"End of input reached while decoding OpSource starting at word"
" 5: truncated literal string operand at word offset 4."},
// (End of input exhaustion test cases.)
// In this case the instruction word count is too small, where
// it would truncate a multi-word operand to OpConstant.
{Concatenate({ExpectedHeaderForBound(3),
{MakeInstruction(SpvOpTypeInt, {1, 64, 0})},
{spvOpcodeMake(4, SpvOpConstant), 1, 2, 44, 44}}),
"Invalid word count: OpConstant starting at word 9 says it has 4"
" words, but found 5 words instead."},
// Word count is to small, where it would truncate a literal string.
{Concatenate({ExpectedHeaderForBound(2),
{spvOpcodeMake(3, SpvOpString), 1, 0x41414141, 0}}),
"Invalid word count: OpString starting at word 5 says it has 3"
" words, but found 4 words instead."},
// Word count is too large. The string terminates before the last
// word.
{Concatenate({ExpectedHeaderForBound(2),
{spvOpcodeMake(4, SpvOpString), 1 /* result id */},
MakeVector("abc"),
{0 /* this word does not belong*/}}),
"Invalid instruction OpString starting at word 5: expected no more"
" operands after 3 words, but stated word count is 4."},
// Word count is too large. There are too many words after the string
// literal. A linkage attribute decoration is the only case in SPIR-V
// where a string operand is followed by another operand.
{Concatenate({ExpectedHeaderForBound(2),
{spvOpcodeMake(6, SpvOpDecorate), 1 /* target id */,
static_cast<uint32_t>(SpvDecorationLinkageAttributes)},
MakeVector("abc"),
{static_cast<uint32_t>(SpvLinkageTypeImport),
0 /* does not belong */}}),
"Invalid instruction OpDecorate starting at word 5: expected no more"
" operands after 5 words, but stated word count is 6."},
// Like the previous case, but with 5 extra words.
{Concatenate({ExpectedHeaderForBound(2),
{spvOpcodeMake(10, SpvOpDecorate), 1 /* target id */,
static_cast<uint32_t>(SpvDecorationLinkageAttributes)},
MakeVector("abc"),
{static_cast<uint32_t>(SpvLinkageTypeImport),
/* don't belong */ 0, 1, 2, 3, 4}}),
"Invalid instruction OpDecorate starting at word 5: expected no more"
" operands after 5 words, but stated word count is 10."},
// Like the previous two cases, but with OpMemberDecorate.
{Concatenate({ExpectedHeaderForBound(2),
{spvOpcodeMake(7, SpvOpMemberDecorate), 1 /* target id */,
42 /* member index */,
static_cast<uint32_t>(SpvDecorationLinkageAttributes)},
MakeVector("abc"),
{static_cast<uint32_t>(SpvLinkageTypeImport),
0 /* does not belong */}}),
"Invalid instruction OpMemberDecorate starting at word 5: expected no"
" more operands after 6 words, but stated word count is 7."},
{Concatenate({ExpectedHeaderForBound(2),
{spvOpcodeMake(11, SpvOpMemberDecorate),
1 /* target id */, 42 /* member index */,
static_cast<uint32_t>(SpvDecorationLinkageAttributes)},
MakeVector("abc"),
{static_cast<uint32_t>(SpvLinkageTypeImport),
/* don't belong */ 0, 1, 2, 3, 4}}),
"Invalid instruction OpMemberDecorate starting at word 5: expected no"
" more operands after 6 words, but stated word count is 11."},
// Word count is too large. There should be no more words
// after the RelaxedPrecision decoration.
{Concatenate({ExpectedHeaderForBound(2),
{spvOpcodeMake(4, SpvOpDecorate), 1 /* target id */,
static_cast<uint32_t>(SpvDecorationRelaxedPrecision),
0 /* does not belong */}}),
"Invalid instruction OpDecorate starting at word 5: expected no"
" more operands after 3 words, but stated word count is 4."},
// Word count is too large. There should be only one word after
// the SpecId decoration enum word.
{Concatenate({ExpectedHeaderForBound(2),
{spvOpcodeMake(5, SpvOpDecorate), 1 /* target id */,
static_cast<uint32_t>(SpvDecorationSpecId),
42 /* the spec id */, 0 /* does not belong */}}),
"Invalid instruction OpDecorate starting at word 5: expected no"
" more operands after 4 words, but stated word count is 5."},
{Concatenate({ExpectedHeaderForBound(2),
{spvOpcodeMake(2, SpvOpTypeVoid), 0}}),
"Error: Result Id is 0"},
{Concatenate({
ExpectedHeaderForBound(2),
{spvOpcodeMake(2, SpvOpTypeVoid), 1},
{spvOpcodeMake(2, SpvOpTypeBool), 1},
}),
"Id 1 is defined more than once"},
{Concatenate({ExpectedHeaderForBound(3),
MakeInstruction(SpvOpExtInst, {2, 3, 100, 4, 5})}),
"OpExtInst set Id 100 does not reference an OpExtInstImport result "
"Id"},
{Concatenate({ExpectedHeaderForBound(101),
MakeInstruction(SpvOpExtInstImport, {100},
MakeVector("OpenCL.std")),
// OpenCL cos is #14
MakeInstruction(SpvOpExtInst, {2, 3, 100, 14, 5, 999})}),
"Invalid instruction OpExtInst starting at word 10: expected no "
"more operands after 6 words, but stated word count is 7."},
// In this case, the OpSwitch selector refers to an invalid ID.
{Concatenate({ExpectedHeaderForBound(3),
MakeInstruction(SpvOpSwitch, {1, 2, 42, 3})}),
"Invalid OpSwitch: selector id 1 has no type"},
// In this case, the OpSwitch selector refers to an ID that has
// no type.
{Concatenate({ExpectedHeaderForBound(3),
MakeInstruction(SpvOpLabel, {1}),
MakeInstruction(SpvOpSwitch, {1, 2, 42, 3})}),
"Invalid OpSwitch: selector id 1 has no type"},
{Concatenate({ExpectedHeaderForBound(3),
MakeInstruction(SpvOpTypeInt, {1, 32, 0}),
MakeInstruction(SpvOpSwitch, {1, 3, 42, 3})}),
"Invalid OpSwitch: selector id 1 is a type, not a value"},
{Concatenate({ExpectedHeaderForBound(3),
MakeInstruction(SpvOpTypeFloat, {1, 32}),
MakeInstruction(SpvOpConstant, {1, 2, 0x78f00000}),
MakeInstruction(SpvOpSwitch, {2, 3, 42, 3})}),
"Invalid OpSwitch: selector id 2 is not a scalar integer"},
{Concatenate({ExpectedHeaderForBound(3),
MakeInstruction(SpvOpExtInstImport, {1},
MakeVector("invalid-import"))}),
"Invalid extended instruction import 'invalid-import'"},
{Concatenate({
ExpectedHeaderForBound(3),
MakeInstruction(SpvOpTypeInt, {1, 32, 0}),
MakeInstruction(SpvOpConstant, {2, 2, 42}),
}),
"Type Id 2 is not a type"},
{Concatenate({
ExpectedHeaderForBound(3),
MakeInstruction(SpvOpTypeBool, {1}),
MakeInstruction(SpvOpConstant, {1, 2, 42}),
}),
"Type Id 1 is not a scalar numeric type"},
}));
// A binary parser diagnostic case generated from an assembly text input.
struct AssemblyDiagnosticCase {
std::string assembly;
std::string expected_diagnostic;
};
using BinaryParseAssemblyDiagnosticTest = spvtest::TextToBinaryTestBase<
::testing::TestWithParam<AssemblyDiagnosticCase>>;
TEST_P(BinaryParseAssemblyDiagnosticTest, AssemblyCases) {
auto words = CompileSuccessfully(GetParam().assembly);
EXPECT_THAT(spvBinaryParse(ScopedContext().context, nullptr, words.data(),
words.size(), nullptr, nullptr, &diagnostic),
AnyOf(SPV_ERROR_INVALID_BINARY, SPV_ERROR_INVALID_ID));
ASSERT_NE(nullptr, diagnostic);
EXPECT_THAT(diagnostic->error, Eq(GetParam().expected_diagnostic));
}
INSTANTIATE_TEST_SUITE_P(
BinaryParseDiagnostic, BinaryParseAssemblyDiagnosticTest,
::testing::ValuesIn(std::vector<AssemblyDiagnosticCase>{
{"%1 = OpConstant !0 42", "Error: Type Id is 0"},
// A required id is 0.
{"OpName !0 \"foo\"", "Id is 0"},
// An optional id is 0, in this case the optional
// initializer.
{"%2 = OpVariable %1 CrossWorkgroup !0", "Id is 0"},
{"OpControlBarrier !0 %1 %2", "scope ID is 0"},
{"OpControlBarrier %1 !0 %2", "scope ID is 0"},
{"OpControlBarrier %1 %2 !0", "memory semantics ID is 0"},
{"%import = OpExtInstImport \"GLSL.std.450\" "
"%result = OpExtInst %type %import !999999 %x",
"Invalid extended instruction number: 999999"},
{"%2 = OpSpecConstantOp %1 !1000 %2",
"Invalid OpSpecConstantOp opcode: 1000"},
{"OpCapability !9999", "Invalid capability operand: 9999"},
{"OpSource !9999 100", "Invalid source language operand: 9999"},
{"OpEntryPoint !9999", "Invalid execution model operand: 9999"},
{"OpMemoryModel !9999", "Invalid addressing model operand: 9999"},
{"OpMemoryModel Logical !9999", "Invalid memory model operand: 9999"},
{"OpExecutionMode %1 !9999", "Invalid execution mode operand: 9999"},
{"OpTypeForwardPointer %1 !9999",
"Invalid storage class operand: 9999"},
{"%2 = OpTypeImage %1 !9999", "Invalid dimensionality operand: 9999"},
{"%2 = OpTypeImage %1 1D 0 0 0 0 !9999",
"Invalid image format operand: 9999"},
{"OpDecorate %1 FPRoundingMode !9999",
"Invalid floating-point rounding mode operand: 9999"},
{"OpDecorate %1 LinkageAttributes \"C\" !9999",
"Invalid linkage type operand: 9999"},
{"%1 = OpTypePipe !9999", "Invalid access qualifier operand: 9999"},
{"OpDecorate %1 FuncParamAttr !9999",
"Invalid function parameter attribute operand: 9999"},
{"OpDecorate %1 !9999", "Invalid decoration operand: 9999"},
{"OpDecorate %1 BuiltIn !9999", "Invalid built-in operand: 9999"},
{"%2 = OpGroupIAdd %1 %3 !9999",
"Invalid group operation operand: 9999"},
{"OpDecorate %1 FPFastMathMode !63",
"Invalid floating-point fast math mode operand: 63 has invalid mask "
"component 32"},
{"%2 = OpFunction %2 !31",
"Invalid function control operand: 31 has invalid mask component 16"},
{"OpLoopMerge %1 %2 !1027",
"Invalid loop control operand: 1027 has invalid mask component 1024"},
{"%2 = OpImageFetch %1 %image %coord !32770",
"Invalid image operand: 32770 has invalid mask component 32768"},
{"OpSelectionMerge %1 !7",
"Invalid selection control operand: 7 has invalid mask component 4"},
}));
} // namespace
} // namespace spvtools

View File

@@ -1,32 +0,0 @@
// Copyright (c) 2016 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 "test/unit_spirv.h"
namespace spvtools {
namespace {
TEST(Strnlen, Samples) {
EXPECT_EQ(0u, spv_strnlen_s(nullptr, 0));
EXPECT_EQ(0u, spv_strnlen_s(nullptr, 5));
EXPECT_EQ(0u, spv_strnlen_s("abc", 0));
EXPECT_EQ(1u, spv_strnlen_s("abc", 1));
EXPECT_EQ(3u, spv_strnlen_s("abc", 3));
EXPECT_EQ(3u, spv_strnlen_s("abc\0", 5));
EXPECT_EQ(0u, spv_strnlen_s("\0", 5));
EXPECT_EQ(1u, spv_strnlen_s("a\0c", 5));
}
} // namespace
} // namespace spvtools

View File

@@ -1,76 +0,0 @@
// Copyright (c) 2015-2016 The Khronos Group 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 <utility>
#include <vector>
#include "gmock/gmock.h"
#include "test/test_fixture.h"
#include "test/unit_spirv.h"
namespace spvtools {
namespace {
using ::testing::Eq;
using RoundTripLiteralsTest =
spvtest::TextToBinaryTestBase<::testing::TestWithParam<std::string>>;
TEST_P(RoundTripLiteralsTest, Sample) {
EXPECT_THAT(EncodeAndDecodeSuccessfully(GetParam()), Eq(GetParam()));
}
// clang-format off
INSTANTIATE_TEST_SUITE_P(
StringLiterals, RoundTripLiteralsTest,
::testing::ValuesIn(std::vector<std::string>{
"OpName %1 \"\"\n", // empty
"OpName %1 \"foo\"\n", // normal
"OpName %1 \"foo bar\"\n", // string with spaces
"OpName %1 \"foo\tbar\"\n", // string with tab
"OpName %1 \"\tfoo\"\n", // starts with tab
"OpName %1 \" foo\"\n", // starts with space
"OpName %1 \"foo \"\n", // ends with space
"OpName %1 \"foo\t\"\n", // ends with tab
"OpName %1 \"foo\nbar\"\n", // contains newline
"OpName %1 \"\nfoo\nbar\"\n", // starts with newline
"OpName %1 \"\n\n\nfoo\nbar\"\n", // multiple newlines
"OpName %1 \"\\\"foo\nbar\\\"\"\n", // escaped quote
"OpName %1 \"\\\\foo\nbar\\\\\"\n", // escaped backslash
"OpName %1 \"\xE4\xBA\xB2\"\n", // UTF-8
}));
// clang-format on
using RoundTripSpecialCaseLiteralsTest = spvtest::TextToBinaryTestBase<
::testing::TestWithParam<std::pair<std::string, std::string>>>;
// Test case where the generated disassembly is not the same as the
// assembly passed in.
TEST_P(RoundTripSpecialCaseLiteralsTest, Sample) {
EXPECT_THAT(EncodeAndDecodeSuccessfully(std::get<0>(GetParam())),
Eq(std::get<1>(GetParam())));
}
// clang-format off
INSTANTIATE_TEST_SUITE_P(
StringLiterals, RoundTripSpecialCaseLiteralsTest,
::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
{"OpName %1 \"\\foo\"\n", "OpName %1 \"foo\"\n"}, // Escape f
{"OpName %1 \"\\\nfoo\"\n", "OpName %1 \"\nfoo\"\n"}, // Escape newline
{"OpName %1 \"\\\xE4\xBA\xB2\"\n", "OpName %1 \"\xE4\xBA\xB2\"\n"}, // Escape utf-8
}));
// clang-format on
} // namespace
} // namespace spvtools

View File

@@ -1,561 +0,0 @@
// Copyright (c) 2015-2016 The Khronos Group 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 <sstream>
#include <string>
#include <tuple>
#include <vector>
#include "gmock/gmock.h"
#include "source/spirv_constant.h"
#include "test/test_fixture.h"
#include "test/unit_spirv.h"
namespace spvtools {
namespace {
using spvtest::AutoText;
using spvtest::ScopedContext;
using spvtest::TextToBinaryTest;
using ::testing::Combine;
using ::testing::Eq;
using ::testing::HasSubstr;
class BinaryToText : public ::testing::Test {
public:
BinaryToText()
: context(spvContextCreate(SPV_ENV_UNIVERSAL_1_0)), binary(nullptr) {}
~BinaryToText() {
spvBinaryDestroy(binary);
spvContextDestroy(context);
}
virtual void SetUp() {
const char* textStr = R"(
OpSource OpenCL_C 12
OpMemoryModel Physical64 OpenCL
OpSourceExtension "PlaceholderExtensionName"
OpEntryPoint Kernel %1 "foo"
OpExecutionMode %1 LocalSizeHint 1 1 1
%2 = OpTypeVoid
%3 = OpTypeBool
%4 = OpTypeInt 8 0
%5 = OpTypeInt 8 1
%6 = OpTypeInt 16 0
%7 = OpTypeInt 16 1
%8 = OpTypeInt 32 0
%9 = OpTypeInt 32 1
%10 = OpTypeInt 64 0
%11 = OpTypeInt 64 1
%12 = OpTypeFloat 16
%13 = OpTypeFloat 32
%14 = OpTypeFloat 64
%15 = OpTypeVector %4 2
)";
spv_text_t text = {textStr, strlen(textStr)};
spv_diagnostic diagnostic = nullptr;
spv_result_t error =
spvTextToBinary(context, text.str, text.length, &binary, &diagnostic);
spvDiagnosticPrint(diagnostic);
spvDiagnosticDestroy(diagnostic);
ASSERT_EQ(SPV_SUCCESS, error);
}
virtual void TearDown() {
spvBinaryDestroy(binary);
binary = nullptr;
}
// Compiles the given assembly text, and saves it into 'binary'.
void CompileSuccessfully(std::string text) {
spvBinaryDestroy(binary);
binary = nullptr;
spv_diagnostic diagnostic = nullptr;
EXPECT_EQ(SPV_SUCCESS, spvTextToBinary(context, text.c_str(), text.size(),
&binary, &diagnostic));
}
spv_context context;
spv_binary binary;
};
TEST_F(BinaryToText, Default) {
spv_text text = nullptr;
spv_diagnostic diagnostic = nullptr;
ASSERT_EQ(
SPV_SUCCESS,
spvBinaryToText(context, binary->code, binary->wordCount,
SPV_BINARY_TO_TEXT_OPTION_NONE, &text, &diagnostic));
printf("%s", text->str);
spvTextDestroy(text);
}
TEST_F(BinaryToText, MissingModule) {
spv_text text;
spv_diagnostic diagnostic = nullptr;
EXPECT_EQ(
SPV_ERROR_INVALID_BINARY,
spvBinaryToText(context, nullptr, 42, SPV_BINARY_TO_TEXT_OPTION_NONE,
&text, &diagnostic));
EXPECT_THAT(diagnostic->error, Eq(std::string("Missing module.")));
if (diagnostic) {
spvDiagnosticPrint(diagnostic);
spvDiagnosticDestroy(diagnostic);
}
}
TEST_F(BinaryToText, TruncatedModule) {
// Make a valid module with zero instructions.
CompileSuccessfully("");
EXPECT_EQ(SPV_INDEX_INSTRUCTION, binary->wordCount);
for (size_t length = 0; length < SPV_INDEX_INSTRUCTION; length++) {
spv_text text = nullptr;
spv_diagnostic diagnostic = nullptr;
EXPECT_EQ(
SPV_ERROR_INVALID_BINARY,
spvBinaryToText(context, binary->code, length,
SPV_BINARY_TO_TEXT_OPTION_NONE, &text, &diagnostic));
ASSERT_NE(nullptr, diagnostic);
std::stringstream expected;
expected << "Module has incomplete header: only " << length
<< " words instead of " << SPV_INDEX_INSTRUCTION;
EXPECT_THAT(diagnostic->error, Eq(expected.str()));
spvDiagnosticDestroy(diagnostic);
}
}
TEST_F(BinaryToText, InvalidMagicNumber) {
CompileSuccessfully("");
std::vector<uint32_t> damaged_binary(binary->code,
binary->code + binary->wordCount);
damaged_binary[SPV_INDEX_MAGIC_NUMBER] ^= 123;
spv_diagnostic diagnostic = nullptr;
spv_text text;
EXPECT_EQ(
SPV_ERROR_INVALID_BINARY,
spvBinaryToText(context, damaged_binary.data(), damaged_binary.size(),
SPV_BINARY_TO_TEXT_OPTION_NONE, &text, &diagnostic));
ASSERT_NE(nullptr, diagnostic);
std::stringstream expected;
expected << "Invalid SPIR-V magic number '" << std::hex
<< damaged_binary[SPV_INDEX_MAGIC_NUMBER] << "'.";
EXPECT_THAT(diagnostic->error, Eq(expected.str()));
spvDiagnosticDestroy(diagnostic);
}
struct FailedDecodeCase {
std::string source_text;
std::vector<uint32_t> appended_instruction;
std::string expected_error_message;
};
using BinaryToTextFail =
spvtest::TextToBinaryTestBase<::testing::TestWithParam<FailedDecodeCase>>;
TEST_P(BinaryToTextFail, EncodeSuccessfullyDecodeFailed) {
EXPECT_THAT(EncodeSuccessfullyDecodeFailed(GetParam().source_text,
GetParam().appended_instruction),
Eq(GetParam().expected_error_message));
}
INSTANTIATE_TEST_SUITE_P(
InvalidIds, BinaryToTextFail,
::testing::ValuesIn(std::vector<FailedDecodeCase>{
{"", spvtest::MakeInstruction(SpvOpTypeVoid, {0}),
"Error: Result Id is 0"},
{"", spvtest::MakeInstruction(SpvOpConstant, {0, 1, 42}),
"Error: Type Id is 0"},
{"%1 = OpTypeVoid", spvtest::MakeInstruction(SpvOpTypeVoid, {1}),
"Id 1 is defined more than once"},
{"%1 = OpTypeVoid\n"
"%2 = OpNot %1 %foo",
spvtest::MakeInstruction(SpvOpNot, {1, 2, 3}),
"Id 2 is defined more than once"},
{"%1 = OpTypeVoid\n"
"%2 = OpNot %1 %foo",
spvtest::MakeInstruction(SpvOpNot, {1, 1, 3}),
"Id 1 is defined more than once"},
// The following are the two failure cases for
// Parser::setNumericTypeInfoForType.
{"", spvtest::MakeInstruction(SpvOpConstant, {500, 1, 42}),
"Type Id 500 is not a type"},
{"%1 = OpTypeInt 32 0\n"
"%2 = OpTypeVector %1 4",
spvtest::MakeInstruction(SpvOpConstant, {2, 3, 999}),
"Type Id 2 is not a scalar numeric type"},
}));
INSTANTIATE_TEST_SUITE_P(
InvalidIdsCheckedDuringLiteralCaseParsing, BinaryToTextFail,
::testing::ValuesIn(std::vector<FailedDecodeCase>{
{"", spvtest::MakeInstruction(SpvOpSwitch, {1, 2, 3, 4}),
"Invalid OpSwitch: selector id 1 has no type"},
{"%1 = OpTypeVoid\n",
spvtest::MakeInstruction(SpvOpSwitch, {1, 2, 3, 4}),
"Invalid OpSwitch: selector id 1 is a type, not a value"},
{"%1 = OpConstantTrue !500",
spvtest::MakeInstruction(SpvOpSwitch, {1, 2, 3, 4}),
"Type Id 500 is not a type"},
{"%1 = OpTypeFloat 32\n%2 = OpConstant %1 1.5",
spvtest::MakeInstruction(SpvOpSwitch, {2, 3, 4, 5}),
"Invalid OpSwitch: selector id 2 is not a scalar integer"},
}));
TEST_F(TextToBinaryTest, OneInstruction) {
const std::string input = "OpSource OpenCL_C 12\n";
EXPECT_EQ(input, EncodeAndDecodeSuccessfully(input));
}
// Exercise the case where an operand itself has operands.
// This could detect problems in updating the expected-set-of-operands
// list.
TEST_F(TextToBinaryTest, OperandWithOperands) {
const std::string input = R"(OpEntryPoint Kernel %1 "foo"
OpExecutionMode %1 LocalSizeHint 100 200 300
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%1 = OpFunction %1 None %3
)";
EXPECT_EQ(input, EncodeAndDecodeSuccessfully(input));
}
using RoundTripInstructionsTest = spvtest::TextToBinaryTestBase<
::testing::TestWithParam<std::tuple<spv_target_env, std::string>>>;
TEST_P(RoundTripInstructionsTest, Sample) {
EXPECT_THAT(EncodeAndDecodeSuccessfully(std::get<1>(GetParam()),
SPV_BINARY_TO_TEXT_OPTION_NONE,
std::get<0>(GetParam())),
Eq(std::get<1>(GetParam())));
}
// clang-format off
INSTANTIATE_TEST_SUITE_P(
NumericLiterals, RoundTripInstructionsTest,
// This test is independent of environment, so just test the one.
Combine(::testing::Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1,
SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_3),
::testing::ValuesIn(std::vector<std::string>{
"%1 = OpTypeInt 12 0\n%2 = OpConstant %1 1867\n",
"%1 = OpTypeInt 12 1\n%2 = OpConstant %1 1867\n",
"%1 = OpTypeInt 12 1\n%2 = OpConstant %1 -1867\n",
"%1 = OpTypeInt 32 0\n%2 = OpConstant %1 1867\n",
"%1 = OpTypeInt 32 1\n%2 = OpConstant %1 1867\n",
"%1 = OpTypeInt 32 1\n%2 = OpConstant %1 -1867\n",
"%1 = OpTypeInt 64 0\n%2 = OpConstant %1 18446744073709551615\n",
"%1 = OpTypeInt 64 1\n%2 = OpConstant %1 9223372036854775807\n",
"%1 = OpTypeInt 64 1\n%2 = OpConstant %1 -9223372036854775808\n",
// 16-bit floats print as hex floats.
"%1 = OpTypeFloat 16\n%2 = OpConstant %1 0x1.ff4p+16\n",
"%1 = OpTypeFloat 16\n%2 = OpConstant %1 -0x1.d2cp-10\n",
// 32-bit floats
"%1 = OpTypeFloat 32\n%2 = OpConstant %1 -3.125\n",
"%1 = OpTypeFloat 32\n%2 = OpConstant %1 0x1.8p+128\n", // NaN
"%1 = OpTypeFloat 32\n%2 = OpConstant %1 -0x1.0002p+128\n", // NaN
"%1 = OpTypeFloat 32\n%2 = OpConstant %1 0x1p+128\n", // Inf
"%1 = OpTypeFloat 32\n%2 = OpConstant %1 -0x1p+128\n", // -Inf
// 64-bit floats
"%1 = OpTypeFloat 64\n%2 = OpConstant %1 -3.125\n",
"%1 = OpTypeFloat 64\n%2 = OpConstant %1 0x1.ffffffffffffap-1023\n", // small normal
"%1 = OpTypeFloat 64\n%2 = OpConstant %1 -0x1.ffffffffffffap-1023\n",
"%1 = OpTypeFloat 64\n%2 = OpConstant %1 0x1.8p+1024\n", // NaN
"%1 = OpTypeFloat 64\n%2 = OpConstant %1 -0x1.0002p+1024\n", // NaN
"%1 = OpTypeFloat 64\n%2 = OpConstant %1 0x1p+1024\n", // Inf
"%1 = OpTypeFloat 64\n%2 = OpConstant %1 -0x1p+1024\n", // -Inf
})));
// clang-format on
INSTANTIATE_TEST_SUITE_P(
MemoryAccessMasks, RoundTripInstructionsTest,
Combine(::testing::Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1,
SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_3),
::testing::ValuesIn(std::vector<std::string>{
"OpStore %1 %2\n", // 3 words long.
"OpStore %1 %2 None\n", // 4 words long, explicit final 0.
"OpStore %1 %2 Volatile\n",
"OpStore %1 %2 Aligned 8\n",
"OpStore %1 %2 Nontemporal\n",
// Combinations show the names from LSB to MSB
"OpStore %1 %2 Volatile|Aligned 16\n",
"OpStore %1 %2 Volatile|Nontemporal\n",
"OpStore %1 %2 Volatile|Aligned|Nontemporal 32\n",
})));
INSTANTIATE_TEST_SUITE_P(
FPFastMathModeMasks, RoundTripInstructionsTest,
Combine(
::testing::Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1,
SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_3),
::testing::ValuesIn(std::vector<std::string>{
"OpDecorate %1 FPFastMathMode None\n",
"OpDecorate %1 FPFastMathMode NotNaN\n",
"OpDecorate %1 FPFastMathMode NotInf\n",
"OpDecorate %1 FPFastMathMode NSZ\n",
"OpDecorate %1 FPFastMathMode AllowRecip\n",
"OpDecorate %1 FPFastMathMode Fast\n",
// Combinations show the names from LSB to MSB
"OpDecorate %1 FPFastMathMode NotNaN|NotInf\n",
"OpDecorate %1 FPFastMathMode NSZ|AllowRecip\n",
"OpDecorate %1 FPFastMathMode NotNaN|NotInf|NSZ|AllowRecip|Fast\n",
})));
INSTANTIATE_TEST_SUITE_P(
LoopControlMasks, RoundTripInstructionsTest,
Combine(::testing::Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1,
SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_2),
::testing::ValuesIn(std::vector<std::string>{
"OpLoopMerge %1 %2 None\n",
"OpLoopMerge %1 %2 Unroll\n",
"OpLoopMerge %1 %2 DontUnroll\n",
"OpLoopMerge %1 %2 Unroll|DontUnroll\n",
})));
INSTANTIATE_TEST_SUITE_P(LoopControlMasksV11, RoundTripInstructionsTest,
Combine(::testing::Values(SPV_ENV_UNIVERSAL_1_1,
SPV_ENV_UNIVERSAL_1_2,
SPV_ENV_UNIVERSAL_1_3),
::testing::ValuesIn(std::vector<std::string>{
"OpLoopMerge %1 %2 DependencyInfinite\n",
"OpLoopMerge %1 %2 DependencyLength 8\n",
})));
INSTANTIATE_TEST_SUITE_P(
SelectionControlMasks, RoundTripInstructionsTest,
Combine(::testing::Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1,
SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_2),
::testing::ValuesIn(std::vector<std::string>{
"OpSelectionMerge %1 None\n",
"OpSelectionMerge %1 Flatten\n",
"OpSelectionMerge %1 DontFlatten\n",
"OpSelectionMerge %1 Flatten|DontFlatten\n",
})));
INSTANTIATE_TEST_SUITE_P(
FunctionControlMasks, RoundTripInstructionsTest,
Combine(::testing::Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1,
SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_3),
::testing::ValuesIn(std::vector<std::string>{
"%2 = OpFunction %1 None %3\n",
"%2 = OpFunction %1 Inline %3\n",
"%2 = OpFunction %1 DontInline %3\n",
"%2 = OpFunction %1 Pure %3\n",
"%2 = OpFunction %1 Const %3\n",
"%2 = OpFunction %1 Inline|Pure|Const %3\n",
"%2 = OpFunction %1 DontInline|Const %3\n",
})));
INSTANTIATE_TEST_SUITE_P(
ImageMasks, RoundTripInstructionsTest,
Combine(::testing::Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1,
SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_3),
::testing::ValuesIn(std::vector<std::string>{
"%2 = OpImageFetch %1 %3 %4\n",
"%2 = OpImageFetch %1 %3 %4 None\n",
"%2 = OpImageFetch %1 %3 %4 Bias %5\n",
"%2 = OpImageFetch %1 %3 %4 Lod %5\n",
"%2 = OpImageFetch %1 %3 %4 Grad %5 %6\n",
"%2 = OpImageFetch %1 %3 %4 ConstOffset %5\n",
"%2 = OpImageFetch %1 %3 %4 Offset %5\n",
"%2 = OpImageFetch %1 %3 %4 ConstOffsets %5\n",
"%2 = OpImageFetch %1 %3 %4 Sample %5\n",
"%2 = OpImageFetch %1 %3 %4 MinLod %5\n",
"%2 = OpImageFetch %1 %3 %4 Bias|Lod|Grad %5 %6 %7 %8\n",
"%2 = OpImageFetch %1 %3 %4 ConstOffset|Offset|ConstOffsets"
" %5 %6 %7\n",
"%2 = OpImageFetch %1 %3 %4 Sample|MinLod %5 %6\n",
"%2 = OpImageFetch %1 %3 %4"
" Bias|Lod|Grad|ConstOffset|Offset|ConstOffsets|Sample|MinLod"
" %5 %6 %7 %8 %9 %10 %11 %12 %13\n"})));
INSTANTIATE_TEST_SUITE_P(
NewInstructionsInSPIRV1_2, RoundTripInstructionsTest,
Combine(::testing::Values(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_3),
::testing::ValuesIn(std::vector<std::string>{
"OpExecutionModeId %1 SubgroupsPerWorkgroupId %2\n",
"OpExecutionModeId %1 LocalSizeId %2 %3 %4\n",
"OpExecutionModeId %1 LocalSizeHintId %2\n",
"OpDecorateId %1 AlignmentId %2\n",
"OpDecorateId %1 MaxByteOffsetId %2\n",
})));
using MaskSorting = TextToBinaryTest;
TEST_F(MaskSorting, MasksAreSortedFromLSBToMSB) {
EXPECT_THAT(EncodeAndDecodeSuccessfully(
"OpStore %1 %2 Nontemporal|Aligned|Volatile 32"),
Eq("OpStore %1 %2 Volatile|Aligned|Nontemporal 32\n"));
EXPECT_THAT(
EncodeAndDecodeSuccessfully(
"OpDecorate %1 FPFastMathMode NotInf|Fast|AllowRecip|NotNaN|NSZ"),
Eq("OpDecorate %1 FPFastMathMode NotNaN|NotInf|NSZ|AllowRecip|Fast\n"));
EXPECT_THAT(
EncodeAndDecodeSuccessfully("OpLoopMerge %1 %2 DontUnroll|Unroll"),
Eq("OpLoopMerge %1 %2 Unroll|DontUnroll\n"));
EXPECT_THAT(
EncodeAndDecodeSuccessfully("OpSelectionMerge %1 DontFlatten|Flatten"),
Eq("OpSelectionMerge %1 Flatten|DontFlatten\n"));
EXPECT_THAT(EncodeAndDecodeSuccessfully(
"%2 = OpFunction %1 DontInline|Const|Pure|Inline %3"),
Eq("%2 = OpFunction %1 Inline|DontInline|Pure|Const %3\n"));
EXPECT_THAT(EncodeAndDecodeSuccessfully(
"%2 = OpImageFetch %1 %3 %4"
" MinLod|Sample|Offset|Lod|Grad|ConstOffsets|ConstOffset|Bias"
" %5 %6 %7 %8 %9 %10 %11 %12 %13\n"),
Eq("%2 = OpImageFetch %1 %3 %4"
" Bias|Lod|Grad|ConstOffset|Offset|ConstOffsets|Sample|MinLod"
" %5 %6 %7 %8 %9 %10 %11 %12 %13\n"));
}
using OperandTypeTest = TextToBinaryTest;
TEST_F(OperandTypeTest, OptionalTypedLiteralNumber) {
const std::string input =
"%1 = OpTypeInt 32 0\n"
"%2 = OpConstant %1 42\n"
"OpSwitch %2 %3 100 %4\n";
EXPECT_EQ(input, EncodeAndDecodeSuccessfully(input));
}
using IndentTest = spvtest::TextToBinaryTest;
TEST_F(IndentTest, Sample) {
const std::string input = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
%1 = OpTypeInt 32 0
%2 = OpTypeStruct %1 %3 %4 %5 %6 %7 %8 %9 %10 ; force IDs into double digits
%11 = OpConstant %1 42
OpStore %2 %3 Aligned|Volatile 4 ; bogus, but not indented
)";
const std::string expected =
R"( OpCapability Shader
OpMemoryModel Logical GLSL450
%1 = OpTypeInt 32 0
%2 = OpTypeStruct %1 %3 %4 %5 %6 %7 %8 %9 %10
%11 = OpConstant %1 42
OpStore %2 %3 Volatile|Aligned 4
)";
EXPECT_THAT(
EncodeAndDecodeSuccessfully(input, SPV_BINARY_TO_TEXT_OPTION_INDENT),
expected);
}
using FriendlyNameDisassemblyTest = spvtest::TextToBinaryTest;
TEST_F(FriendlyNameDisassemblyTest, Sample) {
const std::string input = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
%1 = OpTypeInt 32 0
%2 = OpTypeStruct %1 %3 %4 %5 %6 %7 %8 %9 %10 ; force IDs into double digits
%11 = OpConstant %1 42
)";
const std::string expected =
R"(OpCapability Shader
OpMemoryModel Logical GLSL450
%uint = OpTypeInt 32 0
%_struct_2 = OpTypeStruct %uint %3 %4 %5 %6 %7 %8 %9 %10
%uint_42 = OpConstant %uint 42
)";
EXPECT_THAT(EncodeAndDecodeSuccessfully(
input, SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES),
expected);
}
TEST_F(TextToBinaryTest, ShowByteOffsetsWhenRequested) {
const std::string input = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
%1 = OpTypeInt 32 0
%2 = OpTypeVoid
)";
const std::string expected =
R"(OpCapability Shader ; 0x00000014
OpMemoryModel Logical GLSL450 ; 0x0000001c
%1 = OpTypeInt 32 0 ; 0x00000028
%2 = OpTypeVoid ; 0x00000038
)";
EXPECT_THAT(EncodeAndDecodeSuccessfully(
input, SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET),
expected);
}
// Test version string.
TEST_F(TextToBinaryTest, VersionString) {
auto words = CompileSuccessfully("");
spv_text decoded_text = nullptr;
EXPECT_THAT(spvBinaryToText(ScopedContext().context, words.data(),
words.size(), SPV_BINARY_TO_TEXT_OPTION_NONE,
&decoded_text, &diagnostic),
Eq(SPV_SUCCESS));
EXPECT_EQ(nullptr, diagnostic);
EXPECT_THAT(decoded_text->str, HasSubstr("Version: 1.0\n"))
<< EncodeAndDecodeSuccessfully("");
spvTextDestroy(decoded_text);
}
// Test generator string.
// A test case for the generator string. This allows us to
// test both of the 16-bit components of the generator word.
struct GeneratorStringCase {
uint16_t generator;
uint16_t misc;
std::string expected;
};
using GeneratorStringTest = spvtest::TextToBinaryTestBase<
::testing::TestWithParam<GeneratorStringCase>>;
TEST_P(GeneratorStringTest, Sample) {
auto words = CompileSuccessfully("");
EXPECT_EQ(2u, SPV_INDEX_GENERATOR_NUMBER);
words[SPV_INDEX_GENERATOR_NUMBER] =
SPV_GENERATOR_WORD(GetParam().generator, GetParam().misc);
spv_text decoded_text = nullptr;
EXPECT_THAT(spvBinaryToText(ScopedContext().context, words.data(),
words.size(), SPV_BINARY_TO_TEXT_OPTION_NONE,
&decoded_text, &diagnostic),
Eq(SPV_SUCCESS));
EXPECT_THAT(diagnostic, Eq(nullptr));
EXPECT_THAT(std::string(decoded_text->str), HasSubstr(GetParam().expected));
spvTextDestroy(decoded_text);
}
INSTANTIATE_TEST_SUITE_P(GeneratorStrings, GeneratorStringTest,
::testing::ValuesIn(std::vector<GeneratorStringCase>{
{SPV_GENERATOR_KHRONOS, 12, "Khronos; 12"},
{SPV_GENERATOR_LUNARG, 99, "LunarG; 99"},
{SPV_GENERATOR_VALVE, 1, "Valve; 1"},
{SPV_GENERATOR_CODEPLAY, 65535, "Codeplay; 65535"},
{SPV_GENERATOR_NVIDIA, 19, "NVIDIA; 19"},
{SPV_GENERATOR_ARM, 1000, "ARM; 1000"},
{SPV_GENERATOR_KHRONOS_LLVM_TRANSLATOR, 38,
"Khronos LLVM/SPIR-V Translator; 38"},
{SPV_GENERATOR_KHRONOS_ASSEMBLER, 2,
"Khronos SPIR-V Tools Assembler; 2"},
{SPV_GENERATOR_KHRONOS_GLSLANG, 1,
"Khronos Glslang Reference Front End; 1"},
{1000, 18, "Unknown(1000); 18"},
{65535, 32767, "Unknown(65535); 32767"},
}));
// TODO(dneto): Test new instructions and enums in SPIR-V 1.3
} // namespace
} // namespace spvtools

View File

@@ -1,299 +0,0 @@
// Copyright (c) 2016 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 "gtest/gtest.h"
#include "source/table.h"
#include "spirv-tools/libspirv.h"
namespace spvtools {
namespace {
// TODO(antiagainst): Use public C API for setting the consumer once exists.
#ifndef SPIRV_TOOLS_SHAREDLIB
void SetContextMessageConsumer(spv_context context, MessageConsumer consumer) {
spvtools::SetContextMessageConsumer(context, consumer);
}
#else
void SetContextMessageConsumer(spv_context, MessageConsumer) {}
#endif
// The default consumer is a null std::function.
TEST(CInterface, DefaultConsumerNullDiagnosticForValidInput) {
auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1);
const char input_text[] =
"OpCapability Shader\n"
"OpCapability Linkage\n"
"OpMemoryModel Logical GLSL450";
spv_binary binary = nullptr;
EXPECT_EQ(SPV_SUCCESS, spvTextToBinary(context, input_text,
sizeof(input_text), &binary, nullptr));
{
// Sadly the compiler don't allow me to feed binary directly to
// spvValidate().
spv_const_binary_t b{binary->code, binary->wordCount};
EXPECT_EQ(SPV_SUCCESS, spvValidate(context, &b, nullptr));
}
spv_text text = nullptr;
EXPECT_EQ(SPV_SUCCESS, spvBinaryToText(context, binary->code,
binary->wordCount, 0, &text, nullptr));
spvTextDestroy(text);
spvBinaryDestroy(binary);
spvContextDestroy(context);
}
// The default consumer is a null std::function.
TEST(CInterface, DefaultConsumerNullDiagnosticForInvalidAssembling) {
auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1);
const char input_text[] = "%1 = OpName";
spv_binary binary = nullptr;
EXPECT_EQ(SPV_ERROR_INVALID_TEXT,
spvTextToBinary(context, input_text, sizeof(input_text), &binary,
nullptr));
spvBinaryDestroy(binary);
spvContextDestroy(context);
}
// The default consumer is a null std::function.
TEST(CInterface, DefaultConsumerNullDiagnosticForInvalidDiassembling) {
auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1);
const char input_text[] = "OpNop";
spv_binary binary = nullptr;
ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(context, input_text,
sizeof(input_text), &binary, nullptr));
// Change OpNop to an invalid (wordcount|opcode) word.
binary->code[binary->wordCount - 1] = 0xffffffff;
spv_text text = nullptr;
EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
spvBinaryToText(context, binary->code, binary->wordCount, 0, &text,
nullptr));
spvTextDestroy(text);
spvBinaryDestroy(binary);
spvContextDestroy(context);
}
// The default consumer is a null std::function.
TEST(CInterface, DefaultConsumerNullDiagnosticForInvalidValidating) {
auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1);
const char input_text[] = "OpNop";
spv_binary binary = nullptr;
ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(context, input_text,
sizeof(input_text), &binary, nullptr));
spv_const_binary_t b{binary->code, binary->wordCount};
EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, spvValidate(context, &b, nullptr));
spvBinaryDestroy(binary);
spvContextDestroy(context);
}
TEST(CInterface, SpecifyConsumerNullDiagnosticForAssembling) {
const char input_text[] = " OpName\n";
auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1);
int invocation = 0;
SetContextMessageConsumer(
context,
[&invocation](spv_message_level_t level, const char* source,
const spv_position_t& position, const char* message) {
++invocation;
EXPECT_EQ(SPV_MSG_ERROR, level);
// The error happens at scanning the begining of second line.
EXPECT_STREQ("input", source);
EXPECT_EQ(1u, position.line);
EXPECT_EQ(0u, position.column);
EXPECT_EQ(12u, position.index);
EXPECT_STREQ("Expected operand, found end of stream.", message);
});
spv_binary binary = nullptr;
EXPECT_EQ(SPV_ERROR_INVALID_TEXT,
spvTextToBinary(context, input_text, sizeof(input_text), &binary,
nullptr));
#ifndef SPIRV_TOOLS_SHAREDLIB
EXPECT_EQ(1, invocation);
#endif
spvBinaryDestroy(binary);
spvContextDestroy(context);
}
TEST(CInterface, SpecifyConsumerNullDiagnosticForDisassembling) {
const char input_text[] = "OpNop";
auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1);
int invocation = 0;
SetContextMessageConsumer(
context,
[&invocation](spv_message_level_t level, const char* source,
const spv_position_t& position, const char* message) {
++invocation;
EXPECT_EQ(SPV_MSG_ERROR, level);
EXPECT_STREQ("input", source);
EXPECT_EQ(0u, position.line);
EXPECT_EQ(0u, position.column);
EXPECT_EQ(1u, position.index);
EXPECT_STREQ("Invalid opcode: 65535", message);
});
spv_binary binary = nullptr;
ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(context, input_text,
sizeof(input_text), &binary, nullptr));
// Change OpNop to an invalid (wordcount|opcode) word.
binary->code[binary->wordCount - 1] = 0xffffffff;
spv_text text = nullptr;
EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
spvBinaryToText(context, binary->code, binary->wordCount, 0, &text,
nullptr));
#ifndef SPIRV_TOOLS_SHAREDLIB
EXPECT_EQ(1, invocation);
#endif
spvTextDestroy(text);
spvBinaryDestroy(binary);
spvContextDestroy(context);
}
TEST(CInterface, SpecifyConsumerNullDiagnosticForValidating) {
const char input_text[] = "OpNop";
auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1);
int invocation = 0;
SetContextMessageConsumer(
context,
[&invocation](spv_message_level_t level, const char* source,
const spv_position_t& position, const char* message) {
++invocation;
EXPECT_EQ(SPV_MSG_ERROR, level);
EXPECT_STREQ("input", source);
EXPECT_EQ(0u, position.line);
EXPECT_EQ(0u, position.column);
// TODO(antiagainst): what validation reports is not a word offset here.
// It is inconsistent with diassembler. Should be fixed.
EXPECT_EQ(1u, position.index);
EXPECT_STREQ(
"Nop cannot appear before the memory model instruction\n"
" OpNop\n",
message);
});
spv_binary binary = nullptr;
ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(context, input_text,
sizeof(input_text), &binary, nullptr));
spv_const_binary_t b{binary->code, binary->wordCount};
EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, spvValidate(context, &b, nullptr));
#ifndef SPIRV_TOOLS_SHAREDLIB
EXPECT_EQ(1, invocation);
#endif
spvBinaryDestroy(binary);
spvContextDestroy(context);
}
// When having both a consumer and an diagnostic object, the diagnostic object
// should take priority.
TEST(CInterface, SpecifyConsumerSpecifyDiagnosticForAssembling) {
const char input_text[] = " OpName";
auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1);
int invocation = 0;
SetContextMessageConsumer(
context,
[&invocation](spv_message_level_t, const char*, const spv_position_t&,
const char*) { ++invocation; });
spv_binary binary = nullptr;
spv_diagnostic diagnostic = nullptr;
EXPECT_EQ(SPV_ERROR_INVALID_TEXT,
spvTextToBinary(context, input_text, sizeof(input_text), &binary,
&diagnostic));
EXPECT_EQ(0, invocation); // Consumer should not be invoked at all.
EXPECT_STREQ("Expected operand, found end of stream.", diagnostic->error);
spvDiagnosticDestroy(diagnostic);
spvBinaryDestroy(binary);
spvContextDestroy(context);
}
TEST(CInterface, SpecifyConsumerSpecifyDiagnosticForDisassembling) {
const char input_text[] = "OpNop";
auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1);
int invocation = 0;
SetContextMessageConsumer(
context,
[&invocation](spv_message_level_t, const char*, const spv_position_t&,
const char*) { ++invocation; });
spv_binary binary = nullptr;
ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(context, input_text,
sizeof(input_text), &binary, nullptr));
// Change OpNop to an invalid (wordcount|opcode) word.
binary->code[binary->wordCount - 1] = 0xffffffff;
spv_diagnostic diagnostic = nullptr;
spv_text text = nullptr;
EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
spvBinaryToText(context, binary->code, binary->wordCount, 0, &text,
&diagnostic));
EXPECT_EQ(0, invocation); // Consumer should not be invoked at all.
EXPECT_STREQ("Invalid opcode: 65535", diagnostic->error);
spvTextDestroy(text);
spvDiagnosticDestroy(diagnostic);
spvBinaryDestroy(binary);
spvContextDestroy(context);
}
TEST(CInterface, SpecifyConsumerSpecifyDiagnosticForValidating) {
const char input_text[] = "OpNop";
auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1);
int invocation = 0;
SetContextMessageConsumer(
context,
[&invocation](spv_message_level_t, const char*, const spv_position_t&,
const char*) { ++invocation; });
spv_binary binary = nullptr;
ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(context, input_text,
sizeof(input_text), &binary, nullptr));
spv_diagnostic diagnostic = nullptr;
spv_const_binary_t b{binary->code, binary->wordCount};
EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, spvValidate(context, &b, &diagnostic));
EXPECT_EQ(0, invocation); // Consumer should not be invoked at all.
EXPECT_STREQ(
"Nop cannot appear before the memory model instruction\n"
" OpNop\n",
diagnostic->error);
spvDiagnosticDestroy(diagnostic);
spvBinaryDestroy(binary);
spvContextDestroy(context);
}
} // namespace
} // namespace spvtools

View File

@@ -1,51 +0,0 @@
// Copyright (c) 2015-2016 The Khronos Group 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 "gmock/gmock.h"
#include "source/util/string_utils.h"
#include "test/test_fixture.h"
#include "test/unit_spirv.h"
namespace spvtools {
namespace {
using spvtest::Concatenate;
using spvtest::MakeInstruction;
using utils::MakeVector;
using spvtest::TextToBinaryTest;
using testing::Eq;
TEST_F(TextToBinaryTest, Whitespace) {
std::string input = R"(
; I'm a proud comment at the beginning of the file
; I hide: OpCapability Shader
OpMemoryModel Logical Simple ; comment after instruction
;;;;;;;; many ;'s
%glsl450 = OpExtInstImport "GLSL.std.450"
; comment indented
)";
EXPECT_THAT(
CompiledInstructions(input),
Eq(Concatenate({MakeInstruction(SpvOpMemoryModel,
{uint32_t(SpvAddressingModelLogical),
uint32_t(SpvMemoryModelSimple)}),
MakeInstruction(SpvOpExtInstImport, {1},
MakeVector("GLSL.std.450"))})));
}
} // namespace
} // namespace spvtools

View File

@@ -1,328 +0,0 @@
// Copyright (c) 2016 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 <utility>
#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "spirv-tools/optimizer.hpp"
#include "spirv/1.1/spirv.h"
namespace spvtools {
namespace {
using ::testing::ContainerEq;
using ::testing::HasSubstr;
// 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
)";
}
// When we assemble with a target environment of SPIR-V 1.1, we expect
// the following in the module header version word.
const uint32_t kExpectedSpvVersion = 0x10100;
TEST(CppInterface, SuccessfulRoundTrip) {
const std::string input_text = "%2 = OpSizeOf %1 %3\n";
SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
std::vector<uint32_t> binary;
EXPECT_TRUE(t.Assemble(input_text, &binary));
EXPECT_TRUE(binary.size() > 5u);
EXPECT_EQ(SpvMagicNumber, binary[0]);
EXPECT_EQ(kExpectedSpvVersion, binary[1]);
// This cannot pass validation since %1 is not defined.
t.SetMessageConsumer([](spv_message_level_t level, const char* source,
const spv_position_t& position, const char* message) {
EXPECT_EQ(SPV_MSG_ERROR, level);
EXPECT_STREQ("input", source);
EXPECT_EQ(0u, position.line);
EXPECT_EQ(0u, position.column);
EXPECT_EQ(1u, position.index);
EXPECT_STREQ("ID 1[%1] has not been defined\n %2 = OpSizeOf %1 %3\n",
message);
});
EXPECT_FALSE(t.Validate(binary));
std::string output_text;
EXPECT_TRUE(t.Disassemble(binary, &output_text));
EXPECT_EQ(input_text, output_text);
}
TEST(CppInterface, AssembleEmptyModule) {
std::vector<uint32_t> binary(10, 42);
SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
EXPECT_TRUE(t.Assemble("", &binary));
// We only have the header.
EXPECT_EQ(5u, binary.size());
EXPECT_EQ(SpvMagicNumber, binary[0]);
EXPECT_EQ(kExpectedSpvVersion, binary[1]);
}
TEST(CppInterface, AssembleOverloads) {
const std::string input_text = "%2 = OpSizeOf %1 %3\n";
SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
{
std::vector<uint32_t> binary;
EXPECT_TRUE(t.Assemble(input_text, &binary));
EXPECT_TRUE(binary.size() > 5u);
EXPECT_EQ(SpvMagicNumber, binary[0]);
EXPECT_EQ(kExpectedSpvVersion, binary[1]);
}
{
std::vector<uint32_t> binary;
EXPECT_TRUE(t.Assemble(input_text.data(), input_text.size(), &binary));
EXPECT_TRUE(binary.size() > 5u);
EXPECT_EQ(SpvMagicNumber, binary[0]);
EXPECT_EQ(kExpectedSpvVersion, binary[1]);
}
{ // Ignore the last newline.
std::vector<uint32_t> binary;
EXPECT_TRUE(t.Assemble(input_text.data(), input_text.size() - 1, &binary));
EXPECT_TRUE(binary.size() > 5u);
EXPECT_EQ(SpvMagicNumber, binary[0]);
EXPECT_EQ(kExpectedSpvVersion, binary[1]);
}
}
TEST(CppInterface, DisassembleEmptyModule) {
std::string text(10, 'x');
SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
int invocation_count = 0;
t.SetMessageConsumer(
[&invocation_count](spv_message_level_t level, const char* source,
const spv_position_t& position, const char* message) {
++invocation_count;
EXPECT_EQ(SPV_MSG_ERROR, level);
EXPECT_STREQ("input", source);
EXPECT_EQ(0u, position.line);
EXPECT_EQ(0u, position.column);
EXPECT_EQ(0u, position.index);
EXPECT_STREQ("Missing module.", message);
});
EXPECT_FALSE(t.Disassemble({}, &text));
EXPECT_EQ("xxxxxxxxxx", text); // The original string is unmodified.
EXPECT_EQ(1, invocation_count);
}
TEST(CppInterface, DisassembleOverloads) {
const std::string input_text = "%2 = OpSizeOf %1 %3\n";
SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
std::vector<uint32_t> binary;
EXPECT_TRUE(t.Assemble(input_text, &binary));
{
std::string output_text;
EXPECT_TRUE(t.Disassemble(binary, &output_text));
EXPECT_EQ(input_text, output_text);
}
{
std::string output_text;
EXPECT_TRUE(t.Disassemble(binary.data(), binary.size(), &output_text));
EXPECT_EQ(input_text, output_text);
}
}
TEST(CppInterface, SuccessfulValidation) {
SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
int invocation_count = 0;
t.SetMessageConsumer([&invocation_count](spv_message_level_t, const char*,
const spv_position_t&, const char*) {
++invocation_count;
});
std::vector<uint32_t> binary;
EXPECT_TRUE(t.Assemble(Header(), &binary));
EXPECT_TRUE(t.Validate(binary));
EXPECT_EQ(0, invocation_count);
}
TEST(CppInterface, ValidateOverloads) {
SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
std::vector<uint32_t> binary;
EXPECT_TRUE(t.Assemble(Header(), &binary));
{ EXPECT_TRUE(t.Validate(binary)); }
{ EXPECT_TRUE(t.Validate(binary.data(), binary.size())); }
}
TEST(CppInterface, ValidateEmptyModule) {
SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
int invocation_count = 0;
t.SetMessageConsumer(
[&invocation_count](spv_message_level_t level, const char* source,
const spv_position_t& position, const char* message) {
++invocation_count;
EXPECT_EQ(SPV_MSG_ERROR, level);
EXPECT_STREQ("input", source);
EXPECT_EQ(0u, position.line);
EXPECT_EQ(0u, position.column);
EXPECT_EQ(0u, position.index);
EXPECT_STREQ("Invalid SPIR-V magic number.", message);
});
EXPECT_FALSE(t.Validate({}));
EXPECT_EQ(1, invocation_count);
}
// Returns the assembly for a SPIR-V module with a struct declaration
// with the given number of members.
std::string MakeModuleHavingStruct(int num_members) {
std::stringstream os;
os << Header();
os << R"(%1 = OpTypeInt 32 0
%2 = OpTypeStruct)";
for (int i = 0; i < num_members; i++) os << " %1";
return os.str();
}
TEST(CppInterface, ValidateWithOptionsPass) {
SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
std::vector<uint32_t> binary;
EXPECT_TRUE(t.Assemble(MakeModuleHavingStruct(10), &binary));
const ValidatorOptions opts;
EXPECT_TRUE(t.Validate(binary.data(), binary.size(), opts));
}
TEST(CppInterface, ValidateWithOptionsFail) {
SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
std::vector<uint32_t> binary;
EXPECT_TRUE(t.Assemble(MakeModuleHavingStruct(10), &binary));
ValidatorOptions opts;
opts.SetUniversalLimit(spv_validator_limit_max_struct_members, 9);
std::stringstream os;
t.SetMessageConsumer([&os](spv_message_level_t, const char*,
const spv_position_t&,
const char* message) { os << message; });
EXPECT_FALSE(t.Validate(binary.data(), binary.size(), opts));
EXPECT_THAT(
os.str(),
HasSubstr(
"Number of OpTypeStruct members (10) has exceeded the limit (9)"));
}
// Checks that after running the given optimizer |opt| on the given |original|
// source code, we can get the given |optimized| source code.
void CheckOptimization(const std::string& original,
const std::string& optimized, const Optimizer& opt) {
SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
std::vector<uint32_t> original_binary;
ASSERT_TRUE(t.Assemble(original, &original_binary));
std::vector<uint32_t> optimized_binary;
EXPECT_TRUE(opt.Run(original_binary.data(), original_binary.size(),
&optimized_binary));
std::string optimized_text;
EXPECT_TRUE(t.Disassemble(optimized_binary, &optimized_text));
EXPECT_EQ(optimized, optimized_text);
}
TEST(CppInterface, OptimizeEmptyModule) {
SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
std::vector<uint32_t> binary;
EXPECT_TRUE(t.Assemble("", &binary));
Optimizer o(SPV_ENV_UNIVERSAL_1_1);
o.RegisterPass(CreateStripDebugInfoPass());
// Fails to validate.
EXPECT_FALSE(o.Run(binary.data(), binary.size(), &binary));
}
TEST(CppInterface, OptimizeModifiedModule) {
Optimizer o(SPV_ENV_UNIVERSAL_1_1);
o.RegisterPass(CreateStripDebugInfoPass());
CheckOptimization(Header() + "OpSource GLSL 450", Header(), o);
}
TEST(CppInterface, OptimizeMulitplePasses) {
std::string original_text = Header() +
"OpSource GLSL 450 "
"OpDecorate %true SpecId 1 "
"%bool = OpTypeBool "
"%true = OpSpecConstantTrue %bool";
Optimizer o(SPV_ENV_UNIVERSAL_1_1);
o.RegisterPass(CreateStripDebugInfoPass())
.RegisterPass(CreateFreezeSpecConstantValuePass());
std::string expected_text = Header() +
"%bool = OpTypeBool\n"
"%true = OpConstantTrue %bool\n";
CheckOptimization(original_text, expected_text, o);
}
TEST(CppInterface, OptimizeDoNothingWithPassToken) {
CreateFreezeSpecConstantValuePass();
auto token = CreateUnifyConstantPass();
}
TEST(CppInterface, OptimizeReassignPassToken) {
auto token = CreateNullPass();
token = CreateStripDebugInfoPass();
CheckOptimization(
Header() + "OpSource GLSL 450", Header(),
Optimizer(SPV_ENV_UNIVERSAL_1_1).RegisterPass(std::move(token)));
}
TEST(CppInterface, OptimizeMoveConstructPassToken) {
auto token1 = CreateStripDebugInfoPass();
Optimizer::PassToken token2(std::move(token1));
CheckOptimization(
Header() + "OpSource GLSL 450", Header(),
Optimizer(SPV_ENV_UNIVERSAL_1_1).RegisterPass(std::move(token2)));
}
TEST(CppInterface, OptimizeMoveAssignPassToken) {
auto token1 = CreateStripDebugInfoPass();
auto token2 = CreateNullPass();
token2 = std::move(token1);
CheckOptimization(
Header() + "OpSource GLSL 450", Header(),
Optimizer(SPV_ENV_UNIVERSAL_1_1).RegisterPass(std::move(token2)));
}
TEST(CppInterface, OptimizeSameAddressForOriginalOptimizedBinary) {
SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
std::vector<uint32_t> binary;
ASSERT_TRUE(t.Assemble(Header() + "OpSource GLSL 450", &binary));
EXPECT_TRUE(Optimizer(SPV_ENV_UNIVERSAL_1_1)
.RegisterPass(CreateStripDebugInfoPass())
.Run(binary.data(), binary.size(), &binary));
std::string optimized_text;
EXPECT_TRUE(t.Disassemble(binary, &optimized_text));
EXPECT_EQ(Header(), optimized_text);
}
// TODO(antiagainst): tests for SetMessageConsumer().
} // namespace
} // namespace spvtools

View File

@@ -1,150 +0,0 @@
// Copyright (c) 2015-2016 The Khronos Group 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 <algorithm>
#include <sstream>
#include <utility>
#include "gmock/gmock.h"
#include "test/unit_spirv.h"
namespace spvtools {
namespace {
using ::testing::Eq;
// Returns a newly created diagnostic value.
spv_diagnostic MakeValidDiagnostic() {
spv_position_t position = {};
spv_diagnostic diagnostic = spvDiagnosticCreate(&position, "");
EXPECT_NE(nullptr, diagnostic);
return diagnostic;
}
TEST(Diagnostic, DestroyNull) { spvDiagnosticDestroy(nullptr); }
TEST(Diagnostic, DestroyValidDiagnostic) {
spv_diagnostic diagnostic = MakeValidDiagnostic();
spvDiagnosticDestroy(diagnostic);
// We aren't allowed to use the diagnostic pointer anymore.
// So we can't test its behaviour.
}
TEST(Diagnostic, DestroyValidDiagnosticAfterReassignment) {
spv_diagnostic diagnostic = MakeValidDiagnostic();
spv_diagnostic second_diagnostic = MakeValidDiagnostic();
EXPECT_TRUE(diagnostic != second_diagnostic);
spvDiagnosticDestroy(diagnostic);
diagnostic = second_diagnostic;
spvDiagnosticDestroy(diagnostic);
}
TEST(Diagnostic, PrintDefault) {
char message[] = "Test Diagnostic!";
spv_diagnostic_t diagnostic = {{2, 3, 5}, message};
// TODO: Redirect stderr
ASSERT_EQ(SPV_SUCCESS, spvDiagnosticPrint(&diagnostic));
// TODO: Validate the output of spvDiagnosticPrint()
// TODO: Remove the redirection of stderr
}
TEST(Diagnostic, PrintInvalidDiagnostic) {
ASSERT_EQ(SPV_ERROR_INVALID_DIAGNOSTIC, spvDiagnosticPrint(nullptr));
}
// TODO(dneto): We should be able to redirect the diagnostic printing.
// Once we do that, we can test diagnostic corner cases.
TEST(DiagnosticStream, ConversionToResultType) {
// Check after the DiagnosticStream object is destroyed.
spv_result_t value;
{ value = DiagnosticStream({}, nullptr, "", SPV_ERROR_INVALID_TEXT); }
EXPECT_EQ(SPV_ERROR_INVALID_TEXT, value);
// Check implicit conversion via plain assignment.
value = DiagnosticStream({}, nullptr, "", SPV_SUCCESS);
EXPECT_EQ(SPV_SUCCESS, value);
// Check conversion via constructor.
EXPECT_EQ(SPV_FAILED_MATCH,
spv_result_t(DiagnosticStream({}, nullptr, "", SPV_FAILED_MATCH)));
}
TEST(
DiagnosticStream,
MoveConstructorPreservesPreviousMessagesAndPreventsOutputFromExpiringValue) {
std::ostringstream messages;
int message_count = 0;
auto consumer = [&messages, &message_count](spv_message_level_t, const char*,
const spv_position_t&,
const char* msg) {
message_count++;
messages << msg;
};
// Enclose the DiagnosticStream variables in a scope to force destruction.
{
DiagnosticStream ds0({}, consumer, "", SPV_ERROR_INVALID_BINARY);
ds0 << "First";
DiagnosticStream ds1(std::move(ds0));
ds1 << "Second";
}
EXPECT_THAT(message_count, Eq(1));
EXPECT_THAT(messages.str(), Eq("FirstSecond"));
}
TEST(DiagnosticStream, MoveConstructorCanBeDirectlyShiftedTo) {
std::ostringstream messages;
int message_count = 0;
auto consumer = [&messages, &message_count](spv_message_level_t, const char*,
const spv_position_t&,
const char* msg) {
message_count++;
messages << msg;
};
// Enclose the DiagnosticStream variables in a scope to force destruction.
{
DiagnosticStream ds0({}, consumer, "", SPV_ERROR_INVALID_BINARY);
ds0 << "First";
std::move(ds0) << "Second";
}
EXPECT_THAT(message_count, Eq(1));
EXPECT_THAT(messages.str(), Eq("FirstSecond"));
}
TEST(DiagnosticStream, DiagnosticFromLambdaReturnCanStillBeUsed) {
std::ostringstream messages;
int message_count = 0;
auto consumer = [&messages, &message_count](spv_message_level_t, const char*,
const spv_position_t&,
const char* msg) {
message_count++;
messages << msg;
};
{
auto emitter = [&consumer]() -> DiagnosticStream {
DiagnosticStream ds0({}, consumer, "", SPV_ERROR_INVALID_BINARY);
ds0 << "First";
return ds0;
};
emitter() << "Second";
}
EXPECT_THAT(message_count, Eq(1));
EXPECT_THAT(messages.str(), Eq("FirstSecond"));
}
} // namespace
} // namespace spvtools

View File

@@ -1,290 +0,0 @@
// Copyright (c) 2016 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 <algorithm>
#include <utility>
#include <vector>
#include "gmock/gmock.h"
#include "source/enum_set.h"
#include "test/unit_spirv.h"
namespace spvtools {
namespace {
using spvtest::ElementsIn;
using ::testing::Eq;
using ::testing::ValuesIn;
TEST(EnumSet, IsEmpty1) {
EnumSet<uint32_t> set;
EXPECT_TRUE(set.IsEmpty());
set.Add(0);
EXPECT_FALSE(set.IsEmpty());
}
TEST(EnumSet, IsEmpty2) {
EnumSet<uint32_t> set;
EXPECT_TRUE(set.IsEmpty());
set.Add(150);
EXPECT_FALSE(set.IsEmpty());
}
TEST(EnumSet, IsEmpty3) {
EnumSet<uint32_t> set(4);
EXPECT_FALSE(set.IsEmpty());
}
TEST(EnumSet, IsEmpty4) {
EnumSet<uint32_t> set(300);
EXPECT_FALSE(set.IsEmpty());
}
TEST(EnumSetHasAnyOf, EmptySetEmptyQuery) {
const EnumSet<uint32_t> set;
const EnumSet<uint32_t> empty;
EXPECT_TRUE(set.HasAnyOf(empty));
EXPECT_TRUE(EnumSet<uint32_t>().HasAnyOf(EnumSet<uint32_t>()));
}
TEST(EnumSetHasAnyOf, MaskSetEmptyQuery) {
EnumSet<uint32_t> set;
const EnumSet<uint32_t> empty;
set.Add(5);
set.Add(8);
EXPECT_TRUE(set.HasAnyOf(empty));
}
TEST(EnumSetHasAnyOf, OverflowSetEmptyQuery) {
EnumSet<uint32_t> set;
const EnumSet<uint32_t> empty;
set.Add(200);
set.Add(300);
EXPECT_TRUE(set.HasAnyOf(empty));
}
TEST(EnumSetHasAnyOf, EmptyQuery) {
EnumSet<uint32_t> set;
const EnumSet<uint32_t> empty;
set.Add(5);
set.Add(8);
set.Add(200);
set.Add(300);
EXPECT_TRUE(set.HasAnyOf(empty));
}
TEST(EnumSetHasAnyOf, EmptyQueryAlwaysTrue) {
EnumSet<uint32_t> set;
const EnumSet<uint32_t> empty;
EXPECT_TRUE(set.HasAnyOf(empty));
set.Add(5);
EXPECT_TRUE(set.HasAnyOf(empty));
EXPECT_TRUE(EnumSet<uint32_t>(100).HasAnyOf(EnumSet<uint32_t>()));
}
TEST(EnumSetHasAnyOf, ReflexiveMask) {
EnumSet<uint32_t> set(3);
set.Add(24);
set.Add(30);
EXPECT_TRUE(set.HasAnyOf(set));
}
TEST(EnumSetHasAnyOf, ReflexiveOverflow) {
EnumSet<uint32_t> set(200);
set.Add(300);
set.Add(400);
EXPECT_TRUE(set.HasAnyOf(set));
}
TEST(EnumSetHasAnyOf, Reflexive) {
EnumSet<uint32_t> set(3);
set.Add(24);
set.Add(300);
set.Add(400);
EXPECT_TRUE(set.HasAnyOf(set));
}
TEST(EnumSetHasAnyOf, EmptySetHasNone) {
EnumSet<uint32_t> set;
EnumSet<uint32_t> items;
for (uint32_t i = 0; i < 200; ++i) {
items.Add(i);
EXPECT_FALSE(set.HasAnyOf(items));
EXPECT_FALSE(set.HasAnyOf(EnumSet<uint32_t>(i)));
}
}
TEST(EnumSetHasAnyOf, MaskSetMaskQuery) {
EnumSet<uint32_t> set(0);
EnumSet<uint32_t> items(1);
EXPECT_FALSE(set.HasAnyOf(items));
set.Add(2);
items.Add(3);
EXPECT_FALSE(set.HasAnyOf(items));
set.Add(3);
EXPECT_TRUE(set.HasAnyOf(items));
set.Add(4);
EXPECT_TRUE(set.HasAnyOf(items));
}
TEST(EnumSetHasAnyOf, OverflowSetOverflowQuery) {
EnumSet<uint32_t> set(100);
EnumSet<uint32_t> items(200);
EXPECT_FALSE(set.HasAnyOf(items));
set.Add(300);
items.Add(400);
EXPECT_FALSE(set.HasAnyOf(items));
set.Add(200);
EXPECT_TRUE(set.HasAnyOf(items));
set.Add(500);
EXPECT_TRUE(set.HasAnyOf(items));
}
TEST(EnumSetHasAnyOf, GeneralCase) {
EnumSet<uint32_t> set(0);
EnumSet<uint32_t> items(100);
EXPECT_FALSE(set.HasAnyOf(items));
set.Add(300);
items.Add(4);
EXPECT_FALSE(set.HasAnyOf(items));
set.Add(5);
items.Add(500);
EXPECT_FALSE(set.HasAnyOf(items));
set.Add(500);
EXPECT_TRUE(set.HasAnyOf(items));
EXPECT_FALSE(set.HasAnyOf(EnumSet<uint32_t>(20)));
EXPECT_FALSE(set.HasAnyOf(EnumSet<uint32_t>(600)));
EXPECT_TRUE(set.HasAnyOf(EnumSet<uint32_t>(5)));
EXPECT_TRUE(set.HasAnyOf(EnumSet<uint32_t>(300)));
EXPECT_TRUE(set.HasAnyOf(EnumSet<uint32_t>(0)));
}
TEST(EnumSet, DefaultIsEmpty) {
EnumSet<uint32_t> set;
for (uint32_t i = 0; i < 1000; ++i) {
EXPECT_FALSE(set.Contains(i));
}
}
TEST(CapabilitySet, ConstructSingleMemberMatrix) {
CapabilitySet s(SpvCapabilityMatrix);
EXPECT_TRUE(s.Contains(SpvCapabilityMatrix));
EXPECT_FALSE(s.Contains(SpvCapabilityShader));
EXPECT_FALSE(s.Contains(static_cast<SpvCapability>(1000)));
}
TEST(CapabilitySet, ConstructSingleMemberMaxInMask) {
CapabilitySet s(static_cast<SpvCapability>(63));
EXPECT_FALSE(s.Contains(SpvCapabilityMatrix));
EXPECT_FALSE(s.Contains(SpvCapabilityShader));
EXPECT_TRUE(s.Contains(static_cast<SpvCapability>(63)));
EXPECT_FALSE(s.Contains(static_cast<SpvCapability>(64)));
EXPECT_FALSE(s.Contains(static_cast<SpvCapability>(1000)));
}
TEST(CapabilitySet, ConstructSingleMemberMinOverflow) {
// Check the first one that forces overflow beyond the mask.
CapabilitySet s(static_cast<SpvCapability>(64));
EXPECT_FALSE(s.Contains(SpvCapabilityMatrix));
EXPECT_FALSE(s.Contains(SpvCapabilityShader));
EXPECT_FALSE(s.Contains(static_cast<SpvCapability>(63)));
EXPECT_TRUE(s.Contains(static_cast<SpvCapability>(64)));
EXPECT_FALSE(s.Contains(static_cast<SpvCapability>(1000)));
}
TEST(CapabilitySet, ConstructSingleMemberMaxOverflow) {
// Check the max 32-bit signed int.
CapabilitySet s(static_cast<SpvCapability>(0x7fffffffu));
EXPECT_FALSE(s.Contains(SpvCapabilityMatrix));
EXPECT_FALSE(s.Contains(SpvCapabilityShader));
EXPECT_FALSE(s.Contains(static_cast<SpvCapability>(1000)));
EXPECT_TRUE(s.Contains(static_cast<SpvCapability>(0x7fffffffu)));
}
TEST(CapabilitySet, AddEnum) {
CapabilitySet s(SpvCapabilityShader);
s.Add(SpvCapabilityKernel);
s.Add(static_cast<SpvCapability>(42));
EXPECT_FALSE(s.Contains(SpvCapabilityMatrix));
EXPECT_TRUE(s.Contains(SpvCapabilityShader));
EXPECT_TRUE(s.Contains(SpvCapabilityKernel));
EXPECT_TRUE(s.Contains(static_cast<SpvCapability>(42)));
}
TEST(CapabilitySet, InitializerListEmpty) {
CapabilitySet s{};
for (uint32_t i = 0; i < 1000; i++) {
EXPECT_FALSE(s.Contains(static_cast<SpvCapability>(i)));
}
}
struct ForEachCase {
CapabilitySet capabilities;
std::vector<SpvCapability> expected;
};
using CapabilitySetForEachTest = ::testing::TestWithParam<ForEachCase>;
TEST_P(CapabilitySetForEachTest, CallsAsExpected) {
EXPECT_THAT(ElementsIn(GetParam().capabilities), Eq(GetParam().expected));
}
TEST_P(CapabilitySetForEachTest, CopyConstructor) {
CapabilitySet copy(GetParam().capabilities);
EXPECT_THAT(ElementsIn(copy), Eq(GetParam().expected));
}
TEST_P(CapabilitySetForEachTest, MoveConstructor) {
// We need a writable copy to move from.
CapabilitySet copy(GetParam().capabilities);
CapabilitySet moved(std::move(copy));
EXPECT_THAT(ElementsIn(moved), Eq(GetParam().expected));
// The moved-from set is empty.
EXPECT_THAT(ElementsIn(copy), Eq(std::vector<SpvCapability>{}));
}
TEST_P(CapabilitySetForEachTest, OperatorEquals) {
CapabilitySet assigned = GetParam().capabilities;
EXPECT_THAT(ElementsIn(assigned), Eq(GetParam().expected));
}
TEST_P(CapabilitySetForEachTest, OperatorEqualsSelfAssign) {
CapabilitySet assigned{GetParam().capabilities};
assigned = assigned;
EXPECT_THAT(ElementsIn(assigned), Eq(GetParam().expected));
}
INSTANTIATE_TEST_SUITE_P(Samples, CapabilitySetForEachTest,
ValuesIn(std::vector<ForEachCase>{
{{}, {}},
{{SpvCapabilityMatrix}, {SpvCapabilityMatrix}},
{{SpvCapabilityKernel, SpvCapabilityShader},
{SpvCapabilityShader, SpvCapabilityKernel}},
{{static_cast<SpvCapability>(999)},
{static_cast<SpvCapability>(999)}},
{{static_cast<SpvCapability>(0x7fffffff)},
{static_cast<SpvCapability>(0x7fffffff)}},
// Mixture and out of order
{{static_cast<SpvCapability>(0x7fffffff),
static_cast<SpvCapability>(100),
SpvCapabilityShader, SpvCapabilityMatrix},
{SpvCapabilityMatrix, SpvCapabilityShader,
static_cast<SpvCapability>(100),
static_cast<SpvCapability>(0x7fffffff)}},
}));
} // namespace
} // namespace spvtools

View File

@@ -1,195 +0,0 @@
// 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.
// Tests for OpExtension validator rules.
#include <string>
#include <utility>
#include <vector>
#include "gtest/gtest.h"
#include "source/enum_string_mapping.h"
#include "source/extensions.h"
namespace spvtools {
namespace {
using ::testing::Values;
using ::testing::ValuesIn;
using ExtensionTest =
::testing::TestWithParam<std::pair<Extension, std::string>>;
using UnknownExtensionTest = ::testing::TestWithParam<std::string>;
using CapabilityTest =
::testing::TestWithParam<std::pair<SpvCapability, std::string>>;
TEST_P(ExtensionTest, TestExtensionFromString) {
const std::pair<Extension, std::string>& param = GetParam();
const Extension extension = param.first;
const std::string extension_str = param.second;
Extension result_extension;
ASSERT_TRUE(GetExtensionFromString(extension_str.c_str(), &result_extension));
EXPECT_EQ(extension, result_extension);
}
TEST_P(ExtensionTest, TestExtensionToString) {
const std::pair<Extension, std::string>& param = GetParam();
const Extension extension = param.first;
const std::string extension_str = param.second;
const std::string result_str = ExtensionToString(extension);
EXPECT_EQ(extension_str, result_str);
}
TEST_P(UnknownExtensionTest, TestExtensionFromStringFails) {
Extension result_extension;
ASSERT_FALSE(GetExtensionFromString(GetParam().c_str(), &result_extension));
}
TEST_P(CapabilityTest, TestCapabilityToString) {
const std::pair<SpvCapability, std::string>& param = GetParam();
const SpvCapability capability = param.first;
const std::string capability_str = param.second;
const std::string result_str = CapabilityToString(capability);
EXPECT_EQ(capability_str, result_str);
}
INSTANTIATE_TEST_SUITE_P(
AllExtensions, ExtensionTest,
ValuesIn(std::vector<std::pair<Extension, std::string>>({
{Extension::kSPV_KHR_16bit_storage, "SPV_KHR_16bit_storage"},
{Extension::kSPV_KHR_device_group, "SPV_KHR_device_group"},
{Extension::kSPV_KHR_multiview, "SPV_KHR_multiview"},
{Extension::kSPV_KHR_shader_ballot, "SPV_KHR_shader_ballot"},
{Extension::kSPV_KHR_shader_draw_parameters,
"SPV_KHR_shader_draw_parameters"},
{Extension::kSPV_KHR_subgroup_vote, "SPV_KHR_subgroup_vote"},
{Extension::kSPV_NVX_multiview_per_view_attributes,
"SPV_NVX_multiview_per_view_attributes"},
{Extension::kSPV_NV_geometry_shader_passthrough,
"SPV_NV_geometry_shader_passthrough"},
{Extension::kSPV_NV_sample_mask_override_coverage,
"SPV_NV_sample_mask_override_coverage"},
{Extension::kSPV_NV_stereo_view_rendering,
"SPV_NV_stereo_view_rendering"},
{Extension::kSPV_NV_viewport_array2, "SPV_NV_viewport_array2"},
{Extension::kSPV_GOOGLE_decorate_string, "SPV_GOOGLE_decorate_string"},
{Extension::kSPV_GOOGLE_hlsl_functionality1,
"SPV_GOOGLE_hlsl_functionality1"},
{Extension::kSPV_KHR_8bit_storage, "SPV_KHR_8bit_storage"},
})));
INSTANTIATE_TEST_SUITE_P(UnknownExtensions, UnknownExtensionTest,
Values("", "SPV_KHR_", "SPV_KHR_device_group_ERROR",
/*alphabetically before all extensions*/ "A",
/*alphabetically after all extensions*/ "Z",
"SPV_ERROR_random_string_hfsdklhlktherh"));
INSTANTIATE_TEST_SUITE_P(
AllCapabilities, CapabilityTest,
ValuesIn(std::vector<std::pair<SpvCapability, std::string>>(
{{SpvCapabilityMatrix, "Matrix"},
{SpvCapabilityShader, "Shader"},
{SpvCapabilityGeometry, "Geometry"},
{SpvCapabilityTessellation, "Tessellation"},
{SpvCapabilityAddresses, "Addresses"},
{SpvCapabilityLinkage, "Linkage"},
{SpvCapabilityKernel, "Kernel"},
{SpvCapabilityVector16, "Vector16"},
{SpvCapabilityFloat16Buffer, "Float16Buffer"},
{SpvCapabilityFloat16, "Float16"},
{SpvCapabilityFloat64, "Float64"},
{SpvCapabilityInt64, "Int64"},
{SpvCapabilityInt64Atomics, "Int64Atomics"},
{SpvCapabilityImageBasic, "ImageBasic"},
{SpvCapabilityImageReadWrite, "ImageReadWrite"},
{SpvCapabilityImageMipmap, "ImageMipmap"},
{SpvCapabilityPipes, "Pipes"},
{SpvCapabilityGroups, "Groups"},
{SpvCapabilityDeviceEnqueue, "DeviceEnqueue"},
{SpvCapabilityLiteralSampler, "LiteralSampler"},
{SpvCapabilityAtomicStorage, "AtomicStorage"},
{SpvCapabilityInt16, "Int16"},
{SpvCapabilityTessellationPointSize, "TessellationPointSize"},
{SpvCapabilityGeometryPointSize, "GeometryPointSize"},
{SpvCapabilityImageGatherExtended, "ImageGatherExtended"},
{SpvCapabilityStorageImageMultisample, "StorageImageMultisample"},
{SpvCapabilityUniformBufferArrayDynamicIndexing,
"UniformBufferArrayDynamicIndexing"},
{SpvCapabilitySampledImageArrayDynamicIndexing,
"SampledImageArrayDynamicIndexing"},
{SpvCapabilityStorageBufferArrayDynamicIndexing,
"StorageBufferArrayDynamicIndexing"},
{SpvCapabilityStorageImageArrayDynamicIndexing,
"StorageImageArrayDynamicIndexing"},
{SpvCapabilityClipDistance, "ClipDistance"},
{SpvCapabilityCullDistance, "CullDistance"},
{SpvCapabilityImageCubeArray, "ImageCubeArray"},
{SpvCapabilitySampleRateShading, "SampleRateShading"},
{SpvCapabilityImageRect, "ImageRect"},
{SpvCapabilitySampledRect, "SampledRect"},
{SpvCapabilityGenericPointer, "GenericPointer"},
{SpvCapabilityInt8, "Int8"},
{SpvCapabilityInputAttachment, "InputAttachment"},
{SpvCapabilitySparseResidency, "SparseResidency"},
{SpvCapabilityMinLod, "MinLod"},
{SpvCapabilitySampled1D, "Sampled1D"},
{SpvCapabilityImage1D, "Image1D"},
{SpvCapabilitySampledCubeArray, "SampledCubeArray"},
{SpvCapabilitySampledBuffer, "SampledBuffer"},
{SpvCapabilityImageBuffer, "ImageBuffer"},
{SpvCapabilityImageMSArray, "ImageMSArray"},
{SpvCapabilityStorageImageExtendedFormats,
"StorageImageExtendedFormats"},
{SpvCapabilityImageQuery, "ImageQuery"},
{SpvCapabilityDerivativeControl, "DerivativeControl"},
{SpvCapabilityInterpolationFunction, "InterpolationFunction"},
{SpvCapabilityTransformFeedback, "TransformFeedback"},
{SpvCapabilityGeometryStreams, "GeometryStreams"},
{SpvCapabilityStorageImageReadWithoutFormat,
"StorageImageReadWithoutFormat"},
{SpvCapabilityStorageImageWriteWithoutFormat,
"StorageImageWriteWithoutFormat"},
{SpvCapabilityMultiViewport, "MultiViewport"},
{SpvCapabilitySubgroupDispatch, "SubgroupDispatch"},
{SpvCapabilityNamedBarrier, "NamedBarrier"},
{SpvCapabilityPipeStorage, "PipeStorage"},
{SpvCapabilitySubgroupBallotKHR, "SubgroupBallotKHR"},
{SpvCapabilityDrawParameters, "DrawParameters"},
{SpvCapabilitySubgroupVoteKHR, "SubgroupVoteKHR"},
{SpvCapabilityStorageBuffer16BitAccess, "StorageBuffer16BitAccess"},
{SpvCapabilityStorageUniformBufferBlock16,
"StorageBuffer16BitAccess"}, // Preferred name
{SpvCapabilityUniformAndStorageBuffer16BitAccess,
"UniformAndStorageBuffer16BitAccess"},
{SpvCapabilityStorageUniform16,
"UniformAndStorageBuffer16BitAccess"}, // Preferred name
{SpvCapabilityStoragePushConstant16, "StoragePushConstant16"},
{SpvCapabilityStorageInputOutput16, "StorageInputOutput16"},
{SpvCapabilityDeviceGroup, "DeviceGroup"},
{SpvCapabilityMultiView, "MultiView"},
{SpvCapabilitySampleMaskOverrideCoverageNV,
"SampleMaskOverrideCoverageNV"},
{SpvCapabilityGeometryShaderPassthroughNV,
"GeometryShaderPassthroughNV"},
// The next two are different names for the same token.
{SpvCapabilityShaderViewportIndexLayerNV,
"ShaderViewportIndexLayerEXT"},
{SpvCapabilityShaderViewportIndexLayerEXT,
"ShaderViewportIndexLayerEXT"},
{SpvCapabilityShaderViewportMaskNV, "ShaderViewportMaskNV"},
{SpvCapabilityShaderStereoViewNV, "ShaderStereoViewNV"},
{SpvCapabilityPerViewAttributesNV, "PerViewAttributesNV"}})));
} // namespace
} // namespace spvtools

View File

@@ -1,815 +0,0 @@
// 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 "DebugInfo.h"
#include "gmock/gmock.h"
#include "source/util/string_utils.h"
#include "test/test_fixture.h"
#include "test/unit_spirv.h"
// This file tests the correctness of encoding and decoding of instructions
// involving the DebugInfo extended instruction set.
// Semantic correctness should be the responsibility of validator.
//
// See https://www.khronos.org/registry/spir-v/specs/1.0/DebugInfo.html
namespace spvtools {
namespace {
using spvtest::Concatenate;
using spvtest::MakeInstruction;
using utils::MakeVector;
using testing::Eq;
struct InstructionCase {
uint32_t opcode;
std::string name;
std::string operands;
std::vector<uint32_t> expected_operands;
};
using ExtInstDebugInfoRoundTripTest =
spvtest::TextToBinaryTestBase<::testing::TestWithParam<InstructionCase>>;
using ExtInstDebugInfoRoundTripTestExplicit = spvtest::TextToBinaryTest;
TEST_P(ExtInstDebugInfoRoundTripTest, ParameterizedExtInst) {
const std::string input =
"%1 = OpExtInstImport \"DebugInfo\"\n"
"%3 = OpExtInst %2 %1 " +
GetParam().name + GetParam().operands + "\n";
// First make sure it assembles correctly.
EXPECT_THAT(
CompiledInstructions(input),
Eq(Concatenate(
{MakeInstruction(SpvOpExtInstImport, {1}, MakeVector("DebugInfo")),
MakeInstruction(SpvOpExtInst, {2, 3, 1, GetParam().opcode},
GetParam().expected_operands)})))
<< input;
// Now check the round trip through the disassembler.
EXPECT_THAT(EncodeAndDecodeSuccessfully(input), input) << input;
}
#define CASE_0(Enum) \
{ \
uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, "", {} \
}
#define CASE_ILL(Enum, L0, L1) \
{ \
uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 " #L0 " " #L1, { \
4, L0, L1 \
} \
}
#define CASE_IL(Enum, L0) \
{ \
uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 " #L0, { 4, L0 } \
}
#define CASE_I(Enum) \
{ \
uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4", { 4 } \
}
#define CASE_II(Enum) \
{ \
uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 %5", { 4, 5 } \
}
#define CASE_III(Enum) \
{ \
uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 %5 %6", { 4, 5, 6 } \
}
#define CASE_IIII(Enum) \
{ \
uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 %5 %6 %7", { \
4, 5, 6, 7 \
} \
}
#define CASE_IIIII(Enum) \
{ \
uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 %5 %6 %7 %8", { \
4, 5, 6, 7, 8 \
} \
}
#define CASE_IIIIII(Enum) \
{ \
uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 %5 %6 %7 %8 %9", { \
4, 5, 6, 7, 8, 9 \
} \
}
#define CASE_IIIIIII(Enum) \
{ \
uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 %5 %6 %7 %8 %9 %10", { \
4, 5, 6, 7, 8, 9, 10 \
} \
}
#define CASE_IIILLI(Enum, L0, L1) \
{ \
uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \
" %4 %5 %6 " #L0 " " #L1 " %7", { \
4, 5, 6, L0, L1, 7 \
} \
}
#define CASE_IIILLIL(Enum, L0, L1, L2) \
{ \
uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \
" %4 %5 %6 " #L0 " " #L1 " %7 " #L2, { \
4, 5, 6, L0, L1, 7, L2 \
} \
}
#define CASE_IE(Enum, E0) \
{ \
uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 " #E0, { \
4, uint32_t(DebugInfo##E0) \
} \
}
#define CASE_IIE(Enum, E0) \
{ \
uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 %5 " #E0, { \
4, 5, uint32_t(DebugInfo##E0) \
} \
}
#define CASE_ISF(Enum, S0, Fstr, Fnum) \
{ \
uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 " #S0 " " Fstr, { \
4, uint32_t(SpvStorageClass##S0), Fnum \
} \
}
#define CASE_LII(Enum, L0) \
{ \
uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " " #L0 " %4 %5", { \
L0, 4, 5 \
} \
}
#define CASE_ILI(Enum, L0) \
{ \
uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 " #L0 " %5", { \
4, L0, 5 \
} \
}
#define CASE_ILII(Enum, L0) \
{ \
uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 " #L0 " %5 %6", { \
4, L0, 5, 6 \
} \
}
#define CASE_ILLII(Enum, L0, L1) \
{ \
uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \
" %4 " #L0 " " #L1 " %5 %6", { \
4, L0, L1, 5, 6 \
} \
}
#define CASE_IIILLIIF(Enum, L0, L1, Fstr, Fnum) \
{ \
uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \
" %4 %5 %6 " #L0 " " #L1 " %7 %8 " Fstr, { \
4, 5, 6, L0, L1, 7, 8, Fnum \
} \
}
#define CASE_IIILLIIFII(Enum, L0, L1, Fstr, Fnum) \
{ \
uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \
" %4 %5 %6 " #L0 " " #L1 " %7 %8 " Fstr " %9 %10", { \
4, 5, 6, L0, L1, 7, 8, Fnum, 9, 10 \
} \
}
#define CASE_IIILLIIFIIII(Enum, L0, L1, Fstr, Fnum) \
{ \
uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \
" %4 %5 %6 " #L0 " " #L1 " %7 %8 " Fstr " %9 %10 %11 %12", { \
4, 5, 6, L0, L1, 7, 8, Fnum, 9, 10, 11, 12 \
} \
}
#define CASE_IIILLIIFIIIIII(Enum, L0, L1, Fstr, Fnum) \
{ \
uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \
" %4 %5 %6 " #L0 " " #L1 " %7 %8 " Fstr " %9 %10 %11 %12 %13 %14", { \
4, 5, 6, L0, L1, 7, 8, Fnum, 9, 10, 11, 12, 13, 14 \
} \
}
#define CASE_IEILLIIF(Enum, E0, L0, L1, Fstr, Fnum) \
{ \
uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \
" %4 " #E0 " %5 " #L0 " " #L1 " %6 %7 " Fstr, { \
4, uint32_t(DebugInfo##E0), 5, L0, L1, 6, 7, Fnum \
} \
}
#define CASE_IEILLIIFI(Enum, E0, L0, L1, Fstr, Fnum) \
{ \
uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \
" %4 " #E0 " %5 " #L0 " " #L1 " %6 %7 " Fstr " %8", { \
4, uint32_t(DebugInfo##E0), 5, L0, L1, 6, 7, Fnum, 8 \
} \
}
#define CASE_IEILLIIFII(Enum, E0, L0, L1, Fstr, Fnum) \
{ \
uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \
" %4 " #E0 " %5 " #L0 " " #L1 " %6 %7 " Fstr " %8 %9", { \
4, uint32_t(DebugInfo##E0), 5, L0, L1, 6, 7, Fnum, 8, 9 \
} \
}
#define CASE_IEILLIIFIII(Enum, E0, L0, L1, Fstr, Fnum) \
{ \
uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \
" %4 " #E0 " %5 " #L0 " " #L1 " %6 %7 " Fstr " %8 %9 %10", { \
4, uint32_t(DebugInfo##E0), 5, L0, L1, 6, 7, Fnum, 8, 9, 10 \
} \
}
#define CASE_IEILLIIFIIII(Enum, E0, L0, L1, Fstr, Fnum) \
{ \
uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \
" %4 " #E0 " %5 " #L0 " " #L1 " %6 %7 " Fstr " %8 %9 %10 %11", { \
4, uint32_t(DebugInfo##E0), 5, L0, L1, 6, 7, Fnum, 8, 9, 10, 11 \
} \
}
#define CASE_IIILLIIIF(Enum, L0, L1, Fstr, Fnum) \
{ \
uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \
" %4 %5 %6 " #L0 " " #L1 " %7 %8 %9 " Fstr, { \
4, 5, 6, L0, L1, 7, 8, 9, Fnum \
} \
}
#define CASE_IIILLIIIFI(Enum, L0, L1, Fstr, Fnum) \
{ \
uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \
" %4 %5 %6 " #L0 " " #L1 " %7 %8 %9 " Fstr " %10", { \
4, 5, 6, L0, L1, 7, 8, 9, Fnum, 10 \
} \
}
#define CASE_IIIIF(Enum, Fstr, Fnum) \
{ \
uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 %5 %6 %7 " Fstr, { \
4, 5, 6, 7, Fnum \
} \
}
#define CASE_IIILL(Enum, L0, L1) \
{ \
uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 %5 %6 " #L0 " " #L1, { \
4, 5, 6, L0, L1 \
} \
}
#define CASE_IIIILL(Enum, L0, L1) \
{ \
uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \
" %4 %5 %6 %7 " #L0 " " #L1, { \
4, 5, 6, 7, L0, L1 \
} \
}
#define CASE_IILLI(Enum, L0, L1) \
{ \
uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \
" %4 %5 " #L0 " " #L1 " %6", { \
4, 5, L0, L1, 6 \
} \
}
#define CASE_IILLII(Enum, L0, L1) \
{ \
uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \
" %4 %5 " #L0 " " #L1 " %6 %7", { \
4, 5, L0, L1, 6, 7 \
} \
}
#define CASE_IILLIII(Enum, L0, L1) \
{ \
uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \
" %4 %5 " #L0 " " #L1 " %6 %7 %8", { \
4, 5, L0, L1, 6, 7, 8 \
} \
}
#define CASE_IILLIIII(Enum, L0, L1) \
{ \
uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \
" %4 %5 " #L0 " " #L1 " %6 %7 %8 %9", { \
4, 5, L0, L1, 6, 7, 8, 9 \
} \
}
#define CASE_IIILLIIFLI(Enum, L0, L1, Fstr, Fnum, L2) \
{ \
uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \
" %4 %5 %6 " #L0 " " #L1 " %7 %8 " Fstr " " #L2 " %9", { \
4, 5, 6, L0, L1, 7, 8, Fnum, L2, 9 \
} \
}
#define CASE_IIILLIIFLII(Enum, L0, L1, Fstr, Fnum, L2) \
{ \
uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \
" %4 %5 %6 " #L0 " " #L1 " %7 %8 " Fstr " " #L2 " %9 %10", { \
4, 5, 6, L0, L1, 7, 8, Fnum, L2, 9, 10 \
} \
}
#define CASE_E(Enum, E0) \
{ \
uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " " #E0, { \
uint32_t(DebugInfo##E0) \
} \
}
#define CASE_EL(Enum, E0, L0) \
{ \
uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " " #E0 " " #L0, { \
uint32_t(DebugInfo##E0), L0 \
} \
}
#define CASE_ELL(Enum, E0, L0, L1) \
{ \
uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " " #E0 " " #L0 " " #L1, { \
uint32_t(DebugInfo##E0), L0, L1 \
} \
}
// DebugInfo 4.1 Absent Debugging Information
INSTANTIATE_TEST_SUITE_P(DebugInfoDebugInfoNone, ExtInstDebugInfoRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
CASE_0(InfoNone), // enum value 0
})));
// DebugInfo 4.2 Compilation Unit
INSTANTIATE_TEST_SUITE_P(DebugInfoDebugCompilationUnit,
ExtInstDebugInfoRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
CASE_ILL(CompilationUnit, 100, 42),
})));
// DebugInfo 4.3 Type instructions
INSTANTIATE_TEST_SUITE_P(DebugInfoDebugTypeBasic, ExtInstDebugInfoRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
CASE_IIE(TypeBasic, Unspecified),
CASE_IIE(TypeBasic, Address),
CASE_IIE(TypeBasic, Boolean),
CASE_IIE(TypeBasic, Float),
CASE_IIE(TypeBasic, Signed),
CASE_IIE(TypeBasic, SignedChar),
CASE_IIE(TypeBasic, Unsigned),
CASE_IIE(TypeBasic, UnsignedChar),
})));
// The FlagIsPublic is value is (1 << 0) | (1 << 2) which is the same
// as the bitwise-OR of FlagIsProtected and FlagIsPrivate.
// The disassembler will emit the compound expression instead.
// There is no simple fix for this. This enum is not really a mask
// for the bottom two bits.
TEST_F(ExtInstDebugInfoRoundTripTestExplicit, FlagIsPublic) {
const std::string prefix =
"%1 = OpExtInstImport \"DebugInfo\"\n"
"%3 = OpExtInst %2 %1 DebugTypePointer %4 Private ";
const std::string input = prefix + "FlagIsPublic\n";
const std::string expected = prefix + "FlagIsProtected|FlagIsPrivate\n";
// First make sure it assembles correctly.
EXPECT_THAT(
CompiledInstructions(input),
Eq(Concatenate(
{MakeInstruction(SpvOpExtInstImport, {1}, MakeVector("DebugInfo")),
MakeInstruction(SpvOpExtInst, {2, 3, 1, DebugInfoDebugTypePointer, 4,
uint32_t(SpvStorageClassPrivate),
DebugInfoFlagIsPublic})})))
<< input;
// Now check the round trip through the disassembler.
EXPECT_THAT(EncodeAndDecodeSuccessfully(input), Eq(expected)) << input;
}
INSTANTIATE_TEST_SUITE_P(
DebugInfoDebugTypePointer, ExtInstDebugInfoRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
//// Use each flag independently.
CASE_ISF(TypePointer, Private, "FlagIsProtected",
uint32_t(DebugInfoFlagIsProtected)),
CASE_ISF(TypePointer, Private, "FlagIsPrivate",
uint32_t(DebugInfoFlagIsPrivate)),
// FlagIsPublic is tested above.
CASE_ISF(TypePointer, Private, "FlagIsLocal",
uint32_t(DebugInfoFlagIsLocal)),
CASE_ISF(TypePointer, Private, "FlagIsDefinition",
uint32_t(DebugInfoFlagIsDefinition)),
CASE_ISF(TypePointer, Private, "FlagFwdDecl",
uint32_t(DebugInfoFlagFwdDecl)),
CASE_ISF(TypePointer, Private, "FlagArtificial",
uint32_t(DebugInfoFlagArtificial)),
CASE_ISF(TypePointer, Private, "FlagExplicit",
uint32_t(DebugInfoFlagExplicit)),
CASE_ISF(TypePointer, Private, "FlagPrototyped",
uint32_t(DebugInfoFlagPrototyped)),
CASE_ISF(TypePointer, Private, "FlagObjectPointer",
uint32_t(DebugInfoFlagObjectPointer)),
CASE_ISF(TypePointer, Private, "FlagStaticMember",
uint32_t(DebugInfoFlagStaticMember)),
CASE_ISF(TypePointer, Private, "FlagIndirectVariable",
uint32_t(DebugInfoFlagIndirectVariable)),
CASE_ISF(TypePointer, Private, "FlagLValueReference",
uint32_t(DebugInfoFlagLValueReference)),
CASE_ISF(TypePointer, Private, "FlagIsOptimized",
uint32_t(DebugInfoFlagIsOptimized)),
//// Use flags in combination, and try different storage classes.
CASE_ISF(TypePointer, Function, "FlagIsProtected|FlagIsPrivate",
uint32_t(DebugInfoFlagIsProtected) |
uint32_t(DebugInfoFlagIsPrivate)),
CASE_ISF(
TypePointer, Workgroup,
"FlagIsPrivate|FlagFwdDecl|FlagIndirectVariable|FlagIsOptimized",
uint32_t(DebugInfoFlagIsPrivate) | uint32_t(DebugInfoFlagFwdDecl) |
uint32_t(DebugInfoFlagIndirectVariable) |
uint32_t(DebugInfoFlagIsOptimized)),
})));
INSTANTIATE_TEST_SUITE_P(DebugInfoDebugTypeQualifier,
ExtInstDebugInfoRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
CASE_IE(TypeQualifier, ConstType),
CASE_IE(TypeQualifier, VolatileType),
CASE_IE(TypeQualifier, RestrictType),
})));
INSTANTIATE_TEST_SUITE_P(DebugInfoDebugTypeArray, ExtInstDebugInfoRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
CASE_II(TypeArray),
CASE_III(TypeArray),
CASE_IIII(TypeArray),
CASE_IIIII(TypeArray),
})));
INSTANTIATE_TEST_SUITE_P(DebugInfoDebugTypeVector,
ExtInstDebugInfoRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
CASE_IL(TypeVector, 2),
CASE_IL(TypeVector, 3),
CASE_IL(TypeVector, 4),
CASE_IL(TypeVector, 16),
})));
INSTANTIATE_TEST_SUITE_P(DebugInfoDebugTypedef, ExtInstDebugInfoRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
CASE_IIILLI(Typedef, 12, 13),
CASE_IIILLI(Typedef, 14, 99),
})));
INSTANTIATE_TEST_SUITE_P(DebugInfoDebugTypeFunction,
ExtInstDebugInfoRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
CASE_I(TypeFunction),
CASE_II(TypeFunction),
CASE_III(TypeFunction),
CASE_IIII(TypeFunction),
CASE_IIIII(TypeFunction),
})));
INSTANTIATE_TEST_SUITE_P(
DebugInfoDebugTypeEnum, ExtInstDebugInfoRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
CASE_IIILLIIFII(
TypeEnum, 12, 13,
"FlagIsPrivate|FlagFwdDecl|FlagIndirectVariable|FlagIsOptimized",
uint32_t(DebugInfoFlagIsPrivate) | uint32_t(DebugInfoFlagFwdDecl) |
uint32_t(DebugInfoFlagIndirectVariable) |
uint32_t(DebugInfoFlagIsOptimized)),
CASE_IIILLIIFIIII(TypeEnum, 17, 18, "FlagStaticMember",
uint32_t(DebugInfoFlagStaticMember)),
CASE_IIILLIIFIIIIII(TypeEnum, 99, 1, "FlagStaticMember",
uint32_t(DebugInfoFlagStaticMember)),
})));
INSTANTIATE_TEST_SUITE_P(
DebugInfoDebugTypeComposite, ExtInstDebugInfoRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
CASE_IEILLIIF(
TypeComposite, Class, 12, 13,
"FlagIsPrivate|FlagFwdDecl|FlagIndirectVariable|FlagIsOptimized",
uint32_t(DebugInfoFlagIsPrivate) | uint32_t(DebugInfoFlagFwdDecl) |
uint32_t(DebugInfoFlagIndirectVariable) |
uint32_t(DebugInfoFlagIsOptimized)),
// Cover all tag values: Class, Structure, Union
CASE_IEILLIIF(TypeComposite, Class, 12, 13, "FlagIsPrivate",
uint32_t(DebugInfoFlagIsPrivate)),
CASE_IEILLIIF(TypeComposite, Structure, 12, 13, "FlagIsPrivate",
uint32_t(DebugInfoFlagIsPrivate)),
CASE_IEILLIIF(TypeComposite, Union, 12, 13, "FlagIsPrivate",
uint32_t(DebugInfoFlagIsPrivate)),
// Now add members
CASE_IEILLIIFI(TypeComposite, Class, 9, 10, "FlagIsPrivate",
uint32_t(DebugInfoFlagIsPrivate)),
CASE_IEILLIIFII(TypeComposite, Class, 9, 10, "FlagIsPrivate",
uint32_t(DebugInfoFlagIsPrivate)),
CASE_IEILLIIFIII(TypeComposite, Class, 9, 10, "FlagIsPrivate",
uint32_t(DebugInfoFlagIsPrivate)),
CASE_IEILLIIFIIII(TypeComposite, Class, 9, 10, "FlagIsPrivate",
uint32_t(DebugInfoFlagIsPrivate)),
})));
INSTANTIATE_TEST_SUITE_P(
DebugInfoDebugTypeMember, ExtInstDebugInfoRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
CASE_IIILLIIIF(TypeMember, 12, 13, "FlagIsPrivate",
uint32_t(DebugInfoFlagIsPrivate)),
CASE_IIILLIIIF(TypeMember, 99, 100, "FlagIsPrivate|FlagFwdDecl",
uint32_t(DebugInfoFlagIsPrivate) |
uint32_t(DebugInfoFlagFwdDecl)),
// Add the optional Id argument.
CASE_IIILLIIIFI(TypeMember, 12, 13, "FlagIsPrivate",
uint32_t(DebugInfoFlagIsPrivate)),
})));
INSTANTIATE_TEST_SUITE_P(
DebugInfoDebugTypeInheritance, ExtInstDebugInfoRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
CASE_IIIIF(TypeInheritance, "FlagIsPrivate",
uint32_t(DebugInfoFlagIsPrivate)),
CASE_IIIIF(TypeInheritance, "FlagIsPrivate|FlagFwdDecl",
uint32_t(DebugInfoFlagIsPrivate) |
uint32_t(DebugInfoFlagFwdDecl)),
})));
INSTANTIATE_TEST_SUITE_P(DebugInfoDebugTypePtrToMember,
ExtInstDebugInfoRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
CASE_II(TypePtrToMember),
})));
// DebugInfo 4.4 Templates
INSTANTIATE_TEST_SUITE_P(DebugInfoDebugTypeTemplate,
ExtInstDebugInfoRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
CASE_II(TypeTemplate),
CASE_III(TypeTemplate),
CASE_IIII(TypeTemplate),
CASE_IIIII(TypeTemplate),
})));
INSTANTIATE_TEST_SUITE_P(DebugInfoDebugTypeTemplateParameter,
ExtInstDebugInfoRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
CASE_IIIILL(TypeTemplateParameter, 1, 2),
CASE_IIIILL(TypeTemplateParameter, 99, 102),
CASE_IIIILL(TypeTemplateParameter, 10, 7),
})));
INSTANTIATE_TEST_SUITE_P(DebugInfoDebugTypeTemplateTemplateParameter,
ExtInstDebugInfoRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
CASE_IIILL(TypeTemplateTemplateParameter, 1, 2),
CASE_IIILL(TypeTemplateTemplateParameter, 99, 102),
CASE_IIILL(TypeTemplateTemplateParameter, 10, 7),
})));
INSTANTIATE_TEST_SUITE_P(DebugInfoDebugTypeTemplateParameterPack,
ExtInstDebugInfoRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
CASE_IILLI(TypeTemplateParameterPack, 1, 2),
CASE_IILLII(TypeTemplateParameterPack, 99, 102),
CASE_IILLIII(TypeTemplateParameterPack, 10, 7),
CASE_IILLIIII(TypeTemplateParameterPack, 10, 7),
})));
// DebugInfo 4.5 Global Variables
INSTANTIATE_TEST_SUITE_P(
DebugInfoDebugGlobalVariable, ExtInstDebugInfoRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
CASE_IIILLIIIF(GlobalVariable, 1, 2, "FlagIsOptimized",
uint32_t(DebugInfoFlagIsOptimized)),
CASE_IIILLIIIF(GlobalVariable, 42, 43, "FlagIsOptimized",
uint32_t(DebugInfoFlagIsOptimized)),
CASE_IIILLIIIFI(GlobalVariable, 1, 2, "FlagIsOptimized",
uint32_t(DebugInfoFlagIsOptimized)),
CASE_IIILLIIIFI(GlobalVariable, 42, 43, "FlagIsOptimized",
uint32_t(DebugInfoFlagIsOptimized)),
})));
// DebugInfo 4.6 Functions
INSTANTIATE_TEST_SUITE_P(
DebugInfoDebugFunctionDeclaration, ExtInstDebugInfoRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
CASE_IIILLIIF(FunctionDeclaration, 1, 2, "FlagIsOptimized",
uint32_t(DebugInfoFlagIsOptimized)),
CASE_IIILLIIF(FunctionDeclaration, 42, 43, "FlagFwdDecl",
uint32_t(DebugInfoFlagFwdDecl)),
})));
INSTANTIATE_TEST_SUITE_P(
DebugInfoDebugFunction, ExtInstDebugInfoRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
CASE_IIILLIIFLI(Function, 1, 2, "FlagIsOptimized",
uint32_t(DebugInfoFlagIsOptimized), 3),
CASE_IIILLIIFLI(Function, 42, 43, "FlagFwdDecl",
uint32_t(DebugInfoFlagFwdDecl), 44),
// Add the optional declaration Id.
CASE_IIILLIIFLII(Function, 1, 2, "FlagIsOptimized",
uint32_t(DebugInfoFlagIsOptimized), 3),
CASE_IIILLIIFLII(Function, 42, 43, "FlagFwdDecl",
uint32_t(DebugInfoFlagFwdDecl), 44),
})));
// DebugInfo 4.7 Local Information
INSTANTIATE_TEST_SUITE_P(DebugInfoDebugLexicalBlock,
ExtInstDebugInfoRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
CASE_ILLII(LexicalBlock, 1, 2),
CASE_ILLII(LexicalBlock, 42, 43),
})));
INSTANTIATE_TEST_SUITE_P(DebugInfoDebugLexicalBlockDiscriminator,
ExtInstDebugInfoRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
CASE_ILI(LexicalBlockDiscriminator, 1),
CASE_ILI(LexicalBlockDiscriminator, 42),
})));
INSTANTIATE_TEST_SUITE_P(DebugInfoDebugScope, ExtInstDebugInfoRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
CASE_I(Scope),
CASE_II(Scope),
})));
INSTANTIATE_TEST_SUITE_P(DebugInfoDebugNoScope, ExtInstDebugInfoRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
CASE_0(NoScope),
})));
INSTANTIATE_TEST_SUITE_P(DebugInfoDebugInlinedAt, ExtInstDebugInfoRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
CASE_LII(InlinedAt, 1),
CASE_LII(InlinedAt, 42),
})));
// DebugInfo 4.8 Local Variables
INSTANTIATE_TEST_SUITE_P(DebugInfoDebugLocalVariable,
ExtInstDebugInfoRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
CASE_IIILLI(LocalVariable, 1, 2),
CASE_IIILLI(LocalVariable, 42, 43),
CASE_IIILLIL(LocalVariable, 1, 2, 3),
CASE_IIILLIL(LocalVariable, 42, 43, 44),
})));
INSTANTIATE_TEST_SUITE_P(DebugInfoDebugInlinedVariable,
ExtInstDebugInfoRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
CASE_II(InlinedVariable),
})));
INSTANTIATE_TEST_SUITE_P(DebugInfoDebugDebugDeclare,
ExtInstDebugInfoRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
CASE_III(Declare),
})));
INSTANTIATE_TEST_SUITE_P(
DebugInfoDebugDebugValue, ExtInstDebugInfoRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
CASE_III(Value),
CASE_IIII(Value),
CASE_IIIII(Value),
CASE_IIIIII(Value),
// Test up to 4 id parameters. We can always try more.
CASE_IIIIIII(Value),
})));
INSTANTIATE_TEST_SUITE_P(DebugInfoDebugDebugOperation,
ExtInstDebugInfoRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
CASE_E(Operation, Deref),
CASE_E(Operation, Plus),
CASE_E(Operation, Minus),
CASE_EL(Operation, PlusUconst, 1),
CASE_EL(Operation, PlusUconst, 42),
CASE_ELL(Operation, BitPiece, 1, 2),
CASE_ELL(Operation, BitPiece, 4, 5),
CASE_E(Operation, Swap),
CASE_E(Operation, Xderef),
CASE_E(Operation, StackValue),
CASE_EL(Operation, Constu, 1),
CASE_EL(Operation, Constu, 42),
})));
INSTANTIATE_TEST_SUITE_P(DebugInfoDebugDebugExpression,
ExtInstDebugInfoRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
CASE_0(Expression),
CASE_I(Expression),
CASE_II(Expression),
CASE_III(Expression),
CASE_IIII(Expression),
CASE_IIIII(Expression),
CASE_IIIIII(Expression),
CASE_IIIIIII(Expression),
})));
// DebugInfo 4.9 Macros
INSTANTIATE_TEST_SUITE_P(DebugInfoDebugMacroDef, ExtInstDebugInfoRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
CASE_ILI(MacroDef, 1),
CASE_ILI(MacroDef, 42),
CASE_ILII(MacroDef, 1),
CASE_ILII(MacroDef, 42),
})));
INSTANTIATE_TEST_SUITE_P(DebugInfoDebugMacroUndef,
ExtInstDebugInfoRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
CASE_ILI(MacroUndef, 1),
CASE_ILI(MacroUndef, 42),
})));
#undef CASE_0
#undef CASE_ILL
#undef CASE_IL
#undef CASE_I
#undef CASE_II
#undef CASE_III
#undef CASE_IIII
#undef CASE_IIIII
#undef CASE_IIIIII
#undef CASE_IIIIIII
#undef CASE_IIILLI
#undef CASE_IIILLIL
#undef CASE_IE
#undef CASE_IIE
#undef CASE_ISF
#undef CASE_LII
#undef CASE_ILI
#undef CASE_ILII
#undef CASE_ILLII
#undef CASE_IIILLIIF
#undef CASE_IIILLIIFII
#undef CASE_IIILLIIFIIII
#undef CASE_IIILLIIFIIIIII
#undef CASE_IEILLIIF
#undef CASE_IEILLIIFI
#undef CASE_IEILLIIFII
#undef CASE_IEILLIIFIII
#undef CASE_IEILLIIFIIII
#undef CASE_IIILLIIIF
#undef CASE_IIILLIIIFI
#undef CASE_IIIIF
#undef CASE_IIILL
#undef CASE_IIIILL
#undef CASE_IILLI
#undef CASE_IILLII
#undef CASE_IILLIII
#undef CASE_IILLIIII
#undef CASE_IIILLIIFLI
#undef CASE_IIILLIIFLII
#undef CASE_E
#undef CASE_EL
#undef CASE_ELL
} // namespace
} // namespace spvtools

View File

@@ -1,204 +0,0 @@
// Copyright (c) 2015-2016 The Khronos Group 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 <algorithm>
#include <string>
#include <vector>
#include "source/latest_version_glsl_std_450_header.h"
#include "test/unit_spirv.h"
namespace spvtools {
namespace {
/// Context for an extended instruction.
///
/// Information about a GLSL extended instruction (including its opname, return
/// type, etc.) and related instructions used to generate the return type and
/// constant as the operands. Used in generating extended instruction tests.
struct ExtInstContext {
const char* extInstOpName;
const char* extInstOperandVars;
/// The following fields are used to check the SPIR-V binary representation
/// of this instruction.
uint32_t extInstOpcode; ///< Opcode value for this extended instruction.
uint32_t extInstLength; ///< Wordcount of this extended instruction.
std::vector<uint32_t> extInstOperandIds; ///< Ids for operands.
};
using ExtInstGLSLstd450RoundTripTest = ::testing::TestWithParam<ExtInstContext>;
TEST_P(ExtInstGLSLstd450RoundTripTest, ParameterizedExtInst) {
spv_context context = spvContextCreate(SPV_ENV_UNIVERSAL_1_0);
const std::string spirv = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical Simple
OpEntryPoint Vertex %2 "main"
%3 = OpTypeVoid
%4 = OpTypeFunction %3
%2 = OpFunction %3 None %5
%6 = OpLabel
%8 = OpExtInst %7 %1 )" + std::string(GetParam().extInstOpName) +
" " + GetParam().extInstOperandVars + R"(
OpReturn
OpFunctionEnd
)";
const std::string spirv_header =
R"(; SPIR-V
; Version: 1.0
; Generator: Khronos SPIR-V Tools Assembler; 0
; Bound: 9
; Schema: 0)";
spv_binary binary = nullptr;
spv_diagnostic diagnostic;
spv_result_t error = spvTextToBinary(context, spirv.c_str(), spirv.size(),
&binary, &diagnostic);
if (error) {
spvDiagnosticPrint(diagnostic);
spvDiagnosticDestroy(diagnostic);
ASSERT_EQ(SPV_SUCCESS, error)
<< "Source was: " << std::endl
<< spirv << std::endl
<< "Test case for : " << GetParam().extInstOpName << std::endl;
}
// Check we do have the extended instruction's corresponding binary code in
// the generated SPIR-V binary.
std::vector<uint32_t> expected_contains(
{12 /*OpExtInst*/ | GetParam().extInstLength << 16, 7 /*return type*/,
8 /*result id*/, 1 /*glsl450 import*/, GetParam().extInstOpcode});
for (uint32_t operand : GetParam().extInstOperandIds) {
expected_contains.push_back(operand);
}
EXPECT_NE(binary->code + binary->wordCount,
std::search(binary->code, binary->code + binary->wordCount,
expected_contains.begin(), expected_contains.end()))
<< "Cannot find\n"
<< spvtest::WordVector(expected_contains).str() << "in\n"
<< spvtest::WordVector(*binary).str();
// Check round trip gives the same text.
spv_text output_text = nullptr;
error = spvBinaryToText(context, binary->code, binary->wordCount,
SPV_BINARY_TO_TEXT_OPTION_NONE, &output_text,
&diagnostic);
if (error) {
spvDiagnosticPrint(diagnostic);
spvDiagnosticDestroy(diagnostic);
ASSERT_EQ(SPV_SUCCESS, error);
}
EXPECT_EQ(spirv_header + spirv, output_text->str);
spvTextDestroy(output_text);
spvBinaryDestroy(binary);
spvContextDestroy(context);
}
INSTANTIATE_TEST_SUITE_P(
ExtInstParameters, ExtInstGLSLstd450RoundTripTest,
::testing::ValuesIn(std::vector<ExtInstContext>({
// We are only testing the correctness of encoding and decoding here.
// Semantic correctness should be the responsibility of validator. So
// some of the instructions below have incorrect operand and/or return
// types, e.g, Modf, ModfStruct, etc.
{"Round", "%5", 1, 6, {5}},
{"RoundEven", "%5", 2, 6, {5}},
{"Trunc", "%5", 3, 6, {5}},
{"FAbs", "%5", 4, 6, {5}},
{"SAbs", "%5", 5, 6, {5}},
{"FSign", "%5", 6, 6, {5}},
{"SSign", "%5", 7, 6, {5}},
{"Floor", "%5", 8, 6, {5}},
{"Ceil", "%5", 9, 6, {5}},
{"Fract", "%5", 10, 6, {5}},
{"Radians", "%5", 11, 6, {5}},
{"Degrees", "%5", 12, 6, {5}},
{"Sin", "%5", 13, 6, {5}},
{"Cos", "%5", 14, 6, {5}},
{"Tan", "%5", 15, 6, {5}},
{"Asin", "%5", 16, 6, {5}},
{"Acos", "%5", 17, 6, {5}},
{"Atan", "%5", 18, 6, {5}},
{"Sinh", "%5", 19, 6, {5}},
{"Cosh", "%5", 20, 6, {5}},
{"Tanh", "%5", 21, 6, {5}},
{"Asinh", "%5", 22, 6, {5}},
{"Acosh", "%5", 23, 6, {5}},
{"Atanh", "%5", 24, 6, {5}},
{"Atan2", "%5 %5", 25, 7, {5, 5}},
{"Pow", "%5 %5", 26, 7, {5, 5}},
{"Exp", "%5", 27, 6, {5}},
{"Log", "%5", 28, 6, {5}},
{"Exp2", "%5", 29, 6, {5}},
{"Log2", "%5", 30, 6, {5}},
{"Sqrt", "%5", 31, 6, {5}},
{"InverseSqrt", "%5", 32, 6, {5}},
{"Determinant", "%5", 33, 6, {5}},
{"MatrixInverse", "%5", 34, 6, {5}},
{"Modf", "%5 %5", 35, 7, {5, 5}},
{"ModfStruct", "%5", 36, 6, {5}},
{"FMin", "%5 %5", 37, 7, {5, 5}},
{"UMin", "%5 %5", 38, 7, {5, 5}},
{"SMin", "%5 %5", 39, 7, {5, 5}},
{"FMax", "%5 %5", 40, 7, {5, 5}},
{"UMax", "%5 %5", 41, 7, {5, 5}},
{"SMax", "%5 %5", 42, 7, {5, 5}},
{"FClamp", "%5 %5 %5", 43, 8, {5, 5, 5}},
{"UClamp", "%5 %5 %5", 44, 8, {5, 5, 5}},
{"SClamp", "%5 %5 %5", 45, 8, {5, 5, 5}},
{"FMix", "%5 %5 %5", 46, 8, {5, 5, 5}},
{"IMix", "%5 %5 %5", 47, 8, {5, 5, 5}}, // Bug 15452. Reserved.
{"Step", "%5 %5", 48, 7, {5, 5}},
{"SmoothStep", "%5 %5 %5", 49, 8, {5, 5, 5}},
{"Fma", "%5 %5 %5", 50, 8, {5, 5, 5}},
{"Frexp", "%5 %5", 51, 7, {5, 5}},
{"FrexpStruct", "%5", 52, 6, {5}},
{"Ldexp", "%5 %5", 53, 7, {5, 5}},
{"PackSnorm4x8", "%5", 54, 6, {5}},
{"PackUnorm4x8", "%5", 55, 6, {5}},
{"PackSnorm2x16", "%5", 56, 6, {5}},
{"PackUnorm2x16", "%5", 57, 6, {5}},
{"PackHalf2x16", "%5", 58, 6, {5}},
{"PackDouble2x32", "%5", 59, 6, {5}},
{"UnpackSnorm2x16", "%5", 60, 6, {5}},
{"UnpackUnorm2x16", "%5", 61, 6, {5}},
{"UnpackHalf2x16", "%5", 62, 6, {5}},
{"UnpackSnorm4x8", "%5", 63, 6, {5}},
{"UnpackUnorm4x8", "%5", 64, 6, {5}},
{"UnpackDouble2x32", "%5", 65, 6, {5}},
{"Length", "%5", 66, 6, {5}},
{"Distance", "%5 %5", 67, 7, {5, 5}},
{"Cross", "%5 %5", 68, 7, {5, 5}},
{"Normalize", "%5", 69, 6, {5}},
// clang-format off
{"FaceForward", "%5 %5 %5", 70, 8, {5, 5, 5}},
// clang-format on
{"Reflect", "%5 %5", 71, 7, {5, 5}},
{"Refract", "%5 %5 %5", 72, 8, {5, 5, 5}},
{"FindILsb", "%5", 73, 6, {5}},
{"FindSMsb", "%5", 74, 6, {5}},
{"FindUMsb", "%5", 75, 6, {5}},
{"InterpolateAtCentroid", "%5", 76, 6, {5}},
// clang-format off
{"InterpolateAtSample", "%5 %5", 77, 7, {5, 5}},
{"InterpolateAtOffset", "%5 %5", 78, 7, {5, 5}},
// clang-format on
{"NMin", "%5 %5", 79, 7, {5, 5}},
{"NMax", "%5 %5", 80, 7, {5, 5}},
{"NClamp", "%5 %5 %5", 81, 8, {5, 5, 5}},
})));
} // namespace
} // namespace spvtools

View File

@@ -1,374 +0,0 @@
// Copyright (c) 2015-2016 The Khronos Group 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 "source/latest_version_opencl_std_header.h"
#include "source/util/string_utils.h"
#include "test/test_fixture.h"
#include "test/unit_spirv.h"
namespace spvtools {
namespace {
using spvtest::Concatenate;
using spvtest::MakeInstruction;
using utils::MakeVector;
using spvtest::TextToBinaryTest;
using testing::Eq;
struct InstructionCase {
uint32_t opcode;
std::string name;
std::string operands;
std::vector<uint32_t> expected_operands;
};
using ExtInstOpenCLStdRoundTripTest =
spvtest::TextToBinaryTestBase<::testing::TestWithParam<InstructionCase>>;
TEST_P(ExtInstOpenCLStdRoundTripTest, ParameterizedExtInst) {
// This example should not validate.
const std::string input =
"%1 = OpExtInstImport \"OpenCL.std\"\n"
"%3 = OpExtInst %2 %1 " +
GetParam().name + " " + GetParam().operands + "\n";
// First make sure it assembles correctly.
EXPECT_THAT(
CompiledInstructions(input),
Eq(Concatenate(
{MakeInstruction(SpvOpExtInstImport, {1}, MakeVector("OpenCL.std")),
MakeInstruction(SpvOpExtInst, {2, 3, 1, GetParam().opcode},
GetParam().expected_operands)})))
<< input;
// Now check the round trip through the disassembler.
EXPECT_THAT(EncodeAndDecodeSuccessfully(input), input) << input;
}
#define CASE1(Enum, Name) \
{ \
uint32_t(OpenCLLIB::Entrypoints::Enum), #Name, "%4", { 4 } \
}
#define CASE2(Enum, Name) \
{ \
uint32_t(OpenCLLIB::Entrypoints::Enum), #Name, "%4 %5", { 4, 5 } \
}
#define CASE3(Enum, Name) \
{ \
uint32_t(OpenCLLIB::Entrypoints::Enum), #Name, "%4 %5 %6", { 4, 5, 6 } \
}
#define CASE4(Enum, Name) \
{ \
uint32_t(OpenCLLIB::Entrypoints::Enum), #Name, "%4 %5 %6 %7", { \
4, 5, 6, 7 \
} \
}
#define CASE2Lit(Enum, Name, LiteralNumber) \
{ \
uint32_t(OpenCLLIB::Entrypoints::Enum), #Name, "%4 %5 " #LiteralNumber, { \
4, 5, LiteralNumber \
} \
}
#define CASE3Round(Enum, Name, Mode) \
{ \
uint32_t(OpenCLLIB::Entrypoints::Enum), #Name, "%4 %5 %6 " #Mode, { \
4, 5, 6, uint32_t(SpvFPRoundingMode##Mode) \
} \
}
// clang-format off
// OpenCL.std: 2.1 Math extended instructions
INSTANTIATE_TEST_SUITE_P(
OpenCLMath, ExtInstOpenCLStdRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
// We are only testing the correctness of encoding and decoding here.
// Semantic correctness should be the responsibility of validator.
CASE1(Acos, acos), // enum value 0
CASE1(Acosh, acosh),
CASE1(Acospi, acospi),
CASE1(Asin, asin),
CASE1(Asinh, asinh),
CASE1(Asinh, asinh),
CASE1(Asinpi, asinpi),
CASE1(Atan, atan),
CASE2(Atan2, atan2),
CASE1(Atanh, atanh),
CASE1(Atanpi, atanpi),
CASE2(Atan2pi, atan2pi),
CASE1(Cbrt, cbrt),
CASE1(Ceil, ceil),
CASE1(Ceil, ceil),
CASE2(Copysign, copysign),
CASE1(Cos, cos),
CASE1(Cosh, cosh),
CASE1(Cospi, cospi),
CASE1(Erfc, erfc),
CASE1(Erf, erf),
CASE1(Exp, exp),
CASE1(Exp2, exp2),
CASE1(Exp10, exp10),
CASE1(Expm1, expm1),
CASE1(Fabs, fabs),
CASE2(Fdim, fdim),
CASE1(Floor, floor),
CASE3(Fma, fma),
CASE2(Fmax, fmax),
CASE2(Fmin, fmin),
CASE2(Fmod, fmod),
CASE2(Fract, fract),
CASE2(Frexp, frexp),
CASE2(Hypot, hypot),
CASE1(Ilogb, ilogb),
CASE2(Ldexp, ldexp),
CASE1(Lgamma, lgamma),
CASE2(Lgamma_r, lgamma_r),
CASE1(Log, log),
CASE1(Log2, log2),
CASE1(Log10, log10),
CASE1(Log1p, log1p),
CASE3(Mad, mad),
CASE2(Maxmag, maxmag),
CASE2(Minmag, minmag),
CASE2(Modf, modf),
CASE1(Nan, nan),
CASE2(Nextafter, nextafter),
CASE2(Pow, pow),
CASE2(Pown, pown),
CASE2(Powr, powr),
CASE2(Remainder, remainder),
CASE3(Remquo, remquo),
CASE1(Rint, rint),
CASE2(Rootn, rootn),
CASE1(Round, round),
CASE1(Rsqrt, rsqrt),
CASE1(Sin, sin),
CASE2(Sincos, sincos),
CASE1(Sinh, sinh),
CASE1(Sinpi, sinpi),
CASE1(Sqrt, sqrt),
CASE1(Tan, tan),
CASE1(Tanh, tanh),
CASE1(Tanpi, tanpi),
CASE1(Tgamma, tgamma),
CASE1(Trunc, trunc),
CASE1(Half_cos, half_cos),
CASE2(Half_divide, half_divide),
CASE1(Half_exp, half_exp),
CASE1(Half_exp2, half_exp2),
CASE1(Half_exp10, half_exp10),
CASE1(Half_log, half_log),
CASE1(Half_log2, half_log2),
CASE1(Half_log10, half_log10),
CASE2(Half_powr, half_powr),
CASE1(Half_recip, half_recip),
CASE1(Half_rsqrt, half_rsqrt),
CASE1(Half_sin, half_sin),
CASE1(Half_sqrt, half_sqrt),
CASE1(Half_tan, half_tan),
CASE1(Native_cos, native_cos),
CASE2(Native_divide, native_divide),
CASE1(Native_exp, native_exp),
CASE1(Native_exp2, native_exp2),
CASE1(Native_exp10, native_exp10),
CASE1(Native_log, native_log),
CASE1(Native_log10, native_log10),
CASE2(Native_powr, native_powr),
CASE1(Native_recip, native_recip),
CASE1(Native_rsqrt, native_rsqrt),
CASE1(Native_sin, native_sin),
CASE1(Native_sqrt, native_sqrt),
CASE1(Native_tan, native_tan), // enum value 94
})));
// OpenCL.std: 2.1 Integer instructions
INSTANTIATE_TEST_SUITE_P(
OpenCLInteger, ExtInstOpenCLStdRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
CASE1(SAbs, s_abs), // enum value 141
CASE2(SAbs_diff, s_abs_diff),
CASE2(SAdd_sat, s_add_sat),
CASE2(UAdd_sat, u_add_sat),
CASE2(SHadd, s_hadd),
CASE2(UHadd, u_hadd),
CASE2(SRhadd, s_rhadd),
CASE2(SRhadd, s_rhadd),
CASE3(SClamp, s_clamp),
CASE3(UClamp, u_clamp),
CASE1(Clz, clz),
CASE1(Ctz, ctz),
CASE3(SMad_hi, s_mad_hi),
CASE3(UMad_sat, u_mad_sat),
CASE3(SMad_sat, s_mad_sat),
CASE2(SMax, s_max),
CASE2(UMax, u_max),
CASE2(SMin, s_min),
CASE2(UMin, u_min),
CASE2(SMul_hi, s_mul_hi),
CASE2(Rotate, rotate),
CASE2(SSub_sat, s_sub_sat),
CASE2(USub_sat, u_sub_sat),
CASE2(U_Upsample, u_upsample),
CASE2(S_Upsample, s_upsample),
CASE1(Popcount, popcount),
CASE3(SMad24, s_mad24),
CASE3(UMad24, u_mad24),
CASE2(SMul24, s_mul24),
CASE2(UMul24, u_mul24), // enum value 170
CASE1(UAbs, u_abs), // enum value 201
CASE2(UAbs_diff, u_abs_diff),
CASE2(UMul_hi, u_mul_hi),
CASE3(UMad_hi, u_mad_hi), // enum value 204
})));
// OpenCL.std: 2.3 Common instrucitons
INSTANTIATE_TEST_SUITE_P(
OpenCLCommon, ExtInstOpenCLStdRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
CASE3(FClamp, fclamp), // enum value 95
CASE1(Degrees, degrees),
CASE2(FMax_common, fmax_common),
CASE2(FMin_common, fmin_common),
CASE3(Mix, mix),
CASE1(Radians, radians),
CASE2(Step, step),
CASE3(Smoothstep, smoothstep),
CASE1(Sign, sign), // enum value 103
})));
// OpenCL.std: 2.4 Geometric instructions
INSTANTIATE_TEST_SUITE_P(
OpenCLGeometric, ExtInstOpenCLStdRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
CASE2(Cross, cross), // enum value 104
CASE2(Distance, distance),
CASE1(Length, length),
CASE1(Normalize, normalize),
CASE2(Fast_distance, fast_distance),
CASE1(Fast_length, fast_length),
CASE1(Fast_normalize, fast_normalize), // enum value 110
})));
// OpenCL.std: 2.5 Relational instructions
INSTANTIATE_TEST_SUITE_P(
OpenCLRelational, ExtInstOpenCLStdRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
CASE3(Bitselect, bitselect), // enum value 186
CASE3(Select, select), // enum value 187
})));
// OpenCL.std: 2.6 Vector data load and store instructions
INSTANTIATE_TEST_SUITE_P(
OpenCLVectorLoadStore, ExtInstOpenCLStdRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
// The last argument to Vloadn must be one of 2, 3, 4, 8, 16.
CASE2Lit(Vloadn, vloadn, 2),
CASE2Lit(Vloadn, vloadn, 3),
CASE2Lit(Vloadn, vloadn, 4),
CASE2Lit(Vloadn, vloadn, 8),
CASE2Lit(Vloadn, vloadn, 16),
CASE3(Vstoren, vstoren),
CASE2(Vload_half, vload_half),
CASE2Lit(Vload_halfn, vload_halfn, 2),
CASE2Lit(Vload_halfn, vload_halfn, 3),
CASE2Lit(Vload_halfn, vload_halfn, 4),
CASE2Lit(Vload_halfn, vload_halfn, 8),
CASE2Lit(Vload_halfn, vload_halfn, 16),
CASE3(Vstore_half, vstore_half),
// Try all the rounding modes.
CASE3Round(Vstore_half_r, vstore_half_r, RTE),
CASE3Round(Vstore_half_r, vstore_half_r, RTZ),
CASE3Round(Vstore_half_r, vstore_half_r, RTP),
CASE3Round(Vstore_half_r, vstore_half_r, RTN),
CASE3(Vstore_halfn, vstore_halfn),
CASE3Round(Vstore_halfn_r, vstore_halfn_r, RTE),
CASE3Round(Vstore_halfn_r, vstore_halfn_r, RTZ),
CASE3Round(Vstore_halfn_r, vstore_halfn_r, RTP),
CASE3Round(Vstore_halfn_r, vstore_halfn_r, RTN),
CASE2Lit(Vloada_halfn, vloada_halfn, 2),
CASE2Lit(Vloada_halfn, vloada_halfn, 3),
CASE2Lit(Vloada_halfn, vloada_halfn, 4),
CASE2Lit(Vloada_halfn, vloada_halfn, 8),
CASE2Lit(Vloada_halfn, vloada_halfn, 16),
CASE3(Vstorea_halfn, vstorea_halfn),
CASE3Round(Vstorea_halfn_r, vstorea_halfn_r, RTE),
CASE3Round(Vstorea_halfn_r, vstorea_halfn_r, RTZ),
CASE3Round(Vstorea_halfn_r, vstorea_halfn_r, RTP),
CASE3Round(Vstorea_halfn_r, vstorea_halfn_r, RTN),
})));
// OpenCL.std: 2.7 Miscellaneous vector instructions
INSTANTIATE_TEST_SUITE_P(
OpenCLMiscellaneousVector, ExtInstOpenCLStdRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
CASE2(Shuffle, shuffle),
CASE3(Shuffle2, shuffle2),
})));
// OpenCL.std: 2.8 Miscellaneous instructions
#define PREFIX uint32_t(OpenCLLIB::Entrypoints::Printf), "printf"
INSTANTIATE_TEST_SUITE_P(
OpenCLMiscPrintf, ExtInstOpenCLStdRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
// Printf is interesting because it takes a variable number of arguments.
// Start with zero optional arguments.
{PREFIX, "%4", {4}},
{PREFIX, "%4 %5", {4, 5}},
{PREFIX, "%4 %5 %6", {4, 5, 6}},
{PREFIX, "%4 %5 %6 %7", {4, 5, 6, 7}},
{PREFIX, "%4 %5 %6 %7 %8", {4, 5, 6, 7, 8}},
{PREFIX, "%4 %5 %6 %7 %8 %9", {4, 5, 6, 7, 8, 9}},
{PREFIX, "%4 %5 %6 %7 %8 %9 %10", {4, 5, 6, 7, 8, 9, 10}},
{PREFIX, "%4 %5 %6 %7 %8 %9 %10 %11", {4, 5, 6, 7, 8, 9, 10, 11}},
{PREFIX, "%4 %5 %6 %7 %8 %9 %10 %11 %12",
{4, 5, 6, 7, 8, 9, 10, 11, 12}},
{PREFIX, "%4 %5 %6 %7 %8 %9 %10 %11 %12 %13",
{4, 5, 6, 7, 8, 9, 10, 11, 12, 13}},
{PREFIX, "%4 %5 %6 %7 %8 %9 %10 %11 %12 %13 %14",
{4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}},
})));
#undef PREFIX
INSTANTIATE_TEST_SUITE_P(
OpenCLMiscPrefetch, ExtInstOpenCLStdRoundTripTest,
::testing::ValuesIn(std::vector<InstructionCase>({
CASE2(Prefetch, prefetch),
})));
// OpenCL.std: 2.9.1 Image encoding
// No new instructions defined in this section.
// OpenCL.std: 2.9.2 Sampler encoding
// No new instructions defined in this section.
// OpenCL.std: 2.9.3 Image read
// No new instructions defined in this section.
// Use core instruction OpImageSampleExplicitLod instead.
// OpenCL.std: 2.9.4 Image write
// No new instructions defined in this section.
// clang-format on
#undef CASE1
#undef CASE2
#undef CASE3
#undef CASE4
#undef CASE2Lit
#undef CASE3Round
} // namespace
} // namespace spvtools

View File

@@ -1,64 +0,0 @@
// Copyright (c) 2015-2016 The Khronos Group 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 "test/unit_spirv.h"
namespace spvtools {
namespace {
TEST(FixWord, Default) {
spv_endianness_t endian;
if (I32_ENDIAN_HOST == I32_ENDIAN_LITTLE) {
endian = SPV_ENDIANNESS_LITTLE;
} else {
endian = SPV_ENDIANNESS_BIG;
}
uint32_t word = 0x53780921;
ASSERT_EQ(word, spvFixWord(word, endian));
}
TEST(FixWord, Reorder) {
spv_endianness_t endian;
if (I32_ENDIAN_HOST == I32_ENDIAN_LITTLE) {
endian = SPV_ENDIANNESS_BIG;
} else {
endian = SPV_ENDIANNESS_LITTLE;
}
uint32_t word = 0x53780921;
uint32_t result = 0x21097853;
ASSERT_EQ(result, spvFixWord(word, endian));
}
TEST(FixDoubleWord, Default) {
spv_endianness_t endian =
(I32_ENDIAN_HOST == I32_ENDIAN_LITTLE ? SPV_ENDIANNESS_LITTLE
: SPV_ENDIANNESS_BIG);
uint32_t low = 0x53780921;
uint32_t high = 0xdeadbeef;
uint64_t result = 0xdeadbeef53780921;
ASSERT_EQ(result, spvFixDoubleWord(low, high, endian));
}
TEST(FixDoubleWord, Reorder) {
spv_endianness_t endian =
(I32_ENDIAN_HOST == I32_ENDIAN_LITTLE ? SPV_ENDIANNESS_BIG
: SPV_ENDIANNESS_LITTLE);
uint32_t low = 0x53780921;
uint32_t high = 0xdeadbeef;
uint64_t result = 0xefbeadde21097853;
ASSERT_EQ(result, spvFixDoubleWord(low, high, endian));
}
} // namespace
} // namespace spvtools

View File

@@ -1,65 +0,0 @@
# Copyright (c) 2019 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.
if (${SPIRV_BUILD_FUZZER})
set(SOURCES
fuzz_test_util.h
data_synonym_transformation_test.cpp
equivalence_relation_test.cpp
fact_manager_test.cpp
fuzz_test_util.cpp
fuzzer_pass_add_useful_constructs_test.cpp
instruction_descriptor_test.cpp
transformation_add_constant_boolean_test.cpp
transformation_add_constant_scalar_test.cpp
transformation_add_dead_break_test.cpp
transformation_add_dead_continue_test.cpp
transformation_add_no_contraction_decoration_test.cpp
transformation_add_type_boolean_test.cpp
transformation_add_type_float_test.cpp
transformation_add_type_int_test.cpp
transformation_add_type_pointer_test.cpp
transformation_composite_construct_test.cpp
transformation_composite_extract_test.cpp
transformation_copy_object_test.cpp
transformation_move_block_down_test.cpp
transformation_replace_boolean_constant_with_constant_binary_test.cpp
transformation_replace_constant_with_uniform_test.cpp
transformation_replace_id_with_synonym_test.cpp
transformation_set_function_control_test.cpp
transformation_set_loop_control_test.cpp
transformation_set_memory_operands_mask_test.cpp
transformation_set_selection_control_test.cpp
transformation_split_block_test.cpp
transformation_vector_shuffle_test.cpp
uniform_buffer_element_descriptor_test.cpp)
if (${SPIRV_ENABLE_LONG_FUZZER_TESTS})
# These are long-running tests that depend on random seeds. We do not want
# to run them during regular whole-project CI because they may reveal
# spirv-fuzz bugs in changes that are totally unrelated to spirv-fuzz,
# which would be counfounding. Instead, they should be run regularly but
# separately.
set(SOURCES ${SOURCES}
fuzzer_replayer_test.cpp
fuzzer_shrinker_test.cpp)
endif()
add_spvtools_unittest(TARGET fuzz
SRCS ${SOURCES}
LIBS SPIRV-Tools-fuzz
)
endif()

File diff suppressed because it is too large Load Diff

View File

@@ -1,145 +0,0 @@
// Copyright (c) 2019 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 <set>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "source/fuzz/equivalence_relation.h"
namespace spvtools {
namespace fuzz {
namespace {
struct UInt32Equals {
bool operator()(const uint32_t* first, const uint32_t* second) const {
return *first == *second;
}
};
struct UInt32Hash {
size_t operator()(const uint32_t* element) const {
return static_cast<size_t>(*element);
}
};
std::vector<uint32_t> ToUIntVector(
const std::vector<const uint32_t*>& pointers) {
std::vector<uint32_t> result;
for (auto pointer : pointers) {
result.push_back(*pointer);
}
return result;
}
TEST(EquivalenceRelationTest, BasicTest) {
EquivalenceRelation<uint32_t, UInt32Hash, UInt32Equals> relation;
ASSERT_TRUE(relation.GetAllKnownValues().empty());
for (uint32_t element = 2; element < 80; element += 2) {
relation.MakeEquivalent(0, element);
relation.MakeEquivalent(element - 1, element + 1);
}
for (uint32_t element = 82; element < 100; element += 2) {
relation.MakeEquivalent(80, element);
relation.MakeEquivalent(element - 1, element + 1);
}
relation.MakeEquivalent(78, 80);
std::vector<uint32_t> class1;
for (uint32_t element = 0; element < 98; element += 2) {
ASSERT_TRUE(relation.IsEquivalent(0, element));
ASSERT_TRUE(relation.IsEquivalent(element, element + 2));
class1.push_back(element);
}
class1.push_back(98);
ASSERT_THAT(ToUIntVector(relation.GetEquivalenceClass(0)),
testing::WhenSorted(class1));
ASSERT_THAT(ToUIntVector(relation.GetEquivalenceClass(4)),
testing::WhenSorted(class1));
ASSERT_THAT(ToUIntVector(relation.GetEquivalenceClass(40)),
testing::WhenSorted(class1));
std::vector<uint32_t> class2;
for (uint32_t element = 1; element < 79; element += 2) {
ASSERT_TRUE(relation.IsEquivalent(1, element));
ASSERT_TRUE(relation.IsEquivalent(element, element + 2));
class2.push_back(element);
}
class2.push_back(79);
ASSERT_THAT(ToUIntVector(relation.GetEquivalenceClass(1)),
testing::WhenSorted(class2));
ASSERT_THAT(ToUIntVector(relation.GetEquivalenceClass(11)),
testing::WhenSorted(class2));
ASSERT_THAT(ToUIntVector(relation.GetEquivalenceClass(31)),
testing::WhenSorted(class2));
std::vector<uint32_t> class3;
for (uint32_t element = 81; element < 99; element += 2) {
ASSERT_TRUE(relation.IsEquivalent(81, element));
ASSERT_TRUE(relation.IsEquivalent(element, element + 2));
class3.push_back(element);
}
class3.push_back(99);
ASSERT_THAT(ToUIntVector(relation.GetEquivalenceClass(81)),
testing::WhenSorted(class3));
ASSERT_THAT(ToUIntVector(relation.GetEquivalenceClass(91)),
testing::WhenSorted(class3));
ASSERT_THAT(ToUIntVector(relation.GetEquivalenceClass(99)),
testing::WhenSorted(class3));
bool first = true;
std::vector<const uint32_t*> previous_class;
for (auto representative : relation.GetEquivalenceClassRepresentatives()) {
std::vector<const uint32_t*> current_class =
relation.GetEquivalenceClass(*representative);
ASSERT_TRUE(std::find(current_class.begin(), current_class.end(),
representative) != current_class.end());
if (!first) {
ASSERT_TRUE(std::find(previous_class.begin(), previous_class.end(),
representative) == previous_class.end());
}
previous_class = current_class;
first = false;
}
}
TEST(EquivalenceRelationTest, DeterministicEquivalenceClassOrder) {
EquivalenceRelation<uint32_t, UInt32Hash, UInt32Equals> relation1;
EquivalenceRelation<uint32_t, UInt32Hash, UInt32Equals> relation2;
for (uint32_t i = 0; i < 1000; ++i) {
if (i >= 10) {
relation1.MakeEquivalent(i, i - 10);
relation2.MakeEquivalent(i, i - 10);
}
}
// We constructed the equivalence relations in the same way, so we would like
// them to have identical representatives, and identically-ordered equivalence
// classes per representative.
ASSERT_THAT(ToUIntVector(relation1.GetEquivalenceClassRepresentatives()),
ToUIntVector(relation2.GetEquivalenceClassRepresentatives()));
for (auto representative : relation1.GetEquivalenceClassRepresentatives()) {
ASSERT_THAT(ToUIntVector(relation1.GetEquivalenceClass(*representative)),
ToUIntVector(relation2.GetEquivalenceClass(*representative)));
}
}
} // namespace
} // namespace fuzz
} // namespace spvtools

File diff suppressed because it is too large Load Diff

View File

@@ -1,125 +0,0 @@
// Copyright (c) 2019 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 "test/fuzz/fuzz_test_util.h"
#include <fstream>
#include <iostream>
#include "tools/io.h"
namespace spvtools {
namespace fuzz {
bool IsEqual(const spv_target_env env,
const std::vector<uint32_t>& expected_binary,
const std::vector<uint32_t>& actual_binary) {
if (expected_binary == actual_binary) {
return true;
}
SpirvTools t(env);
std::string expected_disassembled;
std::string actual_disassembled;
if (!t.Disassemble(expected_binary, &expected_disassembled,
kFuzzDisassembleOption)) {
return false;
}
if (!t.Disassemble(actual_binary, &actual_disassembled,
kFuzzDisassembleOption)) {
return false;
}
// Using expect gives us a string diff if the strings are not the same.
EXPECT_EQ(expected_disassembled, actual_disassembled);
// We then return the result of the equality comparison, to be used by an
// assertion in the test root function.
return expected_disassembled == actual_disassembled;
}
bool IsEqual(const spv_target_env env, const std::string& expected_text,
const std::vector<uint32_t>& actual_binary) {
std::vector<uint32_t> expected_binary;
SpirvTools t(env);
if (!t.Assemble(expected_text, &expected_binary, kFuzzAssembleOption)) {
return false;
}
return IsEqual(env, expected_binary, actual_binary);
}
bool IsEqual(const spv_target_env env, const std::string& expected_text,
const opt::IRContext* actual_ir) {
std::vector<uint32_t> actual_binary;
actual_ir->module()->ToBinary(&actual_binary, false);
return IsEqual(env, expected_text, actual_binary);
}
bool IsEqual(const spv_target_env env, const opt::IRContext* ir_1,
const opt::IRContext* ir_2) {
std::vector<uint32_t> binary_1;
ir_1->module()->ToBinary(&binary_1, false);
std::vector<uint32_t> binary_2;
ir_2->module()->ToBinary(&binary_2, false);
return IsEqual(env, binary_1, binary_2);
}
bool IsValid(spv_target_env env, const opt::IRContext* ir) {
std::vector<uint32_t> binary;
ir->module()->ToBinary(&binary, false);
SpirvTools t(env);
return t.Validate(binary);
}
std::string ToString(spv_target_env env, const opt::IRContext* ir) {
std::vector<uint32_t> binary;
ir->module()->ToBinary(&binary, false);
return ToString(env, binary);
}
std::string ToString(spv_target_env env, const std::vector<uint32_t>& binary) {
SpirvTools t(env);
std::string result;
t.Disassemble(binary, &result, kFuzzDisassembleOption);
return result;
}
void DumpShader(opt::IRContext* context, const char* filename) {
std::vector<uint32_t> binary;
context->module()->ToBinary(&binary, false);
DumpShader(binary, filename);
}
void DumpShader(const std::vector<uint32_t>& binary, const char* filename) {
auto write_file_succeeded =
WriteFile(filename, "wb", &binary[0], binary.size());
if (!write_file_succeeded) {
std::cerr << "Failed to dump shader" << std::endl;
}
}
void DumpTransformationsJson(
const protobufs::TransformationSequence& transformations,
const char* filename) {
std::string json_string;
auto json_options = google::protobuf::util::JsonOptions();
json_options.add_whitespace = true;
auto json_generation_status = google::protobuf::util::MessageToJsonString(
transformations, &json_string, json_options);
if (json_generation_status == google::protobuf::util::Status::OK) {
std::ofstream transformations_json_file(filename);
transformations_json_file << json_string;
transformations_json_file.close();
}
}
} // namespace fuzz
} // namespace spvtools

View File

@@ -1,113 +0,0 @@
// Copyright (c) 2019 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.
#ifndef TEST_FUZZ_FUZZ_TEST_UTIL_H_
#define TEST_FUZZ_FUZZ_TEST_UTIL_H_
#include "gtest/gtest.h"
#include <vector>
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
#include "source/opt/build_module.h"
#include "source/opt/ir_context.h"
#include "spirv-tools/libspirv.h"
namespace spvtools {
namespace fuzz {
// Returns true if and only if the given binaries are bit-wise equal.
bool IsEqual(spv_target_env env, const std::vector<uint32_t>& expected_binary,
const std::vector<uint32_t>& actual_binary);
// Assembles the given text and returns true if and only if the resulting binary
// is bit-wise equal to the given binary.
bool IsEqual(spv_target_env env, const std::string& expected_text,
const std::vector<uint32_t>& actual_binary);
// Assembles the given text and turns the given IR into binary, then returns
// true if and only if the resulting binaries are bit-wise equal.
bool IsEqual(spv_target_env env, const std::string& expected_text,
const opt::IRContext* actual_ir);
// Turns the given IRs into binaries, then returns true if and only if the
// resulting binaries are bit-wise equal.
bool IsEqual(spv_target_env env, const opt::IRContext* ir_1,
const opt::IRContext* ir_2);
// Assembles the given IR context and returns true if and only if
// the resulting binary is valid.
bool IsValid(spv_target_env env, const opt::IRContext* ir);
// Assembles the given IR context, then returns its disassembly as a string.
// Useful for debugging.
std::string ToString(spv_target_env env, const opt::IRContext* ir);
// Returns the disassembly of the given binary as a string.
// Useful for debugging.
std::string ToString(spv_target_env env, const std::vector<uint32_t>& binary);
// Assembly options for writing fuzzer tests. It simplifies matters if
// numeric ids do not change.
const uint32_t kFuzzAssembleOption =
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS;
// Disassembly options for writing fuzzer tests.
const uint32_t kFuzzDisassembleOption =
SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | SPV_BINARY_TO_TEXT_OPTION_INDENT;
// A silent message consumer.
const spvtools::MessageConsumer kSilentConsumer =
[](spv_message_level_t, const char*, const spv_position_t&,
const char*) -> void {};
const spvtools::MessageConsumer kConsoleMessageConsumer =
[](spv_message_level_t level, const char*, const spv_position_t& position,
const char* message) -> void {
switch (level) {
case SPV_MSG_FATAL:
case SPV_MSG_INTERNAL_ERROR:
case SPV_MSG_ERROR:
std::cerr << "error: line " << position.index << ": " << message
<< std::endl;
break;
case SPV_MSG_WARNING:
std::cout << "warning: line " << position.index << ": " << message
<< std::endl;
break;
case SPV_MSG_INFO:
std::cout << "info: line " << position.index << ": " << message
<< std::endl;
break;
default:
break;
}
};
// Dumps the SPIRV-V module in |context| to file |filename|. Useful for
// interactive debugging.
void DumpShader(opt::IRContext* context, const char* filename);
// Dumps |binary| to file |filename|. Useful for interactive debugging.
void DumpShader(const std::vector<uint32_t>& binary, const char* filename);
// Dumps |transformations| to file |filename| in JSON format. Useful for
// interactive debugging.
void DumpTransformationsJson(
const protobufs::TransformationSequence& transformations,
const char* filename);
} // namespace fuzz
} // namespace spvtools
#endif // TEST_FUZZ_FUZZ_TEST_UTIL_H_

View File

@@ -1,393 +0,0 @@
// Copyright (c) 2019 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 "source/fuzz/fuzzer_pass_add_useful_constructs.h"
#include "source/fuzz/pseudo_random_generator.h"
#include "source/fuzz/uniform_buffer_element_descriptor.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
namespace fuzz {
namespace {
bool AddFactHelper(
FactManager* fact_manager, opt::IRContext* context, uint32_t word,
const protobufs::UniformBufferElementDescriptor& descriptor) {
protobufs::FactConstantUniform constant_uniform_fact;
constant_uniform_fact.add_constant_word(word);
*constant_uniform_fact.mutable_uniform_buffer_element_descriptor() =
descriptor;
protobufs::Fact fact;
*fact.mutable_constant_uniform_fact() = constant_uniform_fact;
return fact_manager->AddFact(fact, context);
}
TEST(FuzzerPassAddUsefulConstructsTest, CheckBasicStuffIsAdded) {
// The SPIR-V came from the following empty GLSL shader:
//
// #version 450
//
// void main()
// {
// }
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 450
OpName %4 "main"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0).get(), 100);
protobufs::TransformationSequence transformation_sequence;
FuzzerPassAddUsefulConstructs pass(context.get(), &fact_manager,
&fuzzer_context, &transformation_sequence);
pass.Apply();
ASSERT_TRUE(IsValid(env, context.get()));
std::string after = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 450
OpName %4 "main"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%100 = OpTypeBool
%101 = OpTypeInt 32 1
%102 = OpTypeInt 32 0
%103 = OpTypeFloat 32
%104 = OpConstantTrue %100
%105 = OpConstantFalse %100
%106 = OpConstant %101 0
%107 = OpConstant %101 1
%108 = OpConstant %102 0
%109 = OpConstant %102 1
%110 = OpConstant %103 0
%111 = OpConstant %103 1
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after, context.get()));
}
TEST(FuzzerPassAddUsefulConstructsTest,
CheckTypesIndicesAndConstantsAddedForUniformFacts) {
// The SPIR-V came from the following GLSL shader:
//
// #version 450
//
// struct S {
// int x;
// float y;
// int z;
// int w;
// };
//
// uniform buf {
// S s;
// uint w[10];
// };
//
// void main() {
// }
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 450
OpName %4 "main"
OpName %8 "S"
OpMemberName %8 0 "x"
OpMemberName %8 1 "y"
OpMemberName %8 2 "z"
OpMemberName %8 3 "w"
OpName %12 "buf"
OpMemberName %12 0 "s"
OpMemberName %12 1 "w"
OpName %14 ""
OpMemberDecorate %8 0 Offset 0
OpMemberDecorate %8 1 Offset 4
OpMemberDecorate %8 2 Offset 8
OpMemberDecorate %8 3 Offset 12
OpDecorate %11 ArrayStride 16
OpMemberDecorate %12 0 Offset 0
OpMemberDecorate %12 1 Offset 16
OpDecorate %12 Block
OpDecorate %14 DescriptorSet 0
OpDecorate %14 Binding 0
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypeFloat 32
%8 = OpTypeStruct %6 %7 %6 %6
%9 = OpTypeInt 32 0
%10 = OpConstant %9 10
%11 = OpTypeArray %9 %10
%12 = OpTypeStruct %8 %11
%13 = OpTypePointer Uniform %12
%14 = OpVariable %13 Uniform
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0).get(), 100);
protobufs::TransformationSequence transformation_sequence;
// Add some uniform facts.
// buf.s.x == 200
ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 200,
MakeUniformBufferElementDescriptor(0, 0, {0, 0})));
// buf.s.y == 0.5
const float float_value = 0.5;
uint32_t float_value_as_uint;
memcpy(&float_value_as_uint, &float_value, sizeof(float_value));
ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), float_value_as_uint,
MakeUniformBufferElementDescriptor(0, 0, {0, 1})));
// buf.s.z == 300
ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 300,
MakeUniformBufferElementDescriptor(0, 0, {0, 2})));
// buf.s.w == 400
ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 400,
MakeUniformBufferElementDescriptor(0, 0, {0, 3})));
// buf.w[6] = 22
ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 22,
MakeUniformBufferElementDescriptor(0, 0, {1, 6})));
// buf.w[8] = 23
ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 23,
MakeUniformBufferElementDescriptor(0, 0, {1, 8})));
// Assert some things about the module that are not true prior to adding the
// pass
{
// No uniform int pointer
opt::analysis::Integer temp_type_signed_int(32, true);
opt::analysis::Integer* registered_type_signed_int =
context->get_type_mgr()
->GetRegisteredType(&temp_type_signed_int)
->AsInteger();
opt::analysis::Pointer type_pointer_uniform_signed_int(
registered_type_signed_int, SpvStorageClassUniform);
ASSERT_EQ(0,
context->get_type_mgr()->GetId(&type_pointer_uniform_signed_int));
// No uniform uint pointer
opt::analysis::Integer temp_type_unsigned_int(32, false);
opt::analysis::Integer* registered_type_unsigned_int =
context->get_type_mgr()
->GetRegisteredType(&temp_type_unsigned_int)
->AsInteger();
opt::analysis::Pointer type_pointer_uniform_unsigned_int(
registered_type_unsigned_int, SpvStorageClassUniform);
ASSERT_EQ(
0, context->get_type_mgr()->GetId(&type_pointer_uniform_unsigned_int));
// No uniform float pointer
opt::analysis::Float temp_type_float(32);
opt::analysis::Float* registered_type_float =
context->get_type_mgr()->GetRegisteredType(&temp_type_float)->AsFloat();
opt::analysis::Pointer type_pointer_uniform_float(registered_type_float,
SpvStorageClassUniform);
ASSERT_EQ(0, context->get_type_mgr()->GetId(&type_pointer_uniform_float));
// No int constants 200, 300 nor 400
opt::analysis::IntConstant int_constant_200(registered_type_signed_int,
{200});
opt::analysis::IntConstant int_constant_300(registered_type_signed_int,
{300});
opt::analysis::IntConstant int_constant_400(registered_type_signed_int,
{400});
ASSERT_EQ(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_200));
ASSERT_EQ(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_300));
ASSERT_EQ(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_400));
// No float constant 0.5
opt::analysis::FloatConstant float_constant_zero_point_five(
registered_type_float, {float_value_as_uint});
ASSERT_EQ(nullptr, context->get_constant_mgr()->FindConstant(
&float_constant_zero_point_five));
// No uint constant 22
opt::analysis::IntConstant uint_constant_22(registered_type_unsigned_int,
{22});
ASSERT_EQ(nullptr,
context->get_constant_mgr()->FindConstant(&uint_constant_22));
// No uint constant 23
opt::analysis::IntConstant uint_constant_23(registered_type_unsigned_int,
{23});
ASSERT_EQ(nullptr,
context->get_constant_mgr()->FindConstant(&uint_constant_23));
// No int constants 0, 1, 2, 3, 6, 8
opt::analysis::IntConstant int_constant_0(registered_type_signed_int, {0});
opt::analysis::IntConstant int_constant_1(registered_type_signed_int, {1});
opt::analysis::IntConstant int_constant_2(registered_type_signed_int, {2});
opt::analysis::IntConstant int_constant_3(registered_type_signed_int, {3});
opt::analysis::IntConstant int_constant_6(registered_type_signed_int, {6});
opt::analysis::IntConstant int_constant_8(registered_type_signed_int, {8});
ASSERT_EQ(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_0));
ASSERT_EQ(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_1));
ASSERT_EQ(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_2));
ASSERT_EQ(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_3));
ASSERT_EQ(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_6));
ASSERT_EQ(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_8));
}
FuzzerPassAddUsefulConstructs pass(context.get(), &fact_manager,
&fuzzer_context, &transformation_sequence);
pass.Apply();
ASSERT_TRUE(IsValid(env, context.get()));
// Now assert some things about the module that should be true following the
// pass.
// We reconstruct all necessary types and constants to guard against the type
// and constant managers for the module having been invalidated.
{
// Uniform int pointer now present
opt::analysis::Integer temp_type_signed_int(32, true);
opt::analysis::Integer* registered_type_signed_int =
context->get_type_mgr()
->GetRegisteredType(&temp_type_signed_int)
->AsInteger();
opt::analysis::Pointer type_pointer_uniform_signed_int(
registered_type_signed_int, SpvStorageClassUniform);
ASSERT_NE(0,
context->get_type_mgr()->GetId(&type_pointer_uniform_signed_int));
// Uniform uint pointer now present
opt::analysis::Integer temp_type_unsigned_int(32, false);
opt::analysis::Integer* registered_type_unsigned_int =
context->get_type_mgr()
->GetRegisteredType(&temp_type_unsigned_int)
->AsInteger();
opt::analysis::Pointer type_pointer_uniform_unsigned_int(
registered_type_unsigned_int, SpvStorageClassUniform);
ASSERT_NE(
0, context->get_type_mgr()->GetId(&type_pointer_uniform_unsigned_int));
// Uniform float pointer now present
opt::analysis::Float temp_type_float(32);
opt::analysis::Float* registered_type_float =
context->get_type_mgr()->GetRegisteredType(&temp_type_float)->AsFloat();
opt::analysis::Pointer type_pointer_uniform_float(registered_type_float,
SpvStorageClassUniform);
ASSERT_NE(0, context->get_type_mgr()->GetId(&type_pointer_uniform_float));
// int constants 200, 300, 400 now present
opt::analysis::IntConstant int_constant_200(registered_type_signed_int,
{200});
opt::analysis::IntConstant int_constant_300(registered_type_signed_int,
{300});
opt::analysis::IntConstant int_constant_400(registered_type_signed_int,
{400});
ASSERT_NE(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_200));
ASSERT_NE(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_300));
ASSERT_NE(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_400));
// float constant 0.5 now present
opt::analysis::FloatConstant float_constant_zero_point_five(
registered_type_float, {float_value_as_uint});
ASSERT_NE(nullptr, context->get_constant_mgr()->FindConstant(
&float_constant_zero_point_five));
// uint constant 22 now present
opt::analysis::IntConstant uint_constant_22(registered_type_unsigned_int,
{22});
ASSERT_NE(nullptr,
context->get_constant_mgr()->FindConstant(&uint_constant_22));
// uint constant 23 now present
opt::analysis::IntConstant uint_constant_23(registered_type_unsigned_int,
{23});
ASSERT_NE(nullptr,
context->get_constant_mgr()->FindConstant(&uint_constant_23));
// int constants 0, 1, 2, 3, 6, 8 now present
opt::analysis::IntConstant int_constant_0(registered_type_signed_int, {0});
opt::analysis::IntConstant int_constant_1(registered_type_signed_int, {1});
opt::analysis::IntConstant int_constant_2(registered_type_signed_int, {2});
opt::analysis::IntConstant int_constant_3(registered_type_signed_int, {3});
opt::analysis::IntConstant int_constant_6(registered_type_signed_int, {6});
opt::analysis::IntConstant int_constant_8(registered_type_signed_int, {8});
ASSERT_NE(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_0));
ASSERT_NE(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_1));
ASSERT_NE(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_2));
ASSERT_NE(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_3));
ASSERT_NE(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_6));
ASSERT_NE(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_8));
}
}
} // namespace
} // namespace fuzz
} // namespace spvtools

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,69 +0,0 @@
// Copyright (c) 2019 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 "source/fuzz/instruction_descriptor.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
namespace fuzz {
namespace {
TEST(InstructionDescriptorTest, BasicTest) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 0
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%10 = OpTypeInt 32 1
%11 = OpTypePointer Function %10
%13 = OpConstant %10 2
%32 = OpConstant %10 0
%4 = OpFunction %2 None %3
%5 = OpLabel
%164 = OpVariable %11 Function
%165 = OpVariable %11 Function
OpBranch %16
%16 = OpLabel
OpStore %164 %32
OpStore %165 %13
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
for (auto& function : *context->module()) {
for (auto& block : function) {
for (auto inst_it = block.cbegin(); inst_it != block.cend(); ++inst_it) {
ASSERT_EQ(&*inst_it,
FindInstruction(MakeInstructionDescriptor(block, inst_it),
context.get()));
}
}
}
}
} // namespace
} // namespace fuzz
} // namespace spvtools

View File

@@ -1,141 +0,0 @@
// Copyright (c) 2019 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 "source/fuzz/transformation_add_constant_boolean.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
namespace fuzz {
namespace {
TEST(TransformationAddConstantBooleanTest, NeitherPresentInitiallyAddBoth) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
%2 = OpTypeVoid
%6 = OpTypeBool
%3 = OpTypeFunction %2
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
// True and false can both be added as neither is present.
ASSERT_TRUE(TransformationAddConstantBoolean(7, true).IsApplicable(
context.get(), fact_manager));
ASSERT_TRUE(TransformationAddConstantBoolean(7, false).IsApplicable(
context.get(), fact_manager));
// Id 5 is already taken.
ASSERT_FALSE(TransformationAddConstantBoolean(5, true).IsApplicable(
context.get(), fact_manager));
auto add_true = TransformationAddConstantBoolean(7, true);
auto add_false = TransformationAddConstantBoolean(8, false);
ASSERT_TRUE(add_true.IsApplicable(context.get(), fact_manager));
add_true.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
// Having added true, we cannot add it again with the same id.
ASSERT_FALSE(add_true.IsApplicable(context.get(), fact_manager));
// But we can add it with a different id.
auto add_true_again = TransformationAddConstantBoolean(100, true);
ASSERT_TRUE(add_true_again.IsApplicable(context.get(), fact_manager));
add_true_again.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
ASSERT_TRUE(add_false.IsApplicable(context.get(), fact_manager));
add_false.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
// Having added false, we cannot add it again with the same id.
ASSERT_FALSE(add_false.IsApplicable(context.get(), fact_manager));
// But we can add it with a different id.
auto add_false_again = TransformationAddConstantBoolean(101, false);
ASSERT_TRUE(add_false_again.IsApplicable(context.get(), fact_manager));
add_false_again.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
std::string after_transformation = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
%2 = OpTypeVoid
%6 = OpTypeBool
%3 = OpTypeFunction %2
%7 = OpConstantTrue %6
%100 = OpConstantTrue %6
%8 = OpConstantFalse %6
%101 = OpConstantFalse %6
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
TEST(TransformationAddConstantBooleanTest, NoOpTypeBoolPresent) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
// Neither true nor false can be added as OpTypeBool is not present.
ASSERT_FALSE(TransformationAddConstantBoolean(6, true).IsApplicable(
context.get(), fact_manager));
ASSERT_FALSE(TransformationAddConstantBoolean(6, false).IsApplicable(
context.get(), fact_manager));
}
} // namespace
} // namespace fuzz
} // namespace spvtools

View File

@@ -1,187 +0,0 @@
// Copyright (c) 2019 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 "source/fuzz/transformation_add_constant_scalar.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
namespace fuzz {
namespace {
TEST(TransformationAddConstantScalarTest, BasicTest) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %8 "x"
OpName %12 "y"
OpName %16 "z"
OpDecorate %8 RelaxedPrecision
OpDecorate %12 RelaxedPrecision
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 1
%10 = OpTypeInt 32 0
%11 = OpTypePointer Function %10
%13 = OpConstant %10 2
%14 = OpTypeFloat 32
%15 = OpTypePointer Function %14
%17 = OpConstant %14 3
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%12 = OpVariable %11 Function
%16 = OpVariable %15 Function
OpStore %8 %9
OpStore %12 %13
OpStore %16 %17
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
const float float_values[2] = {3.0, 30.0};
uint32_t uint_for_float[2];
memcpy(uint_for_float, float_values, sizeof(float_values));
auto add_signed_int_1 = TransformationAddConstantScalar(100, 6, {1});
auto add_signed_int_10 = TransformationAddConstantScalar(101, 6, {10});
auto add_unsigned_int_2 = TransformationAddConstantScalar(102, 10, {2});
auto add_unsigned_int_20 = TransformationAddConstantScalar(103, 10, {20});
auto add_float_3 =
TransformationAddConstantScalar(104, 14, {uint_for_float[0]});
auto add_float_30 =
TransformationAddConstantScalar(105, 14, {uint_for_float[1]});
auto bad_add_float_30_id_already_used =
TransformationAddConstantScalar(104, 14, {uint_for_float[1]});
auto bad_id_already_used = TransformationAddConstantScalar(1, 6, {1});
auto bad_no_data = TransformationAddConstantScalar(100, 6, {});
auto bad_too_much_data = TransformationAddConstantScalar(100, 6, {1, 2});
auto bad_type_id_does_not_exist =
TransformationAddConstantScalar(108, 2020, {uint_for_float[0]});
auto bad_type_id_is_not_a_type = TransformationAddConstantScalar(109, 9, {0});
auto bad_type_id_is_void = TransformationAddConstantScalar(110, 2, {0});
auto bad_type_id_is_pointer = TransformationAddConstantScalar(111, 11, {0});
// Id is already in use.
ASSERT_FALSE(bad_id_already_used.IsApplicable(context.get(), fact_manager));
// At least one word of data must be provided.
ASSERT_FALSE(bad_no_data.IsApplicable(context.get(), fact_manager));
// Cannot give two data words for a 32-bit type.
ASSERT_FALSE(bad_too_much_data.IsApplicable(context.get(), fact_manager));
// Type id does not exist
ASSERT_FALSE(
bad_type_id_does_not_exist.IsApplicable(context.get(), fact_manager));
// Type id is not a type
ASSERT_FALSE(
bad_type_id_is_not_a_type.IsApplicable(context.get(), fact_manager));
// Type id is void
ASSERT_FALSE(bad_type_id_is_void.IsApplicable(context.get(), fact_manager));
// Type id is pointer
ASSERT_FALSE(
bad_type_id_is_pointer.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(add_signed_int_1.IsApplicable(context.get(), fact_manager));
add_signed_int_1.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
ASSERT_TRUE(add_signed_int_10.IsApplicable(context.get(), fact_manager));
add_signed_int_10.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
ASSERT_TRUE(add_unsigned_int_2.IsApplicable(context.get(), fact_manager));
add_unsigned_int_2.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
ASSERT_TRUE(add_unsigned_int_20.IsApplicable(context.get(), fact_manager));
add_unsigned_int_20.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
ASSERT_TRUE(add_float_3.IsApplicable(context.get(), fact_manager));
add_float_3.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
ASSERT_TRUE(add_float_30.IsApplicable(context.get(), fact_manager));
add_float_30.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
ASSERT_FALSE(bad_add_float_30_id_already_used.IsApplicable(context.get(),
fact_manager));
std::string after_transformation = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %8 "x"
OpName %12 "y"
OpName %16 "z"
OpDecorate %8 RelaxedPrecision
OpDecorate %12 RelaxedPrecision
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 1
%10 = OpTypeInt 32 0
%11 = OpTypePointer Function %10
%13 = OpConstant %10 2
%14 = OpTypeFloat 32
%15 = OpTypePointer Function %14
%17 = OpConstant %14 3
%100 = OpConstant %6 1
%101 = OpConstant %6 10
%102 = OpConstant %10 2
%103 = OpConstant %10 20
%104 = OpConstant %14 3
%105 = OpConstant %14 30
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%12 = OpVariable %11 Function
%16 = OpVariable %15 Function
OpStore %8 %9
OpStore %12 %13
OpStore %16 %17
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
} // namespace
} // namespace fuzz
} // namespace spvtools

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,194 +0,0 @@
// Copyright (c) 2019 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 "source/fuzz/transformation_add_no_contraction_decoration.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
namespace fuzz {
namespace {
TEST(TransformationAddNoContractionDecorationTest, BasicScenarios) {
// This is a simple transformation and this test handles the main cases.
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %8 "x"
OpName %10 "y"
OpName %14 "i"
OpDecorate %32 NoContraction
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%11 = OpConstant %6 2
%12 = OpTypeInt 32 1
%13 = OpTypePointer Function %12
%15 = OpConstant %12 0
%22 = OpConstant %12 10
%23 = OpTypeBool
%31 = OpConstant %6 3.5999999
%38 = OpConstant %12 1
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
%14 = OpVariable %13 Function
OpStore %8 %9
OpStore %10 %11
OpStore %14 %15
OpBranch %16
%16 = OpLabel
OpLoopMerge %18 %19 None
OpBranch %20
%20 = OpLabel
%21 = OpLoad %12 %14
%24 = OpSLessThan %23 %21 %22
OpBranchConditional %24 %17 %18
%17 = OpLabel
%25 = OpLoad %6 %10
%26 = OpLoad %6 %10
%27 = OpFMul %6 %25 %26
%28 = OpLoad %6 %8
%29 = OpFAdd %6 %28 %27
OpStore %8 %29
%30 = OpLoad %6 %10
%32 = OpFDiv %6 %30 %31
OpStore %10 %32
%33 = OpLoad %12 %14
%34 = OpConvertSToF %6 %33
%35 = OpLoad %6 %8
%36 = OpFAdd %6 %35 %34
OpStore %8 %36
OpBranch %19
%19 = OpLabel
%37 = OpLoad %12 %14
%39 = OpIAdd %12 %37 %38
OpStore %14 %39
OpBranch %16
%18 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
// Invalid: 200 is not an id
ASSERT_FALSE(TransformationAddNoContractionDecoration(200).IsApplicable(
context.get(), fact_manager));
// Invalid: 17 is a block id
ASSERT_FALSE(TransformationAddNoContractionDecoration(17).IsApplicable(
context.get(), fact_manager));
// Invalid: 24 is not arithmetic
ASSERT_FALSE(TransformationAddNoContractionDecoration(24).IsApplicable(
context.get(), fact_manager));
// It is valid to add NoContraction to each of these ids (and it's fine to
// have duplicates of the decoration, in the case of 32).
for (uint32_t result_id : {32u, 32u, 27u, 29u, 39u}) {
TransformationAddNoContractionDecoration transformation(result_id);
ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
transformation.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
}
std::string after_transformation = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %8 "x"
OpName %10 "y"
OpName %14 "i"
OpDecorate %32 NoContraction
OpDecorate %32 NoContraction
OpDecorate %32 NoContraction
OpDecorate %27 NoContraction
OpDecorate %29 NoContraction
OpDecorate %39 NoContraction
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%11 = OpConstant %6 2
%12 = OpTypeInt 32 1
%13 = OpTypePointer Function %12
%15 = OpConstant %12 0
%22 = OpConstant %12 10
%23 = OpTypeBool
%31 = OpConstant %6 3.5999999
%38 = OpConstant %12 1
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
%14 = OpVariable %13 Function
OpStore %8 %9
OpStore %10 %11
OpStore %14 %15
OpBranch %16
%16 = OpLabel
OpLoopMerge %18 %19 None
OpBranch %20
%20 = OpLabel
%21 = OpLoad %12 %14
%24 = OpSLessThan %23 %21 %22
OpBranchConditional %24 %17 %18
%17 = OpLabel
%25 = OpLoad %6 %10
%26 = OpLoad %6 %10
%27 = OpFMul %6 %25 %26
%28 = OpLoad %6 %8
%29 = OpFAdd %6 %28 %27
OpStore %8 %29
%30 = OpLoad %6 %10
%32 = OpFDiv %6 %30 %31
OpStore %10 %32
%33 = OpLoad %12 %14
%34 = OpConvertSToF %6 %33
%35 = OpLoad %6 %8
%36 = OpFAdd %6 %35 %34
OpStore %8 %36
OpBranch %19
%19 = OpLabel
%37 = OpLoad %12 %14
%39 = OpIAdd %12 %37 %38
OpStore %14 %39
OpBranch %16
%18 = OpLabel
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
} // namespace
} // namespace fuzz
} // namespace spvtools

View File

@@ -1,80 +0,0 @@
// Copyright (c) 2019 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 "source/fuzz/transformation_add_type_boolean.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
namespace fuzz {
namespace {
TEST(TransformationAddTypeBooleanTest, BasicTest) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
// Not applicable because id 1 is already in use.
ASSERT_FALSE(TransformationAddTypeBoolean(1).IsApplicable(context.get(),
fact_manager));
auto add_type_bool = TransformationAddTypeBoolean(100);
ASSERT_TRUE(add_type_bool.IsApplicable(context.get(), fact_manager));
add_type_bool.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
// Not applicable as we already have this type now.
ASSERT_FALSE(TransformationAddTypeBoolean(101).IsApplicable(context.get(),
fact_manager));
std::string after_transformation = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%100 = OpTypeBool
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
} // namespace
} // namespace fuzz
} // namespace spvtools

View File

@@ -1,80 +0,0 @@
// Copyright (c) 2019 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 "source/fuzz/transformation_add_type_float.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
namespace fuzz {
namespace {
TEST(TransformationAddTypeFloatTest, BasicTest) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
// Not applicable because id 1 is already in use.
ASSERT_FALSE(TransformationAddTypeFloat(1, 32).IsApplicable(context.get(),
fact_manager));
auto add_type_float_32 = TransformationAddTypeFloat(100, 32);
ASSERT_TRUE(add_type_float_32.IsApplicable(context.get(), fact_manager));
add_type_float_32.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
// Not applicable as we already have this type now.
ASSERT_FALSE(TransformationAddTypeFloat(101, 32).IsApplicable(context.get(),
fact_manager));
std::string after_transformation = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%100 = OpTypeFloat 32
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
} // namespace
} // namespace fuzz
} // namespace spvtools

View File

@@ -1,93 +0,0 @@
// Copyright (c) 2019 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 "source/fuzz/transformation_add_type_int.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
namespace fuzz {
namespace {
TEST(TransformationAddTypeIntTest, BasicTest) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
// Not applicable because id 1 is already in use.
ASSERT_FALSE(TransformationAddTypeInt(1, 32, false)
.IsApplicable(context.get(), fact_manager));
auto add_type_signed_int_32 = TransformationAddTypeInt(100, 32, true);
auto add_type_unsigned_int_32 = TransformationAddTypeInt(101, 32, false);
auto add_type_signed_int_32_again = TransformationAddTypeInt(102, 32, true);
auto add_type_unsigned_int_32_again =
TransformationAddTypeInt(103, 32, false);
ASSERT_TRUE(add_type_signed_int_32.IsApplicable(context.get(), fact_manager));
add_type_signed_int_32.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
ASSERT_TRUE(
add_type_unsigned_int_32.IsApplicable(context.get(), fact_manager));
add_type_unsigned_int_32.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
// Not applicable as we already have these types now.
ASSERT_FALSE(
add_type_signed_int_32_again.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
add_type_unsigned_int_32_again.IsApplicable(context.get(), fact_manager));
std::string after_transformation = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%100 = OpTypeInt 32 1
%101 = OpTypeInt 32 0
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
} // namespace
} // namespace fuzz
} // namespace spvtools

View File

@@ -1,206 +0,0 @@
// Copyright (c) 2019 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 "source/fuzz/transformation_add_type_pointer.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
namespace fuzz {
namespace {
TEST(TransformationAddTypePointerTest, BasicTest) {
// The SPIR-V was obtained from this GLSL:
//
// #version 450
//
// int x;
// float y;
// vec2 z;
//
// struct T {
// int a, b;
// };
//
// struct S {
// T t;
// int u;
// };
//
// void main() {
// S myS = S(T(1, 2), 3);
// myS.u = x;
// }
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 450
OpName %4 "main"
OpName %7 "T"
OpMemberName %7 0 "a"
OpMemberName %7 1 "b"
OpName %8 "S"
OpMemberName %8 0 "t"
OpMemberName %8 1 "u"
OpName %10 "myS"
OpName %17 "x"
OpName %23 "y"
OpName %26 "z"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypeStruct %6 %6
%8 = OpTypeStruct %7 %6
%9 = OpTypePointer Function %8
%11 = OpConstant %6 1
%12 = OpConstant %6 2
%13 = OpConstantComposite %7 %11 %12
%14 = OpConstant %6 3
%15 = OpConstantComposite %8 %13 %14
%16 = OpTypePointer Private %6
%17 = OpVariable %16 Private
%19 = OpTypePointer Function %6
%21 = OpTypeFloat 32
%22 = OpTypePointer Private %21
%23 = OpVariable %22 Private
%24 = OpTypeVector %21 2
%25 = OpTypePointer Private %24
%26 = OpVariable %25 Private
%4 = OpFunction %2 None %3
%5 = OpLabel
%10 = OpVariable %9 Function
OpStore %10 %15
%18 = OpLoad %6 %17
%20 = OpAccessChain %19 %10 %11
OpStore %20 %18
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
auto bad_type_id_does_not_exist =
TransformationAddTypePointer(100, SpvStorageClassFunction, 101);
auto bad_type_id_is_not_type =
TransformationAddTypePointer(100, SpvStorageClassFunction, 23);
auto bad_result_id_is_not_fresh =
TransformationAddTypePointer(17, SpvStorageClassFunction, 21);
auto good_new_private_pointer_to_t =
TransformationAddTypePointer(101, SpvStorageClassPrivate, 7);
auto good_new_uniform_pointer_to_t =
TransformationAddTypePointer(102, SpvStorageClassUniform, 7);
auto good_another_function_pointer_to_s =
TransformationAddTypePointer(103, SpvStorageClassFunction, 8);
auto good_new_uniform_pointer_to_s =
TransformationAddTypePointer(104, SpvStorageClassUniform, 8);
auto good_another_private_pointer_to_float =
TransformationAddTypePointer(105, SpvStorageClassPrivate, 21);
auto good_new_private_pointer_to_private_pointer_to_float =
TransformationAddTypePointer(106, SpvStorageClassPrivate, 105);
auto good_new_uniform_pointer_to_vec2 =
TransformationAddTypePointer(107, SpvStorageClassUniform, 24);
auto good_new_private_pointer_to_uniform_pointer_to_vec2 =
TransformationAddTypePointer(108, SpvStorageClassPrivate, 107);
ASSERT_FALSE(
bad_type_id_does_not_exist.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
bad_type_id_is_not_type.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
bad_result_id_is_not_fresh.IsApplicable(context.get(), fact_manager));
for (auto& transformation :
{good_new_private_pointer_to_t, good_new_uniform_pointer_to_t,
good_another_function_pointer_to_s, good_new_uniform_pointer_to_s,
good_another_private_pointer_to_float,
good_new_private_pointer_to_private_pointer_to_float,
good_new_uniform_pointer_to_vec2,
good_new_private_pointer_to_uniform_pointer_to_vec2}) {
ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
transformation.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
}
std::string after_transformation = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 450
OpName %4 "main"
OpName %7 "T"
OpMemberName %7 0 "a"
OpMemberName %7 1 "b"
OpName %8 "S"
OpMemberName %8 0 "t"
OpMemberName %8 1 "u"
OpName %10 "myS"
OpName %17 "x"
OpName %23 "y"
OpName %26 "z"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypeStruct %6 %6
%8 = OpTypeStruct %7 %6
%9 = OpTypePointer Function %8
%11 = OpConstant %6 1
%12 = OpConstant %6 2
%13 = OpConstantComposite %7 %11 %12
%14 = OpConstant %6 3
%15 = OpConstantComposite %8 %13 %14
%16 = OpTypePointer Private %6
%17 = OpVariable %16 Private
%19 = OpTypePointer Function %6
%21 = OpTypeFloat 32
%22 = OpTypePointer Private %21
%23 = OpVariable %22 Private
%24 = OpTypeVector %21 2
%25 = OpTypePointer Private %24
%26 = OpVariable %25 Private
%101 = OpTypePointer Private %7
%102 = OpTypePointer Uniform %7
%103 = OpTypePointer Function %8
%104 = OpTypePointer Uniform %8
%105 = OpTypePointer Private %21
%106 = OpTypePointer Private %105
%107 = OpTypePointer Uniform %24
%108 = OpTypePointer Private %107
%4 = OpFunction %2 None %3
%5 = OpLabel
%10 = OpVariable %9 Function
OpStore %10 %15
%18 = OpLoad %6 %17
%20 = OpAccessChain %19 %10 %11
OpStore %20 %18
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
} // namespace
} // namespace fuzz
} // namespace spvtools

File diff suppressed because it is too large Load Diff

View File

@@ -1,398 +0,0 @@
// Copyright (c) 2019 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 "source/fuzz/transformation_composite_extract.h"
#include "source/fuzz/instruction_descriptor.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
namespace fuzz {
namespace {
TEST(TransformationCompositeExtractTest, BasicTest) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %8 "a"
OpName %10 "b"
OpName %17 "FunnyPoint"
OpMemberName %17 0 "x"
OpMemberName %17 1 "y"
OpMemberName %17 2 "z"
OpName %19 "p"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%12 = OpTypeBool
%16 = OpTypeFloat 32
%17 = OpTypeStruct %16 %16 %6
%81 = OpTypeStruct %17 %16
%18 = OpTypePointer Function %17
%20 = OpConstant %6 0
%23 = OpTypePointer Function %16
%26 = OpConstant %6 1
%30 = OpConstant %6 2
%80 = OpUndef %16
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
%19 = OpVariable %18 Function
%9 = OpLoad %6 %8
%11 = OpLoad %6 %10
%100 = OpCompositeConstruct %17 %80 %80 %26
%104 = OpCompositeConstruct %81 %100 %80
%13 = OpIEqual %12 %9 %11
OpSelectionMerge %15 None
OpBranchConditional %13 %14 %25
%14 = OpLabel
%21 = OpLoad %6 %8
%22 = OpConvertSToF %16 %21
%101 = OpCompositeConstruct %17 %22 %80 %30
%24 = OpAccessChain %23 %19 %20
OpStore %24 %22
OpBranch %15
%25 = OpLabel
%27 = OpLoad %6 %10
%28 = OpConvertSToF %16 %27
%102 = OpCompositeConstruct %17 %80 %28 %27
%29 = OpAccessChain %23 %19 %26
OpStore %29 %28
OpBranch %15
%15 = OpLabel
%31 = OpAccessChain %23 %19 %20
%32 = OpLoad %16 %31
%33 = OpAccessChain %23 %19 %26
%34 = OpLoad %16 %33
%103 = OpCompositeConstruct %17 %34 %32 %9
%35 = OpFAdd %16 %32 %34
%36 = OpConvertFToS %6 %35
%37 = OpAccessChain %7 %19 %30
OpStore %37 %36
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
// Instruction does not exist.
ASSERT_FALSE(TransformationCompositeExtract(
MakeInstructionDescriptor(36, SpvOpIAdd, 0), 200, 101, {0})
.IsApplicable(context.get(), fact_manager));
// Id for composite is not a composite.
ASSERT_FALSE(TransformationCompositeExtract(
MakeInstructionDescriptor(36, SpvOpIAdd, 0), 200, 27, {})
.IsApplicable(context.get(), fact_manager));
// Composite does not dominate instruction being inserted before.
ASSERT_FALSE(
TransformationCompositeExtract(
MakeInstructionDescriptor(37, SpvOpAccessChain, 0), 200, 101, {0})
.IsApplicable(context.get(), fact_manager));
// Too many indices for extraction from struct composite.
ASSERT_FALSE(
TransformationCompositeExtract(
MakeInstructionDescriptor(24, SpvOpAccessChain, 0), 200, 101, {0, 0})
.IsApplicable(context.get(), fact_manager));
// Too many indices for extraction from struct composite.
ASSERT_FALSE(
TransformationCompositeExtract(
MakeInstructionDescriptor(13, SpvOpIEqual, 0), 200, 104, {0, 0, 0})
.IsApplicable(context.get(), fact_manager));
// Out of bounds index for extraction from struct composite.
ASSERT_FALSE(
TransformationCompositeExtract(
MakeInstructionDescriptor(13, SpvOpIEqual, 0), 200, 104, {0, 3})
.IsApplicable(context.get(), fact_manager));
// Result id already used.
ASSERT_FALSE(TransformationCompositeExtract(
MakeInstructionDescriptor(35, SpvOpFAdd, 0), 80, 103, {0})
.IsApplicable(context.get(), fact_manager));
TransformationCompositeExtract transformation_1(
MakeInstructionDescriptor(36, SpvOpConvertFToS, 0), 201, 100, {2});
ASSERT_TRUE(transformation_1.IsApplicable(context.get(), fact_manager));
transformation_1.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
TransformationCompositeExtract transformation_2(
MakeInstructionDescriptor(37, SpvOpAccessChain, 0), 202, 104, {0, 2});
ASSERT_TRUE(transformation_2.IsApplicable(context.get(), fact_manager));
transformation_2.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
TransformationCompositeExtract transformation_3(
MakeInstructionDescriptor(29, SpvOpAccessChain, 0), 203, 104, {0});
ASSERT_TRUE(transformation_3.IsApplicable(context.get(), fact_manager));
transformation_3.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
TransformationCompositeExtract transformation_4(
MakeInstructionDescriptor(24, SpvOpStore, 0), 204, 101, {0});
ASSERT_TRUE(transformation_4.IsApplicable(context.get(), fact_manager));
transformation_4.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
TransformationCompositeExtract transformation_5(
MakeInstructionDescriptor(29, SpvOpBranch, 0), 205, 102, {2});
ASSERT_TRUE(transformation_5.IsApplicable(context.get(), fact_manager));
transformation_5.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
TransformationCompositeExtract transformation_6(
MakeInstructionDescriptor(37, SpvOpReturn, 0), 206, 103, {1});
ASSERT_TRUE(transformation_6.IsApplicable(context.get(), fact_manager));
transformation_6.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(201, {}),
MakeDataDescriptor(100, {2}),
context.get()));
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(202, {}),
MakeDataDescriptor(104, {0, 2}),
context.get()));
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(203, {}),
MakeDataDescriptor(104, {0}),
context.get()));
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(204, {}),
MakeDataDescriptor(101, {0}),
context.get()));
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(205, {}),
MakeDataDescriptor(102, {2}),
context.get()));
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(206, {}),
MakeDataDescriptor(103, {1}),
context.get()));
std::string after_transformation = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %8 "a"
OpName %10 "b"
OpName %17 "FunnyPoint"
OpMemberName %17 0 "x"
OpMemberName %17 1 "y"
OpMemberName %17 2 "z"
OpName %19 "p"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%12 = OpTypeBool
%16 = OpTypeFloat 32
%17 = OpTypeStruct %16 %16 %6
%81 = OpTypeStruct %17 %16
%18 = OpTypePointer Function %17
%20 = OpConstant %6 0
%23 = OpTypePointer Function %16
%26 = OpConstant %6 1
%30 = OpConstant %6 2
%80 = OpUndef %16
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
%19 = OpVariable %18 Function
%9 = OpLoad %6 %8
%11 = OpLoad %6 %10
%100 = OpCompositeConstruct %17 %80 %80 %26
%104 = OpCompositeConstruct %81 %100 %80
%13 = OpIEqual %12 %9 %11
OpSelectionMerge %15 None
OpBranchConditional %13 %14 %25
%14 = OpLabel
%21 = OpLoad %6 %8
%22 = OpConvertSToF %16 %21
%101 = OpCompositeConstruct %17 %22 %80 %30
%24 = OpAccessChain %23 %19 %20
%204 = OpCompositeExtract %16 %101 0
OpStore %24 %22
OpBranch %15
%25 = OpLabel
%27 = OpLoad %6 %10
%28 = OpConvertSToF %16 %27
%102 = OpCompositeConstruct %17 %80 %28 %27
%203 = OpCompositeExtract %17 %104 0
%29 = OpAccessChain %23 %19 %26
OpStore %29 %28
%205 = OpCompositeExtract %6 %102 2
OpBranch %15
%15 = OpLabel
%31 = OpAccessChain %23 %19 %20
%32 = OpLoad %16 %31
%33 = OpAccessChain %23 %19 %26
%34 = OpLoad %16 %33
%103 = OpCompositeConstruct %17 %34 %32 %9
%35 = OpFAdd %16 %32 %34
%201 = OpCompositeExtract %6 %100 2
%36 = OpConvertFToS %6 %35
%202 = OpCompositeExtract %6 %104 0 2
%37 = OpAccessChain %7 %19 %30
OpStore %37 %36
%206 = OpCompositeExtract %16 %103 1
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
TEST(TransformationCompositeExtractTest, IllegalInsertionPoints) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %51 %27
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %25 "buf"
OpMemberName %25 0 "value"
OpName %27 ""
OpName %51 "color"
OpMemberDecorate %25 0 Offset 0
OpDecorate %25 Block
OpDecorate %27 DescriptorSet 0
OpDecorate %27 Binding 0
OpDecorate %51 Location 0
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypeVector %6 4
%10 = OpConstant %6 0.300000012
%11 = OpConstant %6 0.400000006
%12 = OpConstant %6 0.5
%13 = OpConstant %6 1
%14 = OpConstantComposite %7 %10 %11 %12 %13
%15 = OpTypeInt 32 1
%18 = OpConstant %15 0
%25 = OpTypeStruct %6
%26 = OpTypePointer Uniform %25
%27 = OpVariable %26 Uniform
%28 = OpTypePointer Uniform %6
%32 = OpTypeBool
%103 = OpConstantTrue %32
%34 = OpConstant %6 0.100000001
%48 = OpConstant %15 1
%50 = OpTypePointer Output %7
%51 = OpVariable %50 Output
%100 = OpTypePointer Function %6
%4 = OpFunction %2 None %3
%5 = OpLabel
%101 = OpVariable %100 Function
%102 = OpVariable %100 Function
OpBranch %19
%19 = OpLabel
%60 = OpPhi %7 %14 %5 %58 %20
%59 = OpPhi %15 %18 %5 %49 %20
%29 = OpAccessChain %28 %27 %18
%30 = OpLoad %6 %29
%31 = OpConvertFToS %15 %30
%33 = OpSLessThan %32 %59 %31
OpLoopMerge %21 %20 None
OpBranchConditional %33 %20 %21
%20 = OpLabel
%39 = OpCompositeExtract %6 %60 0
%40 = OpFAdd %6 %39 %34
%55 = OpCompositeInsert %7 %40 %60 0
%44 = OpCompositeExtract %6 %60 1
%45 = OpFSub %6 %44 %34
%58 = OpCompositeInsert %7 %45 %55 1
%49 = OpIAdd %15 %59 %48
OpBranch %19
%21 = OpLabel
OpStore %51 %60
OpSelectionMerge %105 None
OpBranchConditional %103 %104 %105
%104 = OpLabel
OpBranch %105
%105 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
// Cannot insert before the OpVariables of a function.
ASSERT_FALSE(
TransformationCompositeExtract(
MakeInstructionDescriptor(101, SpvOpVariable, 0), 200, 14, {0})
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationCompositeExtract(
MakeInstructionDescriptor(101, SpvOpVariable, 1), 200, 14, {1})
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationCompositeExtract(
MakeInstructionDescriptor(102, SpvOpVariable, 0), 200, 14, {1})
.IsApplicable(context.get(), fact_manager));
// OK to insert right after the OpVariables.
ASSERT_FALSE(TransformationCompositeExtract(
MakeInstructionDescriptor(102, SpvOpBranch, 1), 200, 14, {1})
.IsApplicable(context.get(), fact_manager));
// Cannot insert before the OpPhis of a block.
ASSERT_FALSE(TransformationCompositeExtract(
MakeInstructionDescriptor(60, SpvOpPhi, 0), 200, 14, {2})
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationCompositeExtract(
MakeInstructionDescriptor(59, SpvOpPhi, 0), 200, 14, {3})
.IsApplicable(context.get(), fact_manager));
// OK to insert after the OpPhis.
ASSERT_TRUE(
TransformationCompositeExtract(
MakeInstructionDescriptor(59, SpvOpAccessChain, 0), 200, 14, {3})
.IsApplicable(context.get(), fact_manager));
// Cannot insert before OpLoopMerge
ASSERT_FALSE(TransformationCompositeExtract(
MakeInstructionDescriptor(33, SpvOpBranchConditional, 0),
200, 14, {3})
.IsApplicable(context.get(), fact_manager));
// Cannot insert before OpSelectionMerge
ASSERT_FALSE(TransformationCompositeExtract(
MakeInstructionDescriptor(21, SpvOpBranchConditional, 0),
200, 14, {2})
.IsApplicable(context.get(), fact_manager));
}
} // namespace
} // namespace fuzz
} // namespace spvtools

View File

@@ -1,593 +0,0 @@
// Copyright (c) 2019 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 <algorithm>
#include <set>
#include <unordered_set>
#include "source/fuzz/data_descriptor.h"
#include "source/fuzz/instruction_descriptor.h"
#include "source/fuzz/transformation_copy_object.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
namespace fuzz {
namespace {
TEST(TransformationCopyObjectTest, CopyBooleanConstants) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
%2 = OpTypeVoid
%6 = OpTypeBool
%7 = OpConstantTrue %6
%8 = OpConstantFalse %6
%3 = OpTypeFunction %2
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
ASSERT_EQ(0,
fact_manager.GetIdsForWhichSynonymsAreKnown(context.get()).size());
{
TransformationCopyObject copy_true(
7, MakeInstructionDescriptor(5, SpvOpReturn, 0), 100);
ASSERT_TRUE(copy_true.IsApplicable(context.get(), fact_manager));
copy_true.Apply(context.get(), &fact_manager);
std::vector<uint32_t> ids_for_which_synonyms_are_known =
fact_manager.GetIdsForWhichSynonymsAreKnown(context.get());
ASSERT_EQ(2, ids_for_which_synonyms_are_known.size());
ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(),
ids_for_which_synonyms_are_known.end(),
7) != ids_for_which_synonyms_are_known.end());
ASSERT_EQ(2, fact_manager.GetSynonymsForId(7, context.get()).size());
protobufs::DataDescriptor descriptor_100 = MakeDataDescriptor(100, {});
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(7, {}),
descriptor_100, context.get()));
}
{
TransformationCopyObject copy_false(
8, MakeInstructionDescriptor(100, SpvOpReturn, 0), 101);
ASSERT_TRUE(copy_false.IsApplicable(context.get(), fact_manager));
copy_false.Apply(context.get(), &fact_manager);
std::vector<uint32_t> ids_for_which_synonyms_are_known =
fact_manager.GetIdsForWhichSynonymsAreKnown(context.get());
ASSERT_EQ(4, ids_for_which_synonyms_are_known.size());
ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(),
ids_for_which_synonyms_are_known.end(),
8) != ids_for_which_synonyms_are_known.end());
ASSERT_EQ(2, fact_manager.GetSynonymsForId(8, context.get()).size());
protobufs::DataDescriptor descriptor_101 = MakeDataDescriptor(101, {});
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(8, {}),
descriptor_101, context.get()));
}
{
TransformationCopyObject copy_false_again(
101, MakeInstructionDescriptor(5, SpvOpReturn, 0), 102);
ASSERT_TRUE(copy_false_again.IsApplicable(context.get(), fact_manager));
copy_false_again.Apply(context.get(), &fact_manager);
std::vector<uint32_t> ids_for_which_synonyms_are_known =
fact_manager.GetIdsForWhichSynonymsAreKnown(context.get());
ASSERT_EQ(5, ids_for_which_synonyms_are_known.size());
ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(),
ids_for_which_synonyms_are_known.end(),
101) != ids_for_which_synonyms_are_known.end());
ASSERT_EQ(3, fact_manager.GetSynonymsForId(101, context.get()).size());
protobufs::DataDescriptor descriptor_102 = MakeDataDescriptor(102, {});
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(101, {}),
descriptor_102, context.get()));
}
{
TransformationCopyObject copy_true_again(
7, MakeInstructionDescriptor(102, SpvOpReturn, 0), 103);
ASSERT_TRUE(copy_true_again.IsApplicable(context.get(), fact_manager));
copy_true_again.Apply(context.get(), &fact_manager);
std::vector<uint32_t> ids_for_which_synonyms_are_known =
fact_manager.GetIdsForWhichSynonymsAreKnown(context.get());
ASSERT_EQ(6, ids_for_which_synonyms_are_known.size());
ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(),
ids_for_which_synonyms_are_known.end(),
7) != ids_for_which_synonyms_are_known.end());
ASSERT_EQ(3, fact_manager.GetSynonymsForId(7, context.get()).size());
protobufs::DataDescriptor descriptor_103 = MakeDataDescriptor(103, {});
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(7, {}),
descriptor_103, context.get()));
}
std::string after_transformation = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
%2 = OpTypeVoid
%6 = OpTypeBool
%7 = OpConstantTrue %6
%8 = OpConstantFalse %6
%3 = OpTypeFunction %2
%4 = OpFunction %2 None %3
%5 = OpLabel
%100 = OpCopyObject %6 %7
%101 = OpCopyObject %6 %8
%102 = OpCopyObject %6 %101
%103 = OpCopyObject %6 %7
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
TEST(TransformationCopyObjectTest, CheckIllegalCases) {
// The following SPIR-V comes from this GLSL, pushed through spirv-opt
// and then doctored a bit.
//
// #version 310 es
//
// precision highp float;
//
// struct S {
// int a;
// float b;
// };
//
// layout(set = 0, binding = 2) uniform block {
// S s;
// lowp float f;
// int ii;
// } ubuf;
//
// layout(location = 0) out vec4 color;
//
// void main() {
// float c = 0.0;
// lowp float d = 0.0;
// S localS = ubuf.s;
// for (int i = 0; i < ubuf.s.a; i++) {
// switch (ubuf.ii) {
// case 0:
// c += 0.1;
// d += 0.2;
// case 1:
// c += 0.1;
// if (c > d) {
// d += 0.2;
// } else {
// d += c;
// }
// break;
// default:
// i += 1;
// localS.b += d;
// }
// }
// color = vec4(c, d, localS.b, 1.0);
// }
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %80
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %12 "S"
OpMemberName %12 0 "a"
OpMemberName %12 1 "b"
OpName %15 "S"
OpMemberName %15 0 "a"
OpMemberName %15 1 "b"
OpName %16 "block"
OpMemberName %16 0 "s"
OpMemberName %16 1 "f"
OpMemberName %16 2 "ii"
OpName %18 "ubuf"
OpName %80 "color"
OpMemberDecorate %12 0 RelaxedPrecision
OpMemberDecorate %15 0 RelaxedPrecision
OpMemberDecorate %15 0 Offset 0
OpMemberDecorate %15 1 Offset 4
OpMemberDecorate %16 0 Offset 0
OpMemberDecorate %16 1 RelaxedPrecision
OpMemberDecorate %16 1 Offset 16
OpMemberDecorate %16 2 RelaxedPrecision
OpMemberDecorate %16 2 Offset 20
OpDecorate %16 Block
OpDecorate %18 DescriptorSet 0
OpDecorate %18 Binding 2
OpDecorate %38 RelaxedPrecision
OpDecorate %43 RelaxedPrecision
OpDecorate %53 RelaxedPrecision
OpDecorate %62 RelaxedPrecision
OpDecorate %69 RelaxedPrecision
OpDecorate %77 RelaxedPrecision
OpDecorate %80 Location 0
OpDecorate %101 RelaxedPrecision
OpDecorate %102 RelaxedPrecision
OpDecorate %96 RelaxedPrecision
OpDecorate %108 RelaxedPrecision
OpDecorate %107 RelaxedPrecision
OpDecorate %98 RelaxedPrecision
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%9 = OpConstant %6 0
%11 = OpTypeInt 32 1
%12 = OpTypeStruct %11 %6
%15 = OpTypeStruct %11 %6
%16 = OpTypeStruct %15 %6 %11
%17 = OpTypePointer Uniform %16
%18 = OpVariable %17 Uniform
%19 = OpConstant %11 0
%20 = OpTypePointer Uniform %15
%27 = OpConstant %11 1
%36 = OpTypePointer Uniform %11
%39 = OpTypeBool
%41 = OpConstant %11 2
%48 = OpConstant %6 0.100000001
%51 = OpConstant %6 0.200000003
%78 = OpTypeVector %6 4
%79 = OpTypePointer Output %78
%80 = OpVariable %79 Output
%85 = OpConstant %6 1
%95 = OpUndef %12
%112 = OpTypePointer Uniform %6
%113 = OpTypeInt 32 0
%114 = OpConstant %113 1
%179 = OpTypePointer Function %39
%4 = OpFunction %2 None %3
%5 = OpLabel
%180 = OpVariable %179 Function
%181 = OpVariable %179 Function
%182 = OpVariable %179 Function
%21 = OpAccessChain %20 %18 %19
%115 = OpAccessChain %112 %21 %114
%116 = OpLoad %6 %115
%90 = OpCompositeInsert %12 %116 %95 1
OpBranch %30
%30 = OpLabel
%99 = OpPhi %12 %90 %5 %109 %47
%98 = OpPhi %6 %9 %5 %107 %47
%97 = OpPhi %6 %9 %5 %105 %47
%96 = OpPhi %11 %19 %5 %77 %47
%37 = OpAccessChain %36 %18 %19 %19
%38 = OpLoad %11 %37
%40 = OpSLessThan %39 %96 %38
OpLoopMerge %32 %47 None
OpBranchConditional %40 %31 %32
%31 = OpLabel
%42 = OpAccessChain %36 %18 %41
%43 = OpLoad %11 %42
OpSelectionMerge %45 None
OpSwitch %43 %46 0 %44 1 %45
%46 = OpLabel
%69 = OpIAdd %11 %96 %27
%72 = OpCompositeExtract %6 %99 1
%73 = OpFAdd %6 %72 %98
%93 = OpCompositeInsert %12 %73 %99 1
OpBranch %47
%44 = OpLabel
%50 = OpFAdd %6 %97 %48
%53 = OpFAdd %6 %98 %51
OpBranch %45
%45 = OpLabel
%101 = OpPhi %6 %98 %31 %53 %44
%100 = OpPhi %6 %97 %31 %50 %44
%55 = OpFAdd %6 %100 %48
%58 = OpFOrdGreaterThan %39 %55 %101
OpSelectionMerge %60 None
OpBranchConditional %58 %59 %63
%59 = OpLabel
%62 = OpFAdd %6 %101 %51
OpBranch %60
%63 = OpLabel
%66 = OpFAdd %6 %101 %55
OpBranch %60
%60 = OpLabel
%108 = OpPhi %6 %62 %59 %66 %63
OpBranch %47
%47 = OpLabel
%109 = OpPhi %12 %93 %46 %99 %60
%107 = OpPhi %6 %98 %46 %108 %60
%105 = OpPhi %6 %97 %46 %55 %60
%102 = OpPhi %11 %69 %46 %96 %60
%77 = OpIAdd %11 %102 %27
OpBranch %30
%32 = OpLabel
%84 = OpCompositeExtract %6 %99 1
%86 = OpCompositeConstruct %78 %97 %98 %84 %85
OpStore %80 %86
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
// Inapplicable because %18 is decorated.
ASSERT_FALSE(TransformationCopyObject(
18, MakeInstructionDescriptor(21, SpvOpAccessChain, 0), 200)
.IsApplicable(context.get(), fact_manager));
// Inapplicable because %77 is decorated.
ASSERT_FALSE(TransformationCopyObject(
77, MakeInstructionDescriptor(77, SpvOpBranch, 0), 200)
.IsApplicable(context.get(), fact_manager));
// Inapplicable because %80 is decorated.
ASSERT_FALSE(TransformationCopyObject(
80, MakeInstructionDescriptor(77, SpvOpIAdd, 0), 200)
.IsApplicable(context.get(), fact_manager));
// Inapplicable because %84 is not available at the requested point
ASSERT_FALSE(
TransformationCopyObject(
84, MakeInstructionDescriptor(32, SpvOpCompositeExtract, 0), 200)
.IsApplicable(context.get(), fact_manager));
// Fine because %84 is available at the requested point
ASSERT_TRUE(
TransformationCopyObject(
84, MakeInstructionDescriptor(32, SpvOpCompositeConstruct, 0), 200)
.IsApplicable(context.get(), fact_manager));
// Inapplicable because id %9 is already in use
ASSERT_FALSE(
TransformationCopyObject(
84, MakeInstructionDescriptor(32, SpvOpCompositeConstruct, 0), 9)
.IsApplicable(context.get(), fact_manager));
// Inapplicable because the requested point does not exist
ASSERT_FALSE(TransformationCopyObject(
84, MakeInstructionDescriptor(86, SpvOpReturn, 2), 200)
.IsApplicable(context.get(), fact_manager));
// Inapplicable because %9 is not in a function
ASSERT_FALSE(TransformationCopyObject(
9, MakeInstructionDescriptor(9, SpvOpTypeInt, 0), 200)
.IsApplicable(context.get(), fact_manager));
// Inapplicable because the insert point is right before, or inside, a chunk
// of OpPhis
ASSERT_FALSE(TransformationCopyObject(
9, MakeInstructionDescriptor(30, SpvOpPhi, 0), 200)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationCopyObject(
9, MakeInstructionDescriptor(99, SpvOpPhi, 1), 200)
.IsApplicable(context.get(), fact_manager));
// OK, because the insert point is just after a chunk of OpPhis.
ASSERT_TRUE(TransformationCopyObject(
9, MakeInstructionDescriptor(96, SpvOpAccessChain, 0), 200)
.IsApplicable(context.get(), fact_manager));
// Inapplicable because the insert point is right after an OpSelectionMerge
ASSERT_FALSE(
TransformationCopyObject(
9, MakeInstructionDescriptor(58, SpvOpBranchConditional, 0), 200)
.IsApplicable(context.get(), fact_manager));
// OK, because the insert point is right before the OpSelectionMerge
ASSERT_TRUE(TransformationCopyObject(
9, MakeInstructionDescriptor(58, SpvOpSelectionMerge, 0), 200)
.IsApplicable(context.get(), fact_manager));
// Inapplicable because the insert point is right after an OpSelectionMerge
ASSERT_FALSE(TransformationCopyObject(
9, MakeInstructionDescriptor(43, SpvOpSwitch, 0), 200)
.IsApplicable(context.get(), fact_manager));
// OK, because the insert point is right before the OpSelectionMerge
ASSERT_TRUE(TransformationCopyObject(
9, MakeInstructionDescriptor(43, SpvOpSelectionMerge, 0), 200)
.IsApplicable(context.get(), fact_manager));
// Inapplicable because the insert point is right after an OpLoopMerge
ASSERT_FALSE(
TransformationCopyObject(
9, MakeInstructionDescriptor(40, SpvOpBranchConditional, 0), 200)
.IsApplicable(context.get(), fact_manager));
// OK, because the insert point is right before the OpLoopMerge
ASSERT_TRUE(TransformationCopyObject(
9, MakeInstructionDescriptor(40, SpvOpLoopMerge, 0), 200)
.IsApplicable(context.get(), fact_manager));
// Inapplicable because id %300 does not exist
ASSERT_FALSE(TransformationCopyObject(
300, MakeInstructionDescriptor(40, SpvOpLoopMerge, 0), 200)
.IsApplicable(context.get(), fact_manager));
// Inapplicable because the following instruction is OpVariable
ASSERT_FALSE(TransformationCopyObject(
9, MakeInstructionDescriptor(180, SpvOpVariable, 0), 200)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationCopyObject(
9, MakeInstructionDescriptor(181, SpvOpVariable, 0), 200)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationCopyObject(
9, MakeInstructionDescriptor(182, SpvOpVariable, 0), 200)
.IsApplicable(context.get(), fact_manager));
// OK, because this is just past the group of OpVariable instructions.
ASSERT_TRUE(TransformationCopyObject(
9, MakeInstructionDescriptor(182, SpvOpAccessChain, 0), 200)
.IsApplicable(context.get(), fact_manager));
}
TEST(TransformationCopyObjectTest, MiscellaneousCopies) {
// The following SPIR-V comes from this GLSL:
//
// #version 310 es
//
// precision highp float;
//
// float g;
//
// vec4 h;
//
// void main() {
// int a;
// int b;
// b = int(g);
// h.x = float(a);
// }
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %8 "b"
OpName %11 "g"
OpName %16 "h"
OpName %17 "a"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpTypeFloat 32
%10 = OpTypePointer Private %9
%11 = OpVariable %10 Private
%14 = OpTypeVector %9 4
%15 = OpTypePointer Private %14
%16 = OpVariable %15 Private
%20 = OpTypeInt 32 0
%21 = OpConstant %20 0
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%17 = OpVariable %7 Function
%12 = OpLoad %9 %11
%13 = OpConvertFToS %6 %12
OpStore %8 %13
%18 = OpLoad %6 %17
%19 = OpConvertSToF %9 %18
%22 = OpAccessChain %10 %16 %21
OpStore %22 %19
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
std::vector<TransformationCopyObject> transformations = {
TransformationCopyObject(19, MakeInstructionDescriptor(22, SpvOpStore, 0),
100),
TransformationCopyObject(
22, MakeInstructionDescriptor(22, SpvOpCopyObject, 0), 101),
TransformationCopyObject(
12, MakeInstructionDescriptor(22, SpvOpCopyObject, 0), 102),
TransformationCopyObject(
11, MakeInstructionDescriptor(22, SpvOpCopyObject, 0), 103),
TransformationCopyObject(
16, MakeInstructionDescriptor(22, SpvOpCopyObject, 0), 104),
TransformationCopyObject(
8, MakeInstructionDescriptor(22, SpvOpCopyObject, 0), 105),
TransformationCopyObject(
17, MakeInstructionDescriptor(22, SpvOpCopyObject, 0), 106)};
for (auto& transformation : transformations) {
ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
transformation.Apply(context.get(), &fact_manager);
}
ASSERT_TRUE(IsValid(env, context.get()));
std::string after_transformation = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %8 "b"
OpName %11 "g"
OpName %16 "h"
OpName %17 "a"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpTypeFloat 32
%10 = OpTypePointer Private %9
%11 = OpVariable %10 Private
%14 = OpTypeVector %9 4
%15 = OpTypePointer Private %14
%16 = OpVariable %15 Private
%20 = OpTypeInt 32 0
%21 = OpConstant %20 0
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%17 = OpVariable %7 Function
%12 = OpLoad %9 %11
%13 = OpConvertFToS %6 %12
OpStore %8 %13
%18 = OpLoad %6 %17
%19 = OpConvertSToF %9 %18
%22 = OpAccessChain %10 %16 %21
%106 = OpCopyObject %7 %17
%105 = OpCopyObject %7 %8
%104 = OpCopyObject %15 %16
%103 = OpCopyObject %10 %11
%102 = OpCopyObject %9 %12
%101 = OpCopyObject %10 %22
%100 = OpCopyObject %9 %19
OpStore %22 %19
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
} // namespace
} // namespace fuzz
} // namespace spvtools

View File

@@ -1,670 +0,0 @@
// Copyright (c) 2019 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 "source/fuzz/transformation_move_block_down.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
namespace fuzz {
namespace {
TEST(TransformationMoveBlockDownTest, NoMovePossible1) {
// Block 11 cannot be moved down as it dominates block 12.
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpDecorate %8 RelaxedPrecision
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 1
%10 = OpConstant %6 2
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
OpBranch %11
%11 = OpLabel
OpStore %8 %9
OpBranch %12
%12 = OpLabel
OpStore %8 %10
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
FactManager fact_manager;
auto transformation = TransformationMoveBlockDown(11);
ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
}
TEST(TransformationMoveBlockDownTest, NoMovePossible2) {
// Block 5 cannot be moved down as it is the entry block.
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpDecorate %8 RelaxedPrecision
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 1
%10 = OpConstant %6 2
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
OpStore %8 %9
OpStore %8 %10
OpReturn
%11 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
FactManager fact_manager;
auto transformation = TransformationMoveBlockDown(5);
ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
}
TEST(TransformationMoveBlockDownTest, NoMovePossible3) {
// Block 100 does not exist, so cannot be moved down.
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpDecorate %8 RelaxedPrecision
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 1
%10 = OpConstant %6 2
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
OpBranch %11
%11 = OpLabel
OpStore %8 %9
OpBranch %12
%12 = OpLabel
OpStore %8 %10
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
FactManager fact_manager;
auto transformation = TransformationMoveBlockDown(100);
ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
}
TEST(TransformationMoveBlockDownTest, NoMovePossible4) {
// Block 12 is the last block in its function, so cannot be moved down.
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpDecorate %8 RelaxedPrecision
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 1
%10 = OpConstant %6 2
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
OpBranch %11
%11 = OpLabel
OpStore %8 %9
OpBranch %12
%12 = OpLabel
OpStore %8 %10
OpReturn
OpFunctionEnd
%13 = OpFunction %2 None %3
%14 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
FactManager fact_manager;
auto transformation = TransformationMoveBlockDown(12);
ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
}
TEST(TransformationMoveBlockDownTest, ManyMovesPossible) {
// The SPIR-V arising from this shader has lots of opportunities for moving
// blocks around.
//
// void main() {
// int x;
// int y;
// if (x < y) {
// x = 1;
// if (y == x) {
// x = 3;
// } else {
// x = 4;
// }
// } else {
// if (y < x) {
// x = 5;
// } else {
// x = 6;
// }
// }
// }
std::string before_transformation = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %8 "x"
OpName %10 "y"
OpDecorate %8 RelaxedPrecision
OpDecorate %9 RelaxedPrecision
OpDecorate %10 RelaxedPrecision
OpDecorate %11 RelaxedPrecision
OpDecorate %17 RelaxedPrecision
OpDecorate %18 RelaxedPrecision
OpDecorate %26 RelaxedPrecision
OpDecorate %27 RelaxedPrecision
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%12 = OpTypeBool
%16 = OpConstant %6 1
%22 = OpConstant %6 3
%24 = OpConstant %6 4
%31 = OpConstant %6 5
%33 = OpConstant %6 6
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
%9 = OpLoad %6 %8
%11 = OpLoad %6 %10
%13 = OpSLessThan %12 %9 %11
OpSelectionMerge %15 None
OpBranchConditional %13 %14 %25
%14 = OpLabel
OpStore %8 %16
%17 = OpLoad %6 %10
%18 = OpLoad %6 %8
%19 = OpIEqual %12 %17 %18
OpSelectionMerge %21 None
OpBranchConditional %19 %20 %23
%20 = OpLabel
OpStore %8 %22
OpBranch %21
%23 = OpLabel
OpStore %8 %24
OpBranch %21
%21 = OpLabel
OpBranch %15
%25 = OpLabel
%26 = OpLoad %6 %10
%27 = OpLoad %6 %8
%28 = OpSLessThan %12 %26 %27
OpSelectionMerge %30 None
OpBranchConditional %28 %29 %32
%29 = OpLabel
OpStore %8 %31
OpBranch %30
%32 = OpLabel
OpStore %8 %33
OpBranch %30
%30 = OpLabel
OpBranch %15
%15 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context =
BuildModule(env, consumer, before_transformation, kFuzzAssembleOption);
FactManager fact_manager;
// The block ids are: 5 14 20 23 21 25 29 32 30 15
// We make a transformation to move each of them down, plus a transformation
// to move a non-block, 27, down.
auto move_down_5 = TransformationMoveBlockDown(5);
auto move_down_14 = TransformationMoveBlockDown(14);
auto move_down_20 = TransformationMoveBlockDown(20);
auto move_down_23 = TransformationMoveBlockDown(23);
auto move_down_21 = TransformationMoveBlockDown(21);
auto move_down_25 = TransformationMoveBlockDown(25);
auto move_down_29 = TransformationMoveBlockDown(29);
auto move_down_32 = TransformationMoveBlockDown(32);
auto move_down_30 = TransformationMoveBlockDown(30);
auto move_down_15 = TransformationMoveBlockDown(15);
auto move_down_27 = TransformationMoveBlockDown(27);
// Dominance is as follows:
// 5 dominates everything else
// 14 dominates 20, 23, 21
// 20 dominates nothing
// 23 dominates nothing
// 21 dominates nothing
// 25 dominates 29, 32, 30
// 29 dominates nothing
// 32 dominates nothing
// 30 dominates nothing
// 15 dominates nothing
// Current ordering: 5 14 20 23 21 25 29 32 30 15
ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_20.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_25.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_15.IsApplicable(context.get(), fact_manager));
// Let's bubble 20 all the way down.
move_down_20.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
// Current ordering: 5 14 23 20 21 25 29 32 30 15
ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_20.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_25.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_15.IsApplicable(context.get(), fact_manager));
move_down_20.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
// Current ordering: 5 14 23 21 20 25 29 32 30 15
ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_20.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_25.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_15.IsApplicable(context.get(), fact_manager));
move_down_20.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
// Current ordering: 5 14 23 21 25 20 29 32 30 15
ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_25.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_20.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_15.IsApplicable(context.get(), fact_manager));
move_down_20.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
// Current ordering: 5 14 23 21 25 29 20 32 30 15
ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_25.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_20.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_15.IsApplicable(context.get(), fact_manager));
move_down_20.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
// Current ordering: 5 14 23 21 25 29 32 20 30 15
ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_25.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_20.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_15.IsApplicable(context.get(), fact_manager));
move_down_20.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
// Current ordering: 5 14 23 21 25 29 32 30 20 15
ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_25.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_20.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_15.IsApplicable(context.get(), fact_manager));
move_down_20.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
std::string after_bubbling_20_down = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %8 "x"
OpName %10 "y"
OpDecorate %8 RelaxedPrecision
OpDecorate %9 RelaxedPrecision
OpDecorate %10 RelaxedPrecision
OpDecorate %11 RelaxedPrecision
OpDecorate %17 RelaxedPrecision
OpDecorate %18 RelaxedPrecision
OpDecorate %26 RelaxedPrecision
OpDecorate %27 RelaxedPrecision
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%12 = OpTypeBool
%16 = OpConstant %6 1
%22 = OpConstant %6 3
%24 = OpConstant %6 4
%31 = OpConstant %6 5
%33 = OpConstant %6 6
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
%9 = OpLoad %6 %8
%11 = OpLoad %6 %10
%13 = OpSLessThan %12 %9 %11
OpSelectionMerge %15 None
OpBranchConditional %13 %14 %25
%14 = OpLabel
OpStore %8 %16
%17 = OpLoad %6 %10
%18 = OpLoad %6 %8
%19 = OpIEqual %12 %17 %18
OpSelectionMerge %21 None
OpBranchConditional %19 %20 %23
%23 = OpLabel
OpStore %8 %24
OpBranch %21
%21 = OpLabel
OpBranch %15
%25 = OpLabel
%26 = OpLoad %6 %10
%27 = OpLoad %6 %8
%28 = OpSLessThan %12 %26 %27
OpSelectionMerge %30 None
OpBranchConditional %28 %29 %32
%29 = OpLabel
OpStore %8 %31
OpBranch %30
%32 = OpLabel
OpStore %8 %33
OpBranch %30
%30 = OpLabel
OpBranch %15
%15 = OpLabel
OpReturn
%20 = OpLabel
OpStore %8 %22
OpBranch %21
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_bubbling_20_down, context.get()));
// Current ordering: 5 14 23 21 25 29 32 30 15 20
ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_25.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_15.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_20.IsApplicable(context.get(), fact_manager));
move_down_23.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
// Current ordering: 5 14 21 23 25 29 32 30 15 20
ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_25.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_15.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_20.IsApplicable(context.get(), fact_manager));
move_down_23.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
// Current ordering: 5 14 21 25 23 29 32 30 15 20
ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_25.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_15.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_20.IsApplicable(context.get(), fact_manager));
move_down_21.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
// Current ordering: 5 14 25 21 23 29 32 30 15 20
ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_14.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_25.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_15.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_20.IsApplicable(context.get(), fact_manager));
move_down_14.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
std::string after_more_shuffling = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %8 "x"
OpName %10 "y"
OpDecorate %8 RelaxedPrecision
OpDecorate %9 RelaxedPrecision
OpDecorate %10 RelaxedPrecision
OpDecorate %11 RelaxedPrecision
OpDecorate %17 RelaxedPrecision
OpDecorate %18 RelaxedPrecision
OpDecorate %26 RelaxedPrecision
OpDecorate %27 RelaxedPrecision
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%12 = OpTypeBool
%16 = OpConstant %6 1
%22 = OpConstant %6 3
%24 = OpConstant %6 4
%31 = OpConstant %6 5
%33 = OpConstant %6 6
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
%9 = OpLoad %6 %8
%11 = OpLoad %6 %10
%13 = OpSLessThan %12 %9 %11
OpSelectionMerge %15 None
OpBranchConditional %13 %14 %25
%25 = OpLabel
%26 = OpLoad %6 %10
%27 = OpLoad %6 %8
%28 = OpSLessThan %12 %26 %27
OpSelectionMerge %30 None
OpBranchConditional %28 %29 %32
%14 = OpLabel
OpStore %8 %16
%17 = OpLoad %6 %10
%18 = OpLoad %6 %8
%19 = OpIEqual %12 %17 %18
OpSelectionMerge %21 None
OpBranchConditional %19 %20 %23
%21 = OpLabel
OpBranch %15
%23 = OpLabel
OpStore %8 %24
OpBranch %21
%29 = OpLabel
OpStore %8 %31
OpBranch %30
%32 = OpLabel
OpStore %8 %33
OpBranch %30
%30 = OpLabel
OpBranch %15
%15 = OpLabel
OpReturn
%20 = OpLabel
OpStore %8 %22
OpBranch %21
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_more_shuffling, context.get()));
// Final ordering: 5 25 14 21 23 29 32 30 15 20
ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_25.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_15.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_20.IsApplicable(context.get(), fact_manager));
}
TEST(TransformationMoveBlockDownTest, DoNotMoveUnreachable) {
// Block 6 is unreachable, so cannot be moved down.
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%10 = OpTypeInt 32 1
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
%6 = OpLabel
%7 = OpUndef %10
OpBranch %8
%8 = OpLabel
%9 = OpCopyObject %10 %7
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
auto transformation = TransformationMoveBlockDown(6);
ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
}
} // namespace
} // namespace fuzz
} // namespace spvtools

View File

@@ -1,655 +0,0 @@
// Copyright (c) 2019 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 "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h"
#include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/id_use_descriptor.h"
#include "source/fuzz/instruction_descriptor.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
namespace fuzz {
namespace {
TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest,
BasicReplacements) {
// The test came from the following pseudo-GLSL, where int64 and uint64 denote
// 64-bit integer types (they were replaced with int and uint during
// translation to SPIR-V, and the generated SPIR-V has been doctored to
// accommodate them).
//
// #version 450
//
// void main() {
// double d1, d2;
// d1 = 1.0;
// d2 = 2.0;
// float f1, f2;
// f1 = 4.0;
// f2 = 8.0;
// int i1, i2;
// i1 = 100;
// i2 = 200;
//
// uint u1, u2;
// u1 = 300u;
// u2 = 400u;
//
// int64 i64_1, i64_2;
// i64_1 = 500;
// i64_2 = 600;
//
// uint64 u64_1, u64_2;
// u64_1 = 700u;
// u64_2 = 800u;
//
// bool b, c, d, e;
// b = true;
// c = false;
// d = true || c;
// c = c && false;
// }
std::string shader = R"(
OpCapability Shader
OpCapability Float64
OpCapability Int64
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 450
OpName %4 "main"
OpName %8 "d1"
OpName %10 "d2"
OpName %14 "f1"
OpName %16 "f2"
OpName %20 "i1"
OpName %22 "i2"
OpName %26 "u1"
OpName %28 "u2"
OpName %30 "i64_1"
OpName %32 "i64_2"
OpName %34 "u64_1"
OpName %36 "u64_2"
OpName %40 "b"
OpName %42 "c"
OpName %44 "d"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 64
%7 = OpTypePointer Function %6
%9 = OpConstant %6 1
%11 = OpConstant %6 2
%12 = OpTypeFloat 32
%13 = OpTypePointer Function %12
%15 = OpConstant %12 4
%17 = OpConstant %12 8
%18 = OpTypeInt 32 1
%60 = OpTypeInt 64 1
%61 = OpTypePointer Function %60
%19 = OpTypePointer Function %18
%21 = OpConstant %18 -100
%23 = OpConstant %18 200
%24 = OpTypeInt 32 0
%62 = OpTypeInt 64 0
%63 = OpTypePointer Function %62
%25 = OpTypePointer Function %24
%27 = OpConstant %24 300
%29 = OpConstant %24 400
%31 = OpConstant %60 -600
%33 = OpConstant %60 -500
%35 = OpConstant %62 700
%37 = OpConstant %62 800
%38 = OpTypeBool
%39 = OpTypePointer Function %38
%41 = OpConstantTrue %38
%43 = OpConstantFalse %38
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
%14 = OpVariable %13 Function
%16 = OpVariable %13 Function
%20 = OpVariable %19 Function
%22 = OpVariable %19 Function
%26 = OpVariable %25 Function
%28 = OpVariable %25 Function
%30 = OpVariable %61 Function
%32 = OpVariable %61 Function
%34 = OpVariable %63 Function
%36 = OpVariable %63 Function
%40 = OpVariable %39 Function
%42 = OpVariable %39 Function
%44 = OpVariable %39 Function
OpStore %8 %9
OpStore %10 %11
OpStore %14 %15
OpStore %16 %17
OpStore %20 %21
OpStore %22 %23
OpStore %26 %27
OpStore %28 %29
OpStore %30 %31
OpStore %32 %33
OpStore %34 %35
OpStore %36 %37
OpStore %40 %41
OpStore %42 %43
%45 = OpLoad %38 %42
%46 = OpLogicalOr %38 %41 %45
OpStore %44 %46
%47 = OpLoad %38 %42
%48 = OpLogicalAnd %38 %47 %43
OpStore %42 %48
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
std::vector<protobufs::IdUseDescriptor> uses_of_true = {
MakeIdUseDescriptor(41, MakeInstructionDescriptor(44, SpvOpStore, 12), 1),
MakeIdUseDescriptor(41, MakeInstructionDescriptor(46, SpvOpLogicalOr, 0),
0)};
std::vector<protobufs::IdUseDescriptor> uses_of_false = {
MakeIdUseDescriptor(43, MakeInstructionDescriptor(44, SpvOpStore, 13), 1),
MakeIdUseDescriptor(43, MakeInstructionDescriptor(48, SpvOpLogicalAnd, 0),
1)};
const uint32_t fresh_id = 100;
std::vector<SpvOp> fp_gt_opcodes = {
SpvOpFOrdGreaterThan, SpvOpFOrdGreaterThanEqual, SpvOpFUnordGreaterThan,
SpvOpFUnordGreaterThanEqual};
std::vector<SpvOp> fp_lt_opcodes = {SpvOpFOrdLessThan, SpvOpFOrdLessThanEqual,
SpvOpFUnordLessThan,
SpvOpFUnordLessThanEqual};
std::vector<SpvOp> int_gt_opcodes = {SpvOpSGreaterThan,
SpvOpSGreaterThanEqual};
std::vector<SpvOp> int_lt_opcodes = {SpvOpSLessThan, SpvOpSLessThanEqual};
std::vector<SpvOp> uint_gt_opcodes = {SpvOpUGreaterThan,
SpvOpUGreaterThanEqual};
std::vector<SpvOp> uint_lt_opcodes = {SpvOpULessThan, SpvOpULessThanEqual};
#define CHECK_OPERATOR(USE_DESCRIPTOR, LHS_ID, RHS_ID, OPCODE, FRESH_ID) \
ASSERT_TRUE(TransformationReplaceBooleanConstantWithConstantBinary( \
USE_DESCRIPTOR, LHS_ID, RHS_ID, OPCODE, FRESH_ID) \
.IsApplicable(context.get(), fact_manager)); \
ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary( \
USE_DESCRIPTOR, RHS_ID, LHS_ID, OPCODE, FRESH_ID) \
.IsApplicable(context.get(), fact_manager));
#define CHECK_TRANSFORMATION_APPLICABILITY(GT_OPCODES, LT_OPCODES, SMALL_ID, \
LARGE_ID) \
for (auto gt_opcode : GT_OPCODES) { \
for (auto& true_use : uses_of_true) { \
CHECK_OPERATOR(true_use, LARGE_ID, SMALL_ID, gt_opcode, fresh_id); \
} \
for (auto& false_use : uses_of_false) { \
CHECK_OPERATOR(false_use, SMALL_ID, LARGE_ID, gt_opcode, fresh_id); \
} \
} \
for (auto lt_opcode : LT_OPCODES) { \
for (auto& true_use : uses_of_true) { \
CHECK_OPERATOR(true_use, SMALL_ID, LARGE_ID, lt_opcode, fresh_id); \
} \
for (auto& false_use : uses_of_false) { \
CHECK_OPERATOR(false_use, LARGE_ID, SMALL_ID, lt_opcode, fresh_id); \
} \
}
// Float
{ CHECK_TRANSFORMATION_APPLICABILITY(fp_gt_opcodes, fp_lt_opcodes, 15, 17); }
// Double
{ CHECK_TRANSFORMATION_APPLICABILITY(fp_gt_opcodes, fp_lt_opcodes, 9, 11); }
// Int32
{
CHECK_TRANSFORMATION_APPLICABILITY(int_gt_opcodes, int_lt_opcodes, 21, 23);
}
// Int64
{
CHECK_TRANSFORMATION_APPLICABILITY(int_gt_opcodes, int_lt_opcodes, 31, 33);
}
// Uint32
{
CHECK_TRANSFORMATION_APPLICABILITY(uint_gt_opcodes, uint_lt_opcodes, 27,
29);
}
// Uint64
{
CHECK_TRANSFORMATION_APPLICABILITY(uint_gt_opcodes, uint_lt_opcodes, 35,
37);
}
// Target id is not fresh
ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
uses_of_true[0], 15, 17, SpvOpFOrdLessThan, 15)
.IsApplicable(context.get(), fact_manager));
// LHS id does not exist
ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
uses_of_true[0], 300, 17, SpvOpFOrdLessThan, 200)
.IsApplicable(context.get(), fact_manager));
// RHS id does not exist
ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
uses_of_true[0], 15, 300, SpvOpFOrdLessThan, 200)
.IsApplicable(context.get(), fact_manager));
// LHS and RHS ids do not match type
ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
uses_of_true[0], 11, 17, SpvOpFOrdLessThan, 200)
.IsApplicable(context.get(), fact_manager));
// Opcode not appropriate
ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
uses_of_true[0], 15, 17, SpvOpFDiv, 200)
.IsApplicable(context.get(), fact_manager));
auto replace_true_with_double_comparison =
TransformationReplaceBooleanConstantWithConstantBinary(
uses_of_true[0], 11, 9, SpvOpFUnordGreaterThan, 100);
auto replace_true_with_uint32_comparison =
TransformationReplaceBooleanConstantWithConstantBinary(
uses_of_true[1], 27, 29, SpvOpULessThanEqual, 101);
auto replace_false_with_float_comparison =
TransformationReplaceBooleanConstantWithConstantBinary(
uses_of_false[0], 17, 15, SpvOpFOrdLessThan, 102);
auto replace_false_with_sint64_comparison =
TransformationReplaceBooleanConstantWithConstantBinary(
uses_of_false[1], 33, 31, SpvOpSLessThan, 103);
ASSERT_TRUE(replace_true_with_double_comparison.IsApplicable(context.get(),
fact_manager));
replace_true_with_double_comparison.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
ASSERT_TRUE(replace_true_with_uint32_comparison.IsApplicable(context.get(),
fact_manager));
replace_true_with_uint32_comparison.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
ASSERT_TRUE(replace_false_with_float_comparison.IsApplicable(context.get(),
fact_manager));
replace_false_with_float_comparison.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
ASSERT_TRUE(replace_false_with_sint64_comparison.IsApplicable(context.get(),
fact_manager));
replace_false_with_sint64_comparison.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
std::string after = R"(
OpCapability Shader
OpCapability Float64
OpCapability Int64
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 450
OpName %4 "main"
OpName %8 "d1"
OpName %10 "d2"
OpName %14 "f1"
OpName %16 "f2"
OpName %20 "i1"
OpName %22 "i2"
OpName %26 "u1"
OpName %28 "u2"
OpName %30 "i64_1"
OpName %32 "i64_2"
OpName %34 "u64_1"
OpName %36 "u64_2"
OpName %40 "b"
OpName %42 "c"
OpName %44 "d"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 64
%7 = OpTypePointer Function %6
%9 = OpConstant %6 1
%11 = OpConstant %6 2
%12 = OpTypeFloat 32
%13 = OpTypePointer Function %12
%15 = OpConstant %12 4
%17 = OpConstant %12 8
%18 = OpTypeInt 32 1
%60 = OpTypeInt 64 1
%61 = OpTypePointer Function %60
%19 = OpTypePointer Function %18
%21 = OpConstant %18 -100
%23 = OpConstant %18 200
%24 = OpTypeInt 32 0
%62 = OpTypeInt 64 0
%63 = OpTypePointer Function %62
%25 = OpTypePointer Function %24
%27 = OpConstant %24 300
%29 = OpConstant %24 400
%31 = OpConstant %60 -600
%33 = OpConstant %60 -500
%35 = OpConstant %62 700
%37 = OpConstant %62 800
%38 = OpTypeBool
%39 = OpTypePointer Function %38
%41 = OpConstantTrue %38
%43 = OpConstantFalse %38
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
%14 = OpVariable %13 Function
%16 = OpVariable %13 Function
%20 = OpVariable %19 Function
%22 = OpVariable %19 Function
%26 = OpVariable %25 Function
%28 = OpVariable %25 Function
%30 = OpVariable %61 Function
%32 = OpVariable %61 Function
%34 = OpVariable %63 Function
%36 = OpVariable %63 Function
%40 = OpVariable %39 Function
%42 = OpVariable %39 Function
%44 = OpVariable %39 Function
OpStore %8 %9
OpStore %10 %11
OpStore %14 %15
OpStore %16 %17
OpStore %20 %21
OpStore %22 %23
OpStore %26 %27
OpStore %28 %29
OpStore %30 %31
OpStore %32 %33
OpStore %34 %35
OpStore %36 %37
%100 = OpFUnordGreaterThan %38 %11 %9
OpStore %40 %100
%102 = OpFOrdLessThan %38 %17 %15
OpStore %42 %102
%45 = OpLoad %38 %42
%101 = OpULessThanEqual %38 %27 %29
%46 = OpLogicalOr %38 %101 %45
OpStore %44 %46
%47 = OpLoad %38 %42
%103 = OpSLessThan %38 %33 %31
%48 = OpLogicalAnd %38 %47 %103
OpStore %42 %48
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after, context.get()));
if (std::numeric_limits<double>::has_quiet_NaN) {
double quiet_nan_double = std::numeric_limits<double>::quiet_NaN();
uint32_t words[2];
memcpy(words, &quiet_nan_double, sizeof(double));
opt::Instruction::OperandList operands = {
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {words[0]}},
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {words[1]}}};
context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
context.get(), SpvOpConstant, 6, 200, operands));
fuzzerutil::UpdateModuleIdBound(context.get(), 200);
ASSERT_TRUE(IsValid(env, context.get()));
// The transformation is not applicable because %200 is NaN.
ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
uses_of_true[0], 11, 200, SpvOpFOrdLessThan, 300)
.IsApplicable(context.get(), fact_manager));
}
if (std::numeric_limits<double>::has_infinity) {
double positive_infinity_double = std::numeric_limits<double>::infinity();
uint32_t words[2];
memcpy(words, &positive_infinity_double, sizeof(double));
opt::Instruction::OperandList operands = {
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {words[0]}},
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {words[1]}}};
context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
context.get(), SpvOpConstant, 6, 201, operands));
fuzzerutil::UpdateModuleIdBound(context.get(), 201);
ASSERT_TRUE(IsValid(env, context.get()));
// Even though the double constant %11 is less than the infinity %201, the
// transformation is restricted to only apply to finite values.
ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
uses_of_true[0], 11, 201, SpvOpFOrdLessThan, 300)
.IsApplicable(context.get(), fact_manager));
}
if (std::numeric_limits<float>::has_infinity) {
float positive_infinity_float = std::numeric_limits<float>::infinity();
float negative_infinity_float = -1 * positive_infinity_float;
uint32_t words_positive_infinity[1];
uint32_t words_negative_infinity[1];
memcpy(words_positive_infinity, &positive_infinity_float, sizeof(float));
memcpy(words_negative_infinity, &negative_infinity_float, sizeof(float));
opt::Instruction::OperandList operands_positive_infinity = {
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {words_positive_infinity[0]}}};
context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
context.get(), SpvOpConstant, 12, 202, operands_positive_infinity));
fuzzerutil::UpdateModuleIdBound(context.get(), 202);
opt::Instruction::OperandList operands = {
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {words_negative_infinity[0]}}};
context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
context.get(), SpvOpConstant, 12, 203, operands));
fuzzerutil::UpdateModuleIdBound(context.get(), 203);
ASSERT_TRUE(IsValid(env, context.get()));
// Even though the negative infinity at %203 is less than the positive
// infinity %202, the transformation is restricted to only apply to finite
// values.
ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
uses_of_true[0], 203, 202, SpvOpFOrdLessThan, 300)
.IsApplicable(context.get(), fact_manager));
}
}
TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest,
MergeInstructions) {
// The test came from the following GLSL:
//
// void main() {
// int x = 1;
// int y = 2;
// if (true) {
// x = 2;
// }
// while(false) {
// y = 2;
// }
// }
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 450
OpName %4 "main"
OpName %8 "x"
OpName %10 "y"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 1
%11 = OpConstant %6 2
%12 = OpTypeBool
%13 = OpConstantTrue %12
%21 = OpConstantFalse %12
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
OpStore %8 %9
OpStore %10 %11
OpSelectionMerge %15 None
OpBranchConditional %13 %14 %15
%14 = OpLabel
OpStore %8 %11
OpBranch %15
%15 = OpLabel
OpBranch %16
%16 = OpLabel
OpLoopMerge %18 %19 None
OpBranchConditional %21 %17 %18
%17 = OpLabel
OpStore %10 %11
OpBranch %19
%19 = OpLabel
OpBranch %16
%18 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
auto use_of_true_in_if = MakeIdUseDescriptor(
13, MakeInstructionDescriptor(10, SpvOpBranchConditional, 0), 0);
auto use_of_false_in_while = MakeIdUseDescriptor(
21, MakeInstructionDescriptor(16, SpvOpBranchConditional, 0), 0);
auto replacement_1 = TransformationReplaceBooleanConstantWithConstantBinary(
use_of_true_in_if, 9, 11, SpvOpSLessThan, 100);
auto replacement_2 = TransformationReplaceBooleanConstantWithConstantBinary(
use_of_false_in_while, 9, 11, SpvOpSGreaterThanEqual, 101);
ASSERT_TRUE(replacement_1.IsApplicable(context.get(), fact_manager));
replacement_1.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
ASSERT_TRUE(replacement_2.IsApplicable(context.get(), fact_manager));
replacement_2.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
std::string after = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 450
OpName %4 "main"
OpName %8 "x"
OpName %10 "y"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 1
%11 = OpConstant %6 2
%12 = OpTypeBool
%13 = OpConstantTrue %12
%21 = OpConstantFalse %12
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
OpStore %8 %9
OpStore %10 %11
%100 = OpSLessThan %12 %9 %11
OpSelectionMerge %15 None
OpBranchConditional %100 %14 %15
%14 = OpLabel
OpStore %8 %11
OpBranch %15
%15 = OpLabel
OpBranch %16
%16 = OpLabel
%101 = OpSGreaterThanEqual %12 %9 %11
OpLoopMerge %18 %19 None
OpBranchConditional %101 %17 %18
%17 = OpLabel
OpStore %10 %11
OpBranch %19
%19 = OpLabel
OpBranch %16
%18 = OpLabel
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after, context.get()));
}
TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest, OpPhi) {
// Hand-written SPIR-V to check applicability of the transformation on an
// OpPhi argument.
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeBool
%7 = OpTypePointer Function %6
%9 = OpConstantTrue %6
%16 = OpConstantFalse %6
%10 = OpTypeInt 32 1
%11 = OpTypePointer Function %10
%13 = OpConstant %10 0
%15 = OpConstant %10 1
%4 = OpFunction %2 None %3
%5 = OpLabel
OpSelectionMerge %20 None
OpBranchConditional %9 %21 %22
%21 = OpLabel
OpBranch %20
%22 = OpLabel
OpBranch %20
%20 = OpLabel
%23 = OpPhi %6 %9 %21 %16 %22
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
auto replacement = TransformationReplaceBooleanConstantWithConstantBinary(
MakeIdUseDescriptor(9, MakeInstructionDescriptor(23, SpvOpPhi, 0), 0), 13,
15, SpvOpSLessThan, 100);
ASSERT_FALSE(replacement.IsApplicable(context.get(), fact_manager));
}
} // namespace
} // namespace fuzz
} // namespace spvtools

View File

@@ -1,251 +0,0 @@
// Copyright (c) 2019 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 "source/fuzz/transformation_set_function_control.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
namespace fuzz {
namespace {
TEST(TransformationSetFunctionControlTest, VariousScenarios) {
// This is a simple transformation; this test captures the important things
// to check for.
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %54
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %11 "foo(i1;i1;"
OpName %9 "a"
OpName %10 "b"
OpName %13 "bar("
OpName %17 "baz(i1;"
OpName %16 "x"
OpName %21 "boo(i1;i1;"
OpName %19 "a"
OpName %20 "b"
OpName %29 "g"
OpName %42 "param"
OpName %44 "param"
OpName %45 "param"
OpName %48 "param"
OpName %49 "param"
OpName %54 "color"
OpDecorate %54 Location 0
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%8 = OpTypeFunction %6 %7 %7
%15 = OpTypeFunction %6 %7
%28 = OpTypePointer Private %6
%29 = OpVariable %28 Private
%30 = OpConstant %6 2
%31 = OpConstant %6 5
%51 = OpTypeFloat 32
%52 = OpTypeVector %51 4
%53 = OpTypePointer Output %52
%54 = OpVariable %53 Output
%4 = OpFunction %2 None %3
%5 = OpLabel
%42 = OpVariable %7 Function
%44 = OpVariable %7 Function
%45 = OpVariable %7 Function
%48 = OpVariable %7 Function
%49 = OpVariable %7 Function
%41 = OpFunctionCall %2 %13
OpStore %42 %30
%43 = OpFunctionCall %6 %17 %42
OpStore %44 %31
%46 = OpLoad %6 %29
OpStore %45 %46
%47 = OpFunctionCall %6 %21 %44 %45
OpStore %48 %43
OpStore %49 %47
%50 = OpFunctionCall %6 %11 %48 %49
OpReturn
OpFunctionEnd
%11 = OpFunction %6 Const %8
%9 = OpFunctionParameter %7
%10 = OpFunctionParameter %7
%12 = OpLabel
%23 = OpLoad %6 %9
%24 = OpLoad %6 %10
%25 = OpIAdd %6 %23 %24
OpReturnValue %25
OpFunctionEnd
%13 = OpFunction %2 Inline %3
%14 = OpLabel
OpStore %29 %30
OpReturn
OpFunctionEnd
%17 = OpFunction %6 Pure|DontInline %15
%16 = OpFunctionParameter %7
%18 = OpLabel
%32 = OpLoad %6 %16
%33 = OpIAdd %6 %31 %32
OpReturnValue %33
OpFunctionEnd
%21 = OpFunction %6 DontInline %8
%19 = OpFunctionParameter %7
%20 = OpFunctionParameter %7
%22 = OpLabel
%36 = OpLoad %6 %19
%37 = OpLoad %6 %20
%38 = OpIMul %6 %36 %37
OpReturnValue %38
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
FactManager fact_manager;
// %36 is not a function
ASSERT_FALSE(TransformationSetFunctionControl(36, SpvFunctionControlMaskNone)
.IsApplicable(context.get(), fact_manager));
// Cannot add the Pure function control to %4 as it did not already have it
ASSERT_FALSE(TransformationSetFunctionControl(4, SpvFunctionControlPureMask)
.IsApplicable(context.get(), fact_manager));
// Cannot add the Const function control to %21 as it did not already
// have it
ASSERT_FALSE(TransformationSetFunctionControl(21, SpvFunctionControlConstMask)
.IsApplicable(context.get(), fact_manager));
// Set to None, removing Const
TransformationSetFunctionControl transformation1(11,
SpvFunctionControlMaskNone);
ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
transformation1.Apply(context.get(), &fact_manager);
// Set to Inline; silly to do it on an entry point, but it is allowed
TransformationSetFunctionControl transformation2(
4, SpvFunctionControlInlineMask);
ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
transformation2.Apply(context.get(), &fact_manager);
// Set to Pure, removing DontInline
TransformationSetFunctionControl transformation3(17,
SpvFunctionControlPureMask);
ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
transformation3.Apply(context.get(), &fact_manager);
// Change from Inline to DontInline
TransformationSetFunctionControl transformation4(
13, SpvFunctionControlDontInlineMask);
ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager));
transformation4.Apply(context.get(), &fact_manager);
std::string after_transformation = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %54
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %11 "foo(i1;i1;"
OpName %9 "a"
OpName %10 "b"
OpName %13 "bar("
OpName %17 "baz(i1;"
OpName %16 "x"
OpName %21 "boo(i1;i1;"
OpName %19 "a"
OpName %20 "b"
OpName %29 "g"
OpName %42 "param"
OpName %44 "param"
OpName %45 "param"
OpName %48 "param"
OpName %49 "param"
OpName %54 "color"
OpDecorate %54 Location 0
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%8 = OpTypeFunction %6 %7 %7
%15 = OpTypeFunction %6 %7
%28 = OpTypePointer Private %6
%29 = OpVariable %28 Private
%30 = OpConstant %6 2
%31 = OpConstant %6 5
%51 = OpTypeFloat 32
%52 = OpTypeVector %51 4
%53 = OpTypePointer Output %52
%54 = OpVariable %53 Output
%4 = OpFunction %2 Inline %3
%5 = OpLabel
%42 = OpVariable %7 Function
%44 = OpVariable %7 Function
%45 = OpVariable %7 Function
%48 = OpVariable %7 Function
%49 = OpVariable %7 Function
%41 = OpFunctionCall %2 %13
OpStore %42 %30
%43 = OpFunctionCall %6 %17 %42
OpStore %44 %31
%46 = OpLoad %6 %29
OpStore %45 %46
%47 = OpFunctionCall %6 %21 %44 %45
OpStore %48 %43
OpStore %49 %47
%50 = OpFunctionCall %6 %11 %48 %49
OpReturn
OpFunctionEnd
%11 = OpFunction %6 None %8
%9 = OpFunctionParameter %7
%10 = OpFunctionParameter %7
%12 = OpLabel
%23 = OpLoad %6 %9
%24 = OpLoad %6 %10
%25 = OpIAdd %6 %23 %24
OpReturnValue %25
OpFunctionEnd
%13 = OpFunction %2 DontInline %3
%14 = OpLabel
OpStore %29 %30
OpReturn
OpFunctionEnd
%17 = OpFunction %6 Pure %15
%16 = OpFunctionParameter %7
%18 = OpLabel
%32 = OpLoad %6 %16
%33 = OpIAdd %6 %31 %32
OpReturnValue %33
OpFunctionEnd
%21 = OpFunction %6 DontInline %8
%19 = OpFunctionParameter %7
%20 = OpFunctionParameter %7
%22 = OpLabel
%36 = OpLoad %6 %19
%37 = OpLoad %6 %20
%38 = OpIMul %6 %36 %37
OpReturnValue %38
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
} // namespace
} // namespace fuzz
} // namespace spvtools

View File

@@ -1,968 +0,0 @@
// Copyright (c) 2019 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 "source/fuzz/transformation_set_loop_control.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
namespace fuzz {
namespace {
TEST(TransformationSetLoopControlTest, VariousScenarios) {
// This test features loops with various different controls, and goes through
// a number of acceptable and unacceptable transformations to those controls.
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%16 = OpConstant %6 100
%17 = OpTypeBool
%20 = OpConstant %6 1
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%22 = OpVariable %7 Function
%32 = OpVariable %7 Function
%42 = OpVariable %7 Function
%52 = OpVariable %7 Function
%62 = OpVariable %7 Function
%72 = OpVariable %7 Function
%82 = OpVariable %7 Function
%92 = OpVariable %7 Function
%102 = OpVariable %7 Function
%112 = OpVariable %7 Function
%122 = OpVariable %7 Function
OpStore %8 %9
OpBranch %10
%10 = OpLabel
%132 = OpPhi %6 %9 %5 %21 %13
OpLoopMerge %12 %13 None
OpBranch %14
%14 = OpLabel
%18 = OpSLessThan %17 %132 %16
OpBranchConditional %18 %11 %12
%11 = OpLabel
OpBranch %13
%13 = OpLabel
%21 = OpIAdd %6 %132 %20
OpStore %8 %21
OpBranch %10
%12 = OpLabel
OpStore %22 %9
OpBranch %23
%23 = OpLabel
%133 = OpPhi %6 %9 %12 %31 %26
OpLoopMerge %25 %26 Unroll
OpBranch %27
%27 = OpLabel
%29 = OpSLessThan %17 %133 %16
OpBranchConditional %29 %24 %25
%24 = OpLabel
OpBranch %26
%26 = OpLabel
%31 = OpIAdd %6 %133 %20
OpStore %22 %31
OpBranch %23
%25 = OpLabel
OpStore %32 %9
OpBranch %33
%33 = OpLabel
%134 = OpPhi %6 %9 %25 %41 %36
OpLoopMerge %35 %36 DontUnroll
OpBranch %37
%37 = OpLabel
%39 = OpSLessThan %17 %134 %16
OpBranchConditional %39 %34 %35
%34 = OpLabel
OpBranch %36
%36 = OpLabel
%41 = OpIAdd %6 %134 %20
OpStore %32 %41
OpBranch %33
%35 = OpLabel
OpStore %42 %9
OpBranch %43
%43 = OpLabel
%135 = OpPhi %6 %9 %35 %51 %46
OpLoopMerge %45 %46 DependencyInfinite
OpBranch %47
%47 = OpLabel
%49 = OpSLessThan %17 %135 %16
OpBranchConditional %49 %44 %45
%44 = OpLabel
OpBranch %46
%46 = OpLabel
%51 = OpIAdd %6 %135 %20
OpStore %42 %51
OpBranch %43
%45 = OpLabel
OpStore %52 %9
OpBranch %53
%53 = OpLabel
%136 = OpPhi %6 %9 %45 %61 %56
OpLoopMerge %55 %56 DependencyLength 3
OpBranch %57
%57 = OpLabel
%59 = OpSLessThan %17 %136 %16
OpBranchConditional %59 %54 %55
%54 = OpLabel
OpBranch %56
%56 = OpLabel
%61 = OpIAdd %6 %136 %20
OpStore %52 %61
OpBranch %53
%55 = OpLabel
OpStore %62 %9
OpBranch %63
%63 = OpLabel
%137 = OpPhi %6 %9 %55 %71 %66
OpLoopMerge %65 %66 MinIterations 10
OpBranch %67
%67 = OpLabel
%69 = OpSLessThan %17 %137 %16
OpBranchConditional %69 %64 %65
%64 = OpLabel
OpBranch %66
%66 = OpLabel
%71 = OpIAdd %6 %137 %20
OpStore %62 %71
OpBranch %63
%65 = OpLabel
OpStore %72 %9
OpBranch %73
%73 = OpLabel
%138 = OpPhi %6 %9 %65 %81 %76
OpLoopMerge %75 %76 MaxIterations 50
OpBranch %77
%77 = OpLabel
%79 = OpSLessThan %17 %138 %16
OpBranchConditional %79 %74 %75
%74 = OpLabel
OpBranch %76
%76 = OpLabel
%81 = OpIAdd %6 %138 %20
OpStore %72 %81
OpBranch %73
%75 = OpLabel
OpStore %82 %9
OpBranch %83
%83 = OpLabel
%139 = OpPhi %6 %9 %75 %91 %86
OpLoopMerge %85 %86 IterationMultiple 4
OpBranch %87
%87 = OpLabel
%89 = OpSLessThan %17 %139 %16
OpBranchConditional %89 %84 %85
%84 = OpLabel
OpBranch %86
%86 = OpLabel
%91 = OpIAdd %6 %139 %20
OpStore %82 %91
OpBranch %83
%85 = OpLabel
OpStore %92 %9
OpBranch %93
%93 = OpLabel
%140 = OpPhi %6 %9 %85 %101 %96
OpLoopMerge %95 %96 PeelCount 2
OpBranch %97
%97 = OpLabel
%99 = OpSLessThan %17 %140 %16
OpBranchConditional %99 %94 %95
%94 = OpLabel
OpBranch %96
%96 = OpLabel
%101 = OpIAdd %6 %140 %20
OpStore %92 %101
OpBranch %93
%95 = OpLabel
OpStore %102 %9
OpBranch %103
%103 = OpLabel
%141 = OpPhi %6 %9 %95 %111 %106
OpLoopMerge %105 %106 PartialCount 3
OpBranch %107
%107 = OpLabel
%109 = OpSLessThan %17 %141 %16
OpBranchConditional %109 %104 %105
%104 = OpLabel
OpBranch %106
%106 = OpLabel
%111 = OpIAdd %6 %141 %20
OpStore %102 %111
OpBranch %103
%105 = OpLabel
OpStore %112 %9
OpBranch %113
%113 = OpLabel
%142 = OpPhi %6 %9 %105 %121 %116
OpLoopMerge %115 %116 Unroll|PeelCount|PartialCount 3 4
OpBranch %117
%117 = OpLabel
%119 = OpSLessThan %17 %142 %16
OpBranchConditional %119 %114 %115
%114 = OpLabel
OpBranch %116
%116 = OpLabel
%121 = OpIAdd %6 %142 %20
OpStore %112 %121
OpBranch %113
%115 = OpLabel
OpStore %122 %9
OpBranch %123
%123 = OpLabel
%143 = OpPhi %6 %9 %115 %131 %126
OpLoopMerge %125 %126 DependencyLength|MinIterations|MaxIterations|IterationMultiple|PeelCount|PartialCount 2 5 90 4 7 14
OpBranch %127
%127 = OpLabel
%129 = OpSLessThan %17 %143 %16
OpBranchConditional %129 %124 %125
%124 = OpLabel
OpBranch %126
%126 = OpLabel
%131 = OpIAdd %6 %143 %20
OpStore %122 %131
OpBranch %123
%125 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
// These are the loop headers together with the selection controls of their
// merge instructions:
// %10 None
// %23 Unroll
// %33 DontUnroll
// %43 DependencyInfinite
// %53 DependencyLength 3
// %63 MinIterations 10
// %73 MaxIterations 50
// %83 IterationMultiple 4
// %93 PeelCount 2
// %103 PartialCount 3
// %113 Unroll|PeelCount|PartialCount 3 4
// %123
// DependencyLength|MinIterations|MaxIterations|IterationMultiple|PeelCount|PartialCount
// 2 5 90 4 7 14
ASSERT_TRUE(TransformationSetLoopControl(10, SpvLoopControlMaskNone, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(10, SpvLoopControlUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(10, SpvLoopControlDontUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationSetLoopControl(
10, SpvLoopControlDependencyInfiniteMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationSetLoopControl(10, SpvLoopControlDependencyLengthMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationSetLoopControl(10, SpvLoopControlMinIterationsMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationSetLoopControl(10, SpvLoopControlMaxIterationsMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationSetLoopControl(
10, SpvLoopControlIterationMultipleMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(10, SpvLoopControlPeelCountMask, 3, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationSetLoopControl(10, SpvLoopControlPeelCountMask, 3, 3)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(10, SpvLoopControlPartialCountMask, 0, 3)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationSetLoopControl(10, SpvLoopControlPartialCountMask, 3, 3)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(
10,
SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask,
3, 3)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(10,
SpvLoopControlUnrollMask |
SpvLoopControlPeelCountMask |
SpvLoopControlPartialCountMask,
3, 3)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationSetLoopControl(10,
SpvLoopControlDontUnrollMask |
SpvLoopControlPeelCountMask |
SpvLoopControlPartialCountMask,
3, 3)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(23, SpvLoopControlMaskNone, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(23, SpvLoopControlUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(23, SpvLoopControlDontUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(
23,
SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask,
3, 3)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationSetLoopControl(23, SpvLoopControlMaxIterationsMask, 2, 3)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(33, SpvLoopControlMaskNone, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(33, SpvLoopControlUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(33, SpvLoopControlDontUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationSetLoopControl(33, SpvLoopControlMinIterationsMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(
33, SpvLoopControlUnrollMask | SpvLoopControlPeelCountMask, 5, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationSetLoopControl(33,
SpvLoopControlDontUnrollMask |
SpvLoopControlPartialCountMask,
0, 10)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(43, SpvLoopControlMaskNone, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(43, SpvLoopControlUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(43, SpvLoopControlDontUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(
43,
SpvLoopControlMaskNone | SpvLoopControlDependencyInfiniteMask,
0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(
43, SpvLoopControlUnrollMask | SpvLoopControlDependencyInfiniteMask,
0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(
43,
SpvLoopControlDontUnrollMask | SpvLoopControlDependencyInfiniteMask,
0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationSetLoopControl(43,
SpvLoopControlDependencyInfiniteMask |
SpvLoopControlDependencyLengthMask,
0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(
43, SpvLoopControlUnrollMask | SpvLoopControlPeelCountMask, 5, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(53, SpvLoopControlMaskNone, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(53, SpvLoopControlUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(53, SpvLoopControlDontUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationSetLoopControl(53, SpvLoopControlMaxIterationsMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(
53, SpvLoopControlMaskNone | SpvLoopControlDependencyLengthMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationSetLoopControl(
53, SpvLoopControlUnrollMask | SpvLoopControlDependencyInfiniteMask,
0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(
53, SpvLoopControlDontUnrollMask | SpvLoopControlDependencyLengthMask,
0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationSetLoopControl(53,
SpvLoopControlDependencyInfiniteMask |
SpvLoopControlDependencyLengthMask,
0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(
53,
SpvLoopControlUnrollMask | SpvLoopControlDependencyLengthMask |
SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask,
5, 3)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(63, SpvLoopControlMaskNone, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(63, SpvLoopControlUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(63, SpvLoopControlDontUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(63,
SpvLoopControlUnrollMask |
SpvLoopControlMinIterationsMask |
SpvLoopControlPeelCountMask |
SpvLoopControlPartialCountMask,
5, 3)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(63,
SpvLoopControlUnrollMask |
SpvLoopControlMinIterationsMask |
SpvLoopControlPeelCountMask,
23, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationSetLoopControl(
63,
SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask |
SpvLoopControlPeelCountMask,
2, 23)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(73, SpvLoopControlMaskNone, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(73, SpvLoopControlUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(73, SpvLoopControlDontUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationSetLoopControl(
73,
SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask |
SpvLoopControlPeelCountMask |
SpvLoopControlPartialCountMask,
5, 3)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(73,
SpvLoopControlUnrollMask |
SpvLoopControlMaxIterationsMask |
SpvLoopControlPeelCountMask,
23, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationSetLoopControl(
73,
SpvLoopControlUnrollMask | SpvLoopControlMaxIterationsMask |
SpvLoopControlPeelCountMask,
2, 23)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(83, SpvLoopControlMaskNone, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(83, SpvLoopControlUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(83, SpvLoopControlDontUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationSetLoopControl(
83,
SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask |
SpvLoopControlPeelCountMask |
SpvLoopControlPartialCountMask,
5, 3)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(83,
SpvLoopControlUnrollMask |
SpvLoopControlIterationMultipleMask |
SpvLoopControlPeelCountMask,
23, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationSetLoopControl(83,
SpvLoopControlUnrollMask |
SpvLoopControlIterationMultipleMask |
SpvLoopControlPeelCountMask,
2, 23)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(93, SpvLoopControlMaskNone, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(93, SpvLoopControlUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(93, SpvLoopControlDontUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(93, SpvLoopControlPeelCountMask, 8, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationSetLoopControl(93, SpvLoopControlPeelCountMask, 8, 8)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(93, SpvLoopControlPartialCountMask, 0, 8)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(
93,
SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask,
16, 8)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(103, SpvLoopControlMaskNone, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(103, SpvLoopControlUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(103, SpvLoopControlDontUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(103, SpvLoopControlPartialCountMask, 0, 60)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationSetLoopControl(103,
SpvLoopControlDontUnrollMask |
SpvLoopControlPartialCountMask,
0, 60)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(113, SpvLoopControlMaskNone, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(113, SpvLoopControlUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(113, SpvLoopControlDontUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(113, SpvLoopControlPeelCountMask, 12, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationSetLoopControl(
113,
SpvLoopControlIterationMultipleMask | SpvLoopControlPeelCountMask, 12,
0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(123, SpvLoopControlMaskNone, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(123, SpvLoopControlUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(123, SpvLoopControlDontUnrollMask, 0, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(
TransformationSetLoopControl(
123,
SpvLoopControlMinIterationsMask | SpvLoopControlMaxIterationsMask |
SpvLoopControlIterationMultipleMask |
SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask,
7, 8)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationSetLoopControl(123,
SpvLoopControlUnrollMask |
SpvLoopControlMinIterationsMask |
SpvLoopControlMaxIterationsMask |
SpvLoopControlPartialCountMask,
0, 9)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationSetLoopControl(
123,
SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask |
SpvLoopControlMaxIterationsMask |
SpvLoopControlPartialCountMask,
7, 9)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationSetLoopControl(
123,
SpvLoopControlDontUnrollMask | SpvLoopControlMinIterationsMask |
SpvLoopControlMaxIterationsMask | SpvLoopControlPartialCountMask,
7, 9)
.IsApplicable(context.get(), fact_manager));
TransformationSetLoopControl(10,
SpvLoopControlUnrollMask |
SpvLoopControlPeelCountMask |
SpvLoopControlPartialCountMask,
3, 3)
.Apply(context.get(), &fact_manager);
TransformationSetLoopControl(23, SpvLoopControlDontUnrollMask, 0, 0)
.Apply(context.get(), &fact_manager);
TransformationSetLoopControl(33, SpvLoopControlUnrollMask, 0, 0)
.Apply(context.get(), &fact_manager);
TransformationSetLoopControl(
43, SpvLoopControlDontUnrollMask | SpvLoopControlDependencyInfiniteMask,
0, 0)
.Apply(context.get(), &fact_manager);
TransformationSetLoopControl(53, SpvLoopControlMaskNone, 0, 0)
.Apply(context.get(), &fact_manager);
TransformationSetLoopControl(63,
SpvLoopControlUnrollMask |
SpvLoopControlMinIterationsMask |
SpvLoopControlPeelCountMask,
23, 0)
.Apply(context.get(), &fact_manager);
TransformationSetLoopControl(73,
SpvLoopControlUnrollMask |
SpvLoopControlMaxIterationsMask |
SpvLoopControlPeelCountMask,
23, 0)
.Apply(context.get(), &fact_manager);
TransformationSetLoopControl(83, SpvLoopControlDontUnrollMask, 0, 0)
.Apply(context.get(), &fact_manager);
TransformationSetLoopControl(
93, SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask, 16, 8)
.Apply(context.get(), &fact_manager);
TransformationSetLoopControl(103, SpvLoopControlPartialCountMask, 0, 60)
.Apply(context.get(), &fact_manager);
TransformationSetLoopControl(113, SpvLoopControlPeelCountMask, 12, 0)
.Apply(context.get(), &fact_manager);
TransformationSetLoopControl(
123,
SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask |
SpvLoopControlMaxIterationsMask | SpvLoopControlPartialCountMask,
0, 9)
.Apply(context.get(), &fact_manager);
std::string after_transformation = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%16 = OpConstant %6 100
%17 = OpTypeBool
%20 = OpConstant %6 1
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%22 = OpVariable %7 Function
%32 = OpVariable %7 Function
%42 = OpVariable %7 Function
%52 = OpVariable %7 Function
%62 = OpVariable %7 Function
%72 = OpVariable %7 Function
%82 = OpVariable %7 Function
%92 = OpVariable %7 Function
%102 = OpVariable %7 Function
%112 = OpVariable %7 Function
%122 = OpVariable %7 Function
OpStore %8 %9
OpBranch %10
%10 = OpLabel
%132 = OpPhi %6 %9 %5 %21 %13
OpLoopMerge %12 %13 Unroll|PeelCount|PartialCount 3 3
OpBranch %14
%14 = OpLabel
%18 = OpSLessThan %17 %132 %16
OpBranchConditional %18 %11 %12
%11 = OpLabel
OpBranch %13
%13 = OpLabel
%21 = OpIAdd %6 %132 %20
OpStore %8 %21
OpBranch %10
%12 = OpLabel
OpStore %22 %9
OpBranch %23
%23 = OpLabel
%133 = OpPhi %6 %9 %12 %31 %26
OpLoopMerge %25 %26 DontUnroll
OpBranch %27
%27 = OpLabel
%29 = OpSLessThan %17 %133 %16
OpBranchConditional %29 %24 %25
%24 = OpLabel
OpBranch %26
%26 = OpLabel
%31 = OpIAdd %6 %133 %20
OpStore %22 %31
OpBranch %23
%25 = OpLabel
OpStore %32 %9
OpBranch %33
%33 = OpLabel
%134 = OpPhi %6 %9 %25 %41 %36
OpLoopMerge %35 %36 Unroll
OpBranch %37
%37 = OpLabel
%39 = OpSLessThan %17 %134 %16
OpBranchConditional %39 %34 %35
%34 = OpLabel
OpBranch %36
%36 = OpLabel
%41 = OpIAdd %6 %134 %20
OpStore %32 %41
OpBranch %33
%35 = OpLabel
OpStore %42 %9
OpBranch %43
%43 = OpLabel
%135 = OpPhi %6 %9 %35 %51 %46
OpLoopMerge %45 %46 DontUnroll|DependencyInfinite
OpBranch %47
%47 = OpLabel
%49 = OpSLessThan %17 %135 %16
OpBranchConditional %49 %44 %45
%44 = OpLabel
OpBranch %46
%46 = OpLabel
%51 = OpIAdd %6 %135 %20
OpStore %42 %51
OpBranch %43
%45 = OpLabel
OpStore %52 %9
OpBranch %53
%53 = OpLabel
%136 = OpPhi %6 %9 %45 %61 %56
OpLoopMerge %55 %56 None
OpBranch %57
%57 = OpLabel
%59 = OpSLessThan %17 %136 %16
OpBranchConditional %59 %54 %55
%54 = OpLabel
OpBranch %56
%56 = OpLabel
%61 = OpIAdd %6 %136 %20
OpStore %52 %61
OpBranch %53
%55 = OpLabel
OpStore %62 %9
OpBranch %63
%63 = OpLabel
%137 = OpPhi %6 %9 %55 %71 %66
OpLoopMerge %65 %66 Unroll|MinIterations|PeelCount 10 23
OpBranch %67
%67 = OpLabel
%69 = OpSLessThan %17 %137 %16
OpBranchConditional %69 %64 %65
%64 = OpLabel
OpBranch %66
%66 = OpLabel
%71 = OpIAdd %6 %137 %20
OpStore %62 %71
OpBranch %63
%65 = OpLabel
OpStore %72 %9
OpBranch %73
%73 = OpLabel
%138 = OpPhi %6 %9 %65 %81 %76
OpLoopMerge %75 %76 Unroll|MaxIterations|PeelCount 50 23
OpBranch %77
%77 = OpLabel
%79 = OpSLessThan %17 %138 %16
OpBranchConditional %79 %74 %75
%74 = OpLabel
OpBranch %76
%76 = OpLabel
%81 = OpIAdd %6 %138 %20
OpStore %72 %81
OpBranch %73
%75 = OpLabel
OpStore %82 %9
OpBranch %83
%83 = OpLabel
%139 = OpPhi %6 %9 %75 %91 %86
OpLoopMerge %85 %86 DontUnroll
OpBranch %87
%87 = OpLabel
%89 = OpSLessThan %17 %139 %16
OpBranchConditional %89 %84 %85
%84 = OpLabel
OpBranch %86
%86 = OpLabel
%91 = OpIAdd %6 %139 %20
OpStore %82 %91
OpBranch %83
%85 = OpLabel
OpStore %92 %9
OpBranch %93
%93 = OpLabel
%140 = OpPhi %6 %9 %85 %101 %96
OpLoopMerge %95 %96 PeelCount|PartialCount 16 8
OpBranch %97
%97 = OpLabel
%99 = OpSLessThan %17 %140 %16
OpBranchConditional %99 %94 %95
%94 = OpLabel
OpBranch %96
%96 = OpLabel
%101 = OpIAdd %6 %140 %20
OpStore %92 %101
OpBranch %93
%95 = OpLabel
OpStore %102 %9
OpBranch %103
%103 = OpLabel
%141 = OpPhi %6 %9 %95 %111 %106
OpLoopMerge %105 %106 PartialCount 60
OpBranch %107
%107 = OpLabel
%109 = OpSLessThan %17 %141 %16
OpBranchConditional %109 %104 %105
%104 = OpLabel
OpBranch %106
%106 = OpLabel
%111 = OpIAdd %6 %141 %20
OpStore %102 %111
OpBranch %103
%105 = OpLabel
OpStore %112 %9
OpBranch %113
%113 = OpLabel
%142 = OpPhi %6 %9 %105 %121 %116
OpLoopMerge %115 %116 PeelCount 12
OpBranch %117
%117 = OpLabel
%119 = OpSLessThan %17 %142 %16
OpBranchConditional %119 %114 %115
%114 = OpLabel
OpBranch %116
%116 = OpLabel
%121 = OpIAdd %6 %142 %20
OpStore %112 %121
OpBranch %113
%115 = OpLabel
OpStore %122 %9
OpBranch %123
%123 = OpLabel
%143 = OpPhi %6 %9 %115 %131 %126
OpLoopMerge %125 %126 Unroll|MinIterations|MaxIterations|PartialCount 5 90 9
OpBranch %127
%127 = OpLabel
%129 = OpSLessThan %17 %143 %16
OpBranchConditional %129 %124 %125
%124 = OpLabel
OpBranch %126
%126 = OpLabel
%131 = OpIAdd %6 %143 %20
OpStore %122 %131
OpBranch %123
%125 = OpLabel
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
TEST(TransformationSetLoopControlTest, CheckSPIRVVersionsRespected) {
// This test checks that we do not allow introducing PeelCount and
// PartialCount loop controls if the SPIR-V version being used does not
// support them.
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %8 "i"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%16 = OpConstant %6 10
%17 = OpTypeBool
%20 = OpConstant %6 1
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
OpStore %8 %9
OpBranch %10
%10 = OpLabel
OpLoopMerge %12 %13 None
OpBranch %14
%14 = OpLabel
%15 = OpLoad %6 %8
%18 = OpSLessThan %17 %15 %16
OpBranchConditional %18 %11 %12
%11 = OpLabel
OpBranch %13
%13 = OpLabel
%19 = OpLoad %6 %8
%21 = OpIAdd %6 %19 %20
OpStore %8 %21
OpBranch %10
%12 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto consumer = nullptr;
const auto context_1_0 =
BuildModule(SPV_ENV_UNIVERSAL_1_0, consumer, shader, kFuzzAssembleOption);
const auto context_1_1 =
BuildModule(SPV_ENV_UNIVERSAL_1_1, consumer, shader, kFuzzAssembleOption);
const auto context_1_2 =
BuildModule(SPV_ENV_UNIVERSAL_1_2, consumer, shader, kFuzzAssembleOption);
const auto context_1_3 =
BuildModule(SPV_ENV_UNIVERSAL_1_3, consumer, shader, kFuzzAssembleOption);
const auto context_1_4 =
BuildModule(SPV_ENV_UNIVERSAL_1_4, consumer, shader, kFuzzAssembleOption);
const auto context_1_5 =
BuildModule(SPV_ENV_UNIVERSAL_1_5, consumer, shader, kFuzzAssembleOption);
FactManager fact_manager;
TransformationSetLoopControl set_peel_and_partial(
10, SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask, 4, 4);
// PeelCount and PartialCount were introduced in SPIRV 1.4, so are not valid
// in the context of older versions.
ASSERT_FALSE(
set_peel_and_partial.IsApplicable(context_1_0.get(), fact_manager));
ASSERT_FALSE(
set_peel_and_partial.IsApplicable(context_1_1.get(), fact_manager));
ASSERT_FALSE(
set_peel_and_partial.IsApplicable(context_1_2.get(), fact_manager));
ASSERT_FALSE(
set_peel_and_partial.IsApplicable(context_1_3.get(), fact_manager));
ASSERT_TRUE(
set_peel_and_partial.IsApplicable(context_1_4.get(), fact_manager));
ASSERT_TRUE(
set_peel_and_partial.IsApplicable(context_1_5.get(), fact_manager));
}
} // namespace
} // namespace fuzz
} // namespace spvtools

View File

@@ -1,432 +0,0 @@
// Copyright (c) 2019 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 "source/fuzz/transformation_set_memory_operands_mask.h"
#include "source/fuzz/instruction_descriptor.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
namespace fuzz {
namespace {
TEST(TransformationSetMemoryOperandsMaskTest, PreSpirv14) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %7 "Point3D"
OpMemberName %7 0 "x"
OpMemberName %7 1 "y"
OpMemberName %7 2 "z"
OpName %12 "global_points"
OpName %15 "block"
OpMemberName %15 0 "in_points"
OpMemberName %15 1 "in_point"
OpName %17 ""
OpName %133 "local_points"
OpMemberDecorate %7 0 Offset 0
OpMemberDecorate %7 1 Offset 4
OpMemberDecorate %7 2 Offset 8
OpDecorate %10 ArrayStride 16
OpMemberDecorate %15 0 Offset 0
OpMemberDecorate %15 1 Offset 192
OpDecorate %15 Block
OpDecorate %17 DescriptorSet 0
OpDecorate %17 Binding 0
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypeStruct %6 %6 %6
%8 = OpTypeInt 32 0
%9 = OpConstant %8 12
%10 = OpTypeArray %7 %9
%11 = OpTypePointer Private %10
%12 = OpVariable %11 Private
%15 = OpTypeStruct %10 %7
%16 = OpTypePointer Uniform %15
%17 = OpVariable %16 Uniform
%18 = OpTypeInt 32 1
%19 = OpConstant %18 0
%20 = OpTypePointer Uniform %10
%24 = OpTypePointer Private %7
%27 = OpTypePointer Private %6
%30 = OpConstant %18 1
%132 = OpTypePointer Function %10
%135 = OpTypePointer Uniform %7
%145 = OpTypePointer Function %7
%4 = OpFunction %2 None %3
%5 = OpLabel
%133 = OpVariable %132 Function
%21 = OpAccessChain %20 %17 %19
OpCopyMemory %12 %21 Aligned 16
OpCopyMemory %133 %12 Volatile
%136 = OpAccessChain %135 %17 %30
%138 = OpAccessChain %24 %12 %19
OpCopyMemory %138 %136 None
%146 = OpAccessChain %145 %133 %30
%147 = OpLoad %7 %146 Volatile|Nontemporal|Aligned 16
%148 = OpAccessChain %24 %12 %19
OpStore %148 %147 Nontemporal
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
// Not OK: the instruction is not a memory access.
ASSERT_FALSE(TransformationSetMemoryOperandsMask(
MakeInstructionDescriptor(21, SpvOpAccessChain, 0),
SpvMemoryAccessMaskNone, 0)
.IsApplicable(context.get(), fact_manager));
// Not OK to remove Aligned
ASSERT_FALSE(TransformationSetMemoryOperandsMask(
MakeInstructionDescriptor(147, SpvOpLoad, 0),
SpvMemoryAccessVolatileMask | SpvMemoryAccessNontemporalMask,
0)
.IsApplicable(context.get(), fact_manager));
TransformationSetMemoryOperandsMask transformation1(
MakeInstructionDescriptor(147, SpvOpLoad, 0),
SpvMemoryAccessAlignedMask | SpvMemoryAccessVolatileMask, 0);
ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
transformation1.Apply(context.get(), &fact_manager);
// Not OK to remove Aligned
ASSERT_FALSE(TransformationSetMemoryOperandsMask(
MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
SpvMemoryAccessMaskNone, 0)
.IsApplicable(context.get(), fact_manager));
// OK: leaves the mask as is
ASSERT_TRUE(TransformationSetMemoryOperandsMask(
MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
SpvMemoryAccessAlignedMask, 0)
.IsApplicable(context.get(), fact_manager));
// OK: adds Nontemporal and Volatile
TransformationSetMemoryOperandsMask transformation2(
MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
SpvMemoryAccessAlignedMask | SpvMemoryAccessNontemporalMask |
SpvMemoryAccessVolatileMask,
0);
ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
transformation2.Apply(context.get(), &fact_manager);
// Not OK to remove Volatile
ASSERT_FALSE(TransformationSetMemoryOperandsMask(
MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
SpvMemoryAccessNontemporalMask, 0)
.IsApplicable(context.get(), fact_manager));
// Not OK to add Aligned
ASSERT_FALSE(TransformationSetMemoryOperandsMask(
MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
SpvMemoryAccessAlignedMask | SpvMemoryAccessVolatileMask, 0)
.IsApplicable(context.get(), fact_manager));
// OK: adds Nontemporal
TransformationSetMemoryOperandsMask transformation3(
MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0);
ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
transformation3.Apply(context.get(), &fact_manager);
// OK: adds Nontemporal and Volatile
TransformationSetMemoryOperandsMask transformation4(
MakeInstructionDescriptor(138, SpvOpCopyMemory, 0),
SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0);
ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager));
transformation4.Apply(context.get(), &fact_manager);
// OK: removes Nontemporal, adds Volatile
TransformationSetMemoryOperandsMask transformation5(
MakeInstructionDescriptor(148, SpvOpStore, 0),
SpvMemoryAccessVolatileMask, 0);
ASSERT_TRUE(transformation5.IsApplicable(context.get(), fact_manager));
transformation5.Apply(context.get(), &fact_manager);
std::string after_transformation = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %7 "Point3D"
OpMemberName %7 0 "x"
OpMemberName %7 1 "y"
OpMemberName %7 2 "z"
OpName %12 "global_points"
OpName %15 "block"
OpMemberName %15 0 "in_points"
OpMemberName %15 1 "in_point"
OpName %17 ""
OpName %133 "local_points"
OpMemberDecorate %7 0 Offset 0
OpMemberDecorate %7 1 Offset 4
OpMemberDecorate %7 2 Offset 8
OpDecorate %10 ArrayStride 16
OpMemberDecorate %15 0 Offset 0
OpMemberDecorate %15 1 Offset 192
OpDecorate %15 Block
OpDecorate %17 DescriptorSet 0
OpDecorate %17 Binding 0
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypeStruct %6 %6 %6
%8 = OpTypeInt 32 0
%9 = OpConstant %8 12
%10 = OpTypeArray %7 %9
%11 = OpTypePointer Private %10
%12 = OpVariable %11 Private
%15 = OpTypeStruct %10 %7
%16 = OpTypePointer Uniform %15
%17 = OpVariable %16 Uniform
%18 = OpTypeInt 32 1
%19 = OpConstant %18 0
%20 = OpTypePointer Uniform %10
%24 = OpTypePointer Private %7
%27 = OpTypePointer Private %6
%30 = OpConstant %18 1
%132 = OpTypePointer Function %10
%135 = OpTypePointer Uniform %7
%145 = OpTypePointer Function %7
%4 = OpFunction %2 None %3
%5 = OpLabel
%133 = OpVariable %132 Function
%21 = OpAccessChain %20 %17 %19
OpCopyMemory %12 %21 Aligned|Nontemporal|Volatile 16
OpCopyMemory %133 %12 Nontemporal|Volatile
%136 = OpAccessChain %135 %17 %30
%138 = OpAccessChain %24 %12 %19
OpCopyMemory %138 %136 Nontemporal|Volatile
%146 = OpAccessChain %145 %133 %30
%147 = OpLoad %7 %146 Aligned|Volatile 16
%148 = OpAccessChain %24 %12 %19
OpStore %148 %147 Volatile
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
TEST(TransformationSetMemoryOperandsMaskTest, Spirv14) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %12 %17
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %7 "Point3D"
OpMemberName %7 0 "x"
OpMemberName %7 1 "y"
OpMemberName %7 2 "z"
OpName %12 "global_points"
OpName %15 "block"
OpMemberName %15 0 "in_points"
OpMemberName %15 1 "in_point"
OpName %17 ""
OpName %133 "local_points"
OpMemberDecorate %7 0 Offset 0
OpMemberDecorate %7 1 Offset 4
OpMemberDecorate %7 2 Offset 8
OpDecorate %10 ArrayStride 16
OpMemberDecorate %15 0 Offset 0
OpMemberDecorate %15 1 Offset 192
OpDecorate %15 Block
OpDecorate %17 DescriptorSet 0
OpDecorate %17 Binding 0
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypeStruct %6 %6 %6
%8 = OpTypeInt 32 0
%9 = OpConstant %8 12
%10 = OpTypeArray %7 %9
%11 = OpTypePointer Private %10
%12 = OpVariable %11 Private
%15 = OpTypeStruct %10 %7
%16 = OpTypePointer Uniform %15
%17 = OpVariable %16 Uniform
%18 = OpTypeInt 32 1
%19 = OpConstant %18 0
%20 = OpTypePointer Uniform %10
%24 = OpTypePointer Private %7
%27 = OpTypePointer Private %6
%30 = OpConstant %18 1
%132 = OpTypePointer Function %10
%135 = OpTypePointer Uniform %7
%145 = OpTypePointer Function %7
%4 = OpFunction %2 None %3
%5 = OpLabel
%133 = OpVariable %132 Function
%21 = OpAccessChain %20 %17 %19
OpCopyMemory %12 %21 Aligned 16 Nontemporal|Aligned 16
OpCopyMemory %133 %12 Volatile
%136 = OpAccessChain %135 %17 %30
%138 = OpAccessChain %24 %12 %19
OpCopyMemory %138 %136 None Aligned 16
OpCopyMemory %138 %136 Aligned 16
%146 = OpAccessChain %145 %133 %30
%147 = OpLoad %7 %146 Volatile|Nontemporal|Aligned 16
%148 = OpAccessChain %24 %12 %19
OpStore %148 %147 Nontemporal
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
TransformationSetMemoryOperandsMask transformation1(
MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
SpvMemoryAccessAlignedMask | SpvMemoryAccessVolatileMask, 1);
// Bad: cannot remove aligned
ASSERT_FALSE(TransformationSetMemoryOperandsMask(
MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
SpvMemoryAccessVolatileMask, 1)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
transformation1.Apply(context.get(), &fact_manager);
TransformationSetMemoryOperandsMask transformation2(
MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 1);
// Bad: cannot remove volatile
ASSERT_FALSE(TransformationSetMemoryOperandsMask(
MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
SpvMemoryAccessNontemporalMask, 0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
transformation2.Apply(context.get(), &fact_manager);
TransformationSetMemoryOperandsMask transformation3(
MakeInstructionDescriptor(138, SpvOpCopyMemory, 0),
SpvMemoryAccessAlignedMask | SpvMemoryAccessNontemporalMask, 1);
// Bad: the first mask is None, so Aligned cannot be added to it.
ASSERT_FALSE(TransformationSetMemoryOperandsMask(
MakeInstructionDescriptor(138, SpvOpCopyMemory, 0),
SpvMemoryAccessAlignedMask | SpvMemoryAccessNontemporalMask,
0)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
transformation3.Apply(context.get(), &fact_manager);
TransformationSetMemoryOperandsMask transformation4(
MakeInstructionDescriptor(138, SpvOpCopyMemory, 1),
SpvMemoryAccessVolatileMask, 1);
ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager));
transformation4.Apply(context.get(), &fact_manager);
TransformationSetMemoryOperandsMask transformation5(
MakeInstructionDescriptor(147, SpvOpLoad, 0),
SpvMemoryAccessVolatileMask | SpvMemoryAccessAlignedMask, 0);
ASSERT_TRUE(transformation5.IsApplicable(context.get(), fact_manager));
transformation5.Apply(context.get(), &fact_manager);
TransformationSetMemoryOperandsMask transformation6(
MakeInstructionDescriptor(148, SpvOpStore, 0), SpvMemoryAccessMaskNone,
0);
ASSERT_TRUE(transformation6.IsApplicable(context.get(), fact_manager));
transformation6.Apply(context.get(), &fact_manager);
std::string after_transformation = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %12 %17
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %7 "Point3D"
OpMemberName %7 0 "x"
OpMemberName %7 1 "y"
OpMemberName %7 2 "z"
OpName %12 "global_points"
OpName %15 "block"
OpMemberName %15 0 "in_points"
OpMemberName %15 1 "in_point"
OpName %17 ""
OpName %133 "local_points"
OpMemberDecorate %7 0 Offset 0
OpMemberDecorate %7 1 Offset 4
OpMemberDecorate %7 2 Offset 8
OpDecorate %10 ArrayStride 16
OpMemberDecorate %15 0 Offset 0
OpMemberDecorate %15 1 Offset 192
OpDecorate %15 Block
OpDecorate %17 DescriptorSet 0
OpDecorate %17 Binding 0
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypeStruct %6 %6 %6
%8 = OpTypeInt 32 0
%9 = OpConstant %8 12
%10 = OpTypeArray %7 %9
%11 = OpTypePointer Private %10
%12 = OpVariable %11 Private
%15 = OpTypeStruct %10 %7
%16 = OpTypePointer Uniform %15
%17 = OpVariable %16 Uniform
%18 = OpTypeInt 32 1
%19 = OpConstant %18 0
%20 = OpTypePointer Uniform %10
%24 = OpTypePointer Private %7
%27 = OpTypePointer Private %6
%30 = OpConstant %18 1
%132 = OpTypePointer Function %10
%135 = OpTypePointer Uniform %7
%145 = OpTypePointer Function %7
%4 = OpFunction %2 None %3
%5 = OpLabel
%133 = OpVariable %132 Function
%21 = OpAccessChain %20 %17 %19
OpCopyMemory %12 %21 Aligned 16 Aligned|Volatile 16
OpCopyMemory %133 %12 Volatile Nontemporal|Volatile
%136 = OpAccessChain %135 %17 %30
%138 = OpAccessChain %24 %12 %19
OpCopyMemory %138 %136 None Aligned|Nontemporal 16
OpCopyMemory %138 %136 Aligned 16 Volatile
%146 = OpAccessChain %145 %133 %30
%147 = OpLoad %7 %146 Volatile|Aligned 16
%148 = OpAccessChain %24 %12 %19
OpStore %148 %147 None
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
} // namespace
} // namespace fuzz
} // namespace spvtools

View File

@@ -1,219 +0,0 @@
// Copyright (c) 2019 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 "source/fuzz/transformation_set_selection_control.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
namespace fuzz {
namespace {
TEST(TransformationSetSelectionControlTest, VariousScenarios) {
// This is a simple transformation; this test captures the important things
// to check for.
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %8 "i"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%16 = OpConstant %6 10
%17 = OpTypeBool
%20 = OpConstant %6 3
%25 = OpConstant %6 1
%28 = OpConstant %6 2
%38 = OpConstant %6 4
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
OpStore %8 %9
OpBranch %10
%10 = OpLabel
OpLoopMerge %12 %13 None
OpBranch %14
%14 = OpLabel
%15 = OpLoad %6 %8
%18 = OpSLessThan %17 %15 %16
OpBranchConditional %18 %11 %12
%11 = OpLabel
%19 = OpLoad %6 %8
%21 = OpSGreaterThan %17 %19 %20
OpSelectionMerge %23 Flatten
OpBranchConditional %21 %22 %23
%22 = OpLabel
%24 = OpLoad %6 %8
%26 = OpIAdd %6 %24 %25
OpStore %8 %26
OpBranch %23
%23 = OpLabel
%27 = OpLoad %6 %8
%29 = OpSLessThan %17 %27 %28
OpSelectionMerge %31 DontFlatten
OpBranchConditional %29 %30 %31
%30 = OpLabel
%32 = OpLoad %6 %8
%33 = OpISub %6 %32 %25
OpStore %8 %33
OpBranch %31
%31 = OpLabel
%34 = OpLoad %6 %8
OpSelectionMerge %37 None
OpSwitch %34 %36 0 %35
%36 = OpLabel
OpBranch %37
%35 = OpLabel
%39 = OpLoad %6 %8
%40 = OpIAdd %6 %39 %38
OpStore %8 %40
OpBranch %36
%37 = OpLabel
OpBranch %13
%13 = OpLabel
%43 = OpLoad %6 %8
%44 = OpIAdd %6 %43 %25
OpStore %8 %44
OpBranch %10
%12 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
FactManager fact_manager;
// %44 is not a block
ASSERT_FALSE(
TransformationSetSelectionControl(44, SpvSelectionControlFlattenMask)
.IsApplicable(context.get(), fact_manager));
// %13 does not end with OpSelectionMerge
ASSERT_FALSE(
TransformationSetSelectionControl(13, SpvSelectionControlMaskNone)
.IsApplicable(context.get(), fact_manager));
// %10 ends in OpLoopMerge, not OpSelectionMerge
ASSERT_FALSE(
TransformationSetSelectionControl(10, SpvSelectionControlMaskNone)
.IsApplicable(context.get(), fact_manager));
TransformationSetSelectionControl transformation1(
11, SpvSelectionControlDontFlattenMask);
ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
transformation1.Apply(context.get(), &fact_manager);
TransformationSetSelectionControl transformation2(
23, SpvSelectionControlFlattenMask);
ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
transformation2.Apply(context.get(), &fact_manager);
TransformationSetSelectionControl transformation3(
31, SpvSelectionControlMaskNone);
ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
transformation3.Apply(context.get(), &fact_manager);
TransformationSetSelectionControl transformation4(
31, SpvSelectionControlFlattenMask);
ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager));
transformation4.Apply(context.get(), &fact_manager);
std::string after_transformation = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %8 "i"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%16 = OpConstant %6 10
%17 = OpTypeBool
%20 = OpConstant %6 3
%25 = OpConstant %6 1
%28 = OpConstant %6 2
%38 = OpConstant %6 4
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
OpStore %8 %9
OpBranch %10
%10 = OpLabel
OpLoopMerge %12 %13 None
OpBranch %14
%14 = OpLabel
%15 = OpLoad %6 %8
%18 = OpSLessThan %17 %15 %16
OpBranchConditional %18 %11 %12
%11 = OpLabel
%19 = OpLoad %6 %8
%21 = OpSGreaterThan %17 %19 %20
OpSelectionMerge %23 DontFlatten
OpBranchConditional %21 %22 %23
%22 = OpLabel
%24 = OpLoad %6 %8
%26 = OpIAdd %6 %24 %25
OpStore %8 %26
OpBranch %23
%23 = OpLabel
%27 = OpLoad %6 %8
%29 = OpSLessThan %17 %27 %28
OpSelectionMerge %31 Flatten
OpBranchConditional %29 %30 %31
%30 = OpLabel
%32 = OpLoad %6 %8
%33 = OpISub %6 %32 %25
OpStore %8 %33
OpBranch %31
%31 = OpLabel
%34 = OpLoad %6 %8
OpSelectionMerge %37 Flatten
OpSwitch %34 %36 0 %35
%36 = OpLabel
OpBranch %37
%35 = OpLabel
%39 = OpLoad %6 %8
%40 = OpIAdd %6 %39 %38
OpStore %8 %40
OpBranch %36
%37 = OpLabel
OpBranch %13
%13 = OpLabel
%43 = OpLoad %6 %8
%44 = OpIAdd %6 %43 %25
OpStore %8 %44
OpBranch %10
%12 = OpLabel
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
} // namespace
} // namespace fuzz
} // namespace spvtools

View File

@@ -1,779 +0,0 @@
// Copyright (c) 2019 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 "source/fuzz/transformation_split_block.h"
#include "source/fuzz/instruction_descriptor.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
namespace fuzz {
namespace {
TEST(TransformationSplitBlockTest, NotApplicable) {
// The SPIR-V in this test came from the following fragment shader, with
// local store elimination applied to get some OpPhi instructions.
//
// void main() {
// int x;
// int i;
// for (i = 0; i < 100; i++) {
// x += i;
// }
// }
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %8 "i"
OpName %19 "x"
OpDecorate %8 RelaxedPrecision
OpDecorate %19 RelaxedPrecision
OpDecorate %22 RelaxedPrecision
OpDecorate %25 RelaxedPrecision
OpDecorate %26 RelaxedPrecision
OpDecorate %27 RelaxedPrecision
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%16 = OpConstant %6 100
%17 = OpTypeBool
%24 = OpConstant %6 1
%28 = OpUndef %6
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%19 = OpVariable %7 Function
OpStore %8 %9
OpBranch %10
%10 = OpLabel
%27 = OpPhi %6 %28 %5 %22 %13
%26 = OpPhi %6 %9 %5 %25 %13
OpLoopMerge %12 %13 None
OpBranch %14
%14 = OpLabel
%18 = OpSLessThan %17 %26 %16
OpBranchConditional %18 %11 %12
%11 = OpLabel
%22 = OpIAdd %6 %27 %26
OpStore %19 %22
OpBranch %13
%13 = OpLabel
%25 = OpIAdd %6 %26 %24
OpStore %8 %25
OpBranch %10
%12 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
FactManager fact_manager;
// No split before OpVariable
ASSERT_FALSE(TransformationSplitBlock(
MakeInstructionDescriptor(8, SpvOpVariable, 0), 100)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationSplitBlock(
MakeInstructionDescriptor(8, SpvOpVariable, 1), 100)
.IsApplicable(context.get(), fact_manager));
// No split before OpLabel
ASSERT_FALSE(TransformationSplitBlock(
MakeInstructionDescriptor(14, SpvOpLabel, 0), 100)
.IsApplicable(context.get(), fact_manager));
// No split if base instruction is outside a function
ASSERT_FALSE(
TransformationSplitBlock(MakeInstructionDescriptor(1, SpvOpLabel, 0), 100)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationSplitBlock(
MakeInstructionDescriptor(1, SpvOpExecutionMode, 0), 100)
.IsApplicable(context.get(), fact_manager));
// No split if block is loop header
ASSERT_FALSE(
TransformationSplitBlock(MakeInstructionDescriptor(27, SpvOpPhi, 0), 100)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationSplitBlock(MakeInstructionDescriptor(27, SpvOpPhi, 1), 100)
.IsApplicable(context.get(), fact_manager));
// No split if base instruction does not exist
ASSERT_FALSE(
TransformationSplitBlock(MakeInstructionDescriptor(88, SpvOpIAdd, 0), 100)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationSplitBlock(
MakeInstructionDescriptor(88, SpvOpIMul, 22), 100)
.IsApplicable(context.get(), fact_manager));
// No split if too many instructions with the desired opcode are skipped
ASSERT_FALSE(
TransformationSplitBlock(
MakeInstructionDescriptor(18, SpvOpBranchConditional, 1), 100)
.IsApplicable(context.get(), fact_manager));
// No split if id in use
ASSERT_FALSE(TransformationSplitBlock(
MakeInstructionDescriptor(18, SpvOpSLessThan, 0), 27)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationSplitBlock(
MakeInstructionDescriptor(18, SpvOpSLessThan, 0), 14)
.IsApplicable(context.get(), fact_manager));
}
TEST(TransformationSplitBlockTest, SplitBlockSeveralTimes) {
// The SPIR-V in this test came from the following fragment shader:
//
// void main() {
// int a;
// int b;
// a = 1;
// b = a;
// a = b;
// b = 2;
// b++;
// }
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %8 "a"
OpName %10 "b"
OpDecorate %8 RelaxedPrecision
OpDecorate %10 RelaxedPrecision
OpDecorate %11 RelaxedPrecision
OpDecorate %12 RelaxedPrecision
OpDecorate %14 RelaxedPrecision
OpDecorate %15 RelaxedPrecision
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 1
%13 = OpConstant %6 2
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
OpStore %8 %9
%11 = OpLoad %6 %8
OpStore %10 %11
%12 = OpLoad %6 %10
OpStore %8 %12
OpStore %10 %13
%14 = OpLoad %6 %10
%15 = OpIAdd %6 %14 %9
OpStore %10 %15
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
FactManager fact_manager;
auto split_1 = TransformationSplitBlock(
MakeInstructionDescriptor(5, SpvOpStore, 0), 100);
ASSERT_TRUE(split_1.IsApplicable(context.get(), fact_manager));
split_1.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
std::string after_split_1 = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %8 "a"
OpName %10 "b"
OpDecorate %8 RelaxedPrecision
OpDecorate %10 RelaxedPrecision
OpDecorate %11 RelaxedPrecision
OpDecorate %12 RelaxedPrecision
OpDecorate %14 RelaxedPrecision
OpDecorate %15 RelaxedPrecision
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 1
%13 = OpConstant %6 2
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
OpBranch %100
%100 = OpLabel
OpStore %8 %9
%11 = OpLoad %6 %8
OpStore %10 %11
%12 = OpLoad %6 %10
OpStore %8 %12
OpStore %10 %13
%14 = OpLoad %6 %10
%15 = OpIAdd %6 %14 %9
OpStore %10 %15
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_split_1, context.get()));
auto split_2 = TransformationSplitBlock(
MakeInstructionDescriptor(11, SpvOpStore, 0), 101);
ASSERT_TRUE(split_2.IsApplicable(context.get(), fact_manager));
split_2.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
std::string after_split_2 = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %8 "a"
OpName %10 "b"
OpDecorate %8 RelaxedPrecision
OpDecorate %10 RelaxedPrecision
OpDecorate %11 RelaxedPrecision
OpDecorate %12 RelaxedPrecision
OpDecorate %14 RelaxedPrecision
OpDecorate %15 RelaxedPrecision
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 1
%13 = OpConstant %6 2
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
OpBranch %100
%100 = OpLabel
OpStore %8 %9
%11 = OpLoad %6 %8
OpBranch %101
%101 = OpLabel
OpStore %10 %11
%12 = OpLoad %6 %10
OpStore %8 %12
OpStore %10 %13
%14 = OpLoad %6 %10
%15 = OpIAdd %6 %14 %9
OpStore %10 %15
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_split_2, context.get()));
auto split_3 = TransformationSplitBlock(
MakeInstructionDescriptor(14, SpvOpLoad, 0), 102);
ASSERT_TRUE(split_3.IsApplicable(context.get(), fact_manager));
split_3.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
std::string after_split_3 = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %8 "a"
OpName %10 "b"
OpDecorate %8 RelaxedPrecision
OpDecorate %10 RelaxedPrecision
OpDecorate %11 RelaxedPrecision
OpDecorate %12 RelaxedPrecision
OpDecorate %14 RelaxedPrecision
OpDecorate %15 RelaxedPrecision
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 1
%13 = OpConstant %6 2
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
OpBranch %100
%100 = OpLabel
OpStore %8 %9
%11 = OpLoad %6 %8
OpBranch %101
%101 = OpLabel
OpStore %10 %11
%12 = OpLoad %6 %10
OpStore %8 %12
OpStore %10 %13
OpBranch %102
%102 = OpLabel
%14 = OpLoad %6 %10
%15 = OpIAdd %6 %14 %9
OpStore %10 %15
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_split_3, context.get()));
}
TEST(TransformationSplitBlockTest, SplitBlockBeforeSelectBranch) {
// The SPIR-V in this test came from the following fragment shader:
//
// void main() {
// int x, y;
// x = 2;
// if (x < y) {
// y = 3;
// } else {
// y = 4;
// }
// }
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %8 "x"
OpName %11 "y"
OpDecorate %8 RelaxedPrecision
OpDecorate %10 RelaxedPrecision
OpDecorate %11 RelaxedPrecision
OpDecorate %12 RelaxedPrecision
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 2
%13 = OpTypeBool
%17 = OpConstant %6 3
%19 = OpConstant %6 4
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%11 = OpVariable %7 Function
OpStore %8 %9
%10 = OpLoad %6 %8
%12 = OpLoad %6 %11
%14 = OpSLessThan %13 %10 %12
OpSelectionMerge %16 None
OpBranchConditional %14 %15 %18
%15 = OpLabel
OpStore %11 %17
OpBranch %16
%18 = OpLabel
OpStore %11 %19
OpBranch %16
%16 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
FactManager fact_manager;
// Illegal to split between the merge and the conditional branch.
ASSERT_FALSE(
TransformationSplitBlock(
MakeInstructionDescriptor(14, SpvOpBranchConditional, 0), 100)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationSplitBlock(
MakeInstructionDescriptor(12, SpvOpBranchConditional, 0), 100)
.IsApplicable(context.get(), fact_manager));
auto split = TransformationSplitBlock(
MakeInstructionDescriptor(14, SpvOpSelectionMerge, 0), 100);
ASSERT_TRUE(split.IsApplicable(context.get(), fact_manager));
split.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
std::string after_split = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %8 "x"
OpName %11 "y"
OpDecorate %8 RelaxedPrecision
OpDecorate %10 RelaxedPrecision
OpDecorate %11 RelaxedPrecision
OpDecorate %12 RelaxedPrecision
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 2
%13 = OpTypeBool
%17 = OpConstant %6 3
%19 = OpConstant %6 4
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%11 = OpVariable %7 Function
OpStore %8 %9
%10 = OpLoad %6 %8
%12 = OpLoad %6 %11
%14 = OpSLessThan %13 %10 %12
OpBranch %100
%100 = OpLabel
OpSelectionMerge %16 None
OpBranchConditional %14 %15 %18
%15 = OpLabel
OpStore %11 %17
OpBranch %16
%18 = OpLabel
OpStore %11 %19
OpBranch %16
%16 = OpLabel
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_split, context.get()));
}
TEST(TransformationSplitBlockTest, SplitBlockBeforeSwitchBranch) {
// The SPIR-V in this test came from the following fragment shader:
//
// void main() {
// int x, y;
// switch (y) {
// case 1:
// x = 2;
// case 2:
// break;
// case 3:
// x = 4;
// default:
// x = 6;
// }
// }
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %8 "y"
OpName %15 "x"
OpDecorate %8 RelaxedPrecision
OpDecorate %9 RelaxedPrecision
OpDecorate %15 RelaxedPrecision
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%16 = OpConstant %6 2
%18 = OpConstant %6 4
%19 = OpConstant %6 6
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%15 = OpVariable %7 Function
%9 = OpLoad %6 %8
OpSelectionMerge %14 None
OpSwitch %9 %13 1 %10 2 %11 3 %12
%13 = OpLabel
OpStore %15 %19
OpBranch %14
%10 = OpLabel
OpStore %15 %16
OpBranch %11
%11 = OpLabel
OpBranch %14
%12 = OpLabel
OpStore %15 %18
OpBranch %13
%14 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
FactManager fact_manager;
// Illegal to split between the merge and the conditional branch.
ASSERT_FALSE(TransformationSplitBlock(
MakeInstructionDescriptor(9, SpvOpSwitch, 0), 100)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationSplitBlock(
MakeInstructionDescriptor(15, SpvOpSwitch, 0), 100)
.IsApplicable(context.get(), fact_manager));
auto split = TransformationSplitBlock(
MakeInstructionDescriptor(9, SpvOpSelectionMerge, 0), 100);
ASSERT_TRUE(split.IsApplicable(context.get(), fact_manager));
split.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
std::string after_split = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %8 "y"
OpName %15 "x"
OpDecorate %8 RelaxedPrecision
OpDecorate %9 RelaxedPrecision
OpDecorate %15 RelaxedPrecision
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%16 = OpConstant %6 2
%18 = OpConstant %6 4
%19 = OpConstant %6 6
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%15 = OpVariable %7 Function
%9 = OpLoad %6 %8
OpBranch %100
%100 = OpLabel
OpSelectionMerge %14 None
OpSwitch %9 %13 1 %10 2 %11 3 %12
%13 = OpLabel
OpStore %15 %19
OpBranch %14
%10 = OpLabel
OpStore %15 %16
OpBranch %11
%11 = OpLabel
OpBranch %14
%12 = OpLabel
OpStore %15 %18
OpBranch %13
%14 = OpLabel
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_split, context.get()));
}
TEST(TransformationSplitBlockTest, NoSplitDuringOpPhis) {
// The SPIR-V in this test came from the following fragment shader, with
// local store elimination applied to get some OpPhi instructions.
//
// void main() {
// int x;
// int i;
// for (i = 0; i < 100; i++) {
// x += i;
// }
// }
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %8 "i"
OpName %19 "x"
OpDecorate %8 RelaxedPrecision
OpDecorate %19 RelaxedPrecision
OpDecorate %22 RelaxedPrecision
OpDecorate %25 RelaxedPrecision
OpDecorate %26 RelaxedPrecision
OpDecorate %27 RelaxedPrecision
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%16 = OpConstant %6 100
%17 = OpTypeBool
%24 = OpConstant %6 1
%28 = OpUndef %6
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%19 = OpVariable %7 Function
OpStore %8 %9
OpBranch %10
%10 = OpLabel
%27 = OpPhi %6 %28 %5 %22 %13
%26 = OpPhi %6 %9 %5 %25 %13
OpBranch %50
%50 = OpLabel
OpLoopMerge %12 %13 None
OpBranch %14
%14 = OpLabel
%18 = OpSLessThan %17 %26 %16
OpBranchConditional %18 %11 %12
%11 = OpLabel
%22 = OpIAdd %6 %27 %26
OpStore %19 %22
OpBranch %13
%13 = OpLabel
%25 = OpIAdd %6 %26 %24
OpStore %8 %25
OpBranch %50
%12 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
FactManager fact_manager;
// We cannot split before OpPhi instructions, since the number of incoming
// blocks may not appropriately match after splitting.
ASSERT_FALSE(
TransformationSplitBlock(MakeInstructionDescriptor(26, SpvOpPhi, 0), 100)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationSplitBlock(MakeInstructionDescriptor(27, SpvOpPhi, 0), 100)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationSplitBlock(MakeInstructionDescriptor(27, SpvOpPhi, 1), 100)
.IsApplicable(context.get(), fact_manager));
}
TEST(TransformationSplitBlockTest, SplitOpPhiWithSinglePredecessor) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %8 "x"
OpName %10 "y"
OpDecorate %8 RelaxedPrecision
OpDecorate %10 RelaxedPrecision
OpDecorate %11 RelaxedPrecision
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 1
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
OpStore %8 %9
%11 = OpLoad %6 %8
OpBranch %20
%20 = OpLabel
%21 = OpPhi %6 %11 %5
OpStore %10 %21
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
FactManager fact_manager;
ASSERT_TRUE(
TransformationSplitBlock(MakeInstructionDescriptor(21, SpvOpPhi, 0), 100)
.IsApplicable(context.get(), fact_manager));
// An equivalent transformation to the above, just described with respect to a
// different base instruction.
auto split =
TransformationSplitBlock(MakeInstructionDescriptor(20, SpvOpPhi, 0), 100);
ASSERT_TRUE(split.IsApplicable(context.get(), fact_manager));
split.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
std::string after_split = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %8 "x"
OpName %10 "y"
OpDecorate %8 RelaxedPrecision
OpDecorate %10 RelaxedPrecision
OpDecorate %11 RelaxedPrecision
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 1
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
OpStore %8 %9
%11 = OpLoad %6 %8
OpBranch %20
%20 = OpLabel
OpBranch %100
%100 = OpLabel
%21 = OpPhi %6 %11 %20
OpStore %10 %21
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_split, context.get()));
}
} // namespace
} // namespace fuzz
} // namespace spvtools

View File

@@ -1,532 +0,0 @@
// Copyright (c) 2019 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 "source/fuzz/transformation_vector_shuffle.h"
#include "source/fuzz/instruction_descriptor.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
namespace fuzz {
namespace {
TEST(TransformationVectorShuffle, BasicTest) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeBool
%7 = OpTypeVector %6 2
%10 = OpConstantTrue %6
%11 = OpConstantFalse %6
%12 = OpConstantComposite %7 %10 %11
%112 = OpUndef %7
%13 = OpTypeVector %6 3
%16 = OpConstantComposite %13 %10 %11 %10
%17 = OpTypeVector %6 4
%20 = OpConstantComposite %17 %10 %11 %10 %11
%21 = OpTypeInt 32 1
%22 = OpTypeVector %21 2
%25 = OpConstant %21 1
%26 = OpConstant %21 0
%27 = OpConstantComposite %22 %25 %26
%28 = OpTypeVector %21 3
%31 = OpConstantComposite %28 %25 %26 %25
%32 = OpTypeVector %21 4
%33 = OpTypePointer Function %32
%35 = OpConstantComposite %32 %25 %26 %25 %26
%36 = OpTypeInt 32 0
%37 = OpTypeVector %36 2
%40 = OpConstant %36 1
%41 = OpConstant %36 0
%42 = OpConstantComposite %37 %40 %41
%43 = OpTypeVector %36 3
%46 = OpConstantComposite %43 %40 %41 %40
%47 = OpTypeVector %36 4
%50 = OpConstantComposite %47 %40 %41 %40 %41
%51 = OpTypeFloat 32
%55 = OpConstant %51 1
%56 = OpConstant %51 0
%58 = OpTypeVector %51 3
%61 = OpConstantComposite %58 %55 %56 %55
%62 = OpTypeVector %51 4
%65 = OpConstantComposite %62 %55 %56 %55 %56
%4 = OpFunction %2 None %3
%5 = OpLabel
OpSelectionMerge %100 None
OpBranchConditional %10 %101 %102
%101 = OpLabel
%103 = OpCompositeConstruct %62 %55 %55 %55 %56
OpBranch %100
%102 = OpLabel
OpBranch %100
%100 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
fact_manager.AddFactDataSynonym(MakeDataDescriptor(10, {}),
MakeDataDescriptor(12, {0}), context.get());
fact_manager.AddFactDataSynonym(MakeDataDescriptor(11, {}),
MakeDataDescriptor(12, {1}), context.get());
fact_manager.AddFactDataSynonym(MakeDataDescriptor(10, {}),
MakeDataDescriptor(16, {0}), context.get());
fact_manager.AddFactDataSynonym(MakeDataDescriptor(11, {}),
MakeDataDescriptor(16, {1}), context.get());
fact_manager.AddFactDataSynonym(MakeDataDescriptor(10, {}),
MakeDataDescriptor(16, {2}), context.get());
fact_manager.AddFactDataSynonym(MakeDataDescriptor(10, {}),
MakeDataDescriptor(20, {0}), context.get());
fact_manager.AddFactDataSynonym(MakeDataDescriptor(11, {}),
MakeDataDescriptor(20, {1}), context.get());
fact_manager.AddFactDataSynonym(MakeDataDescriptor(10, {}),
MakeDataDescriptor(20, {2}), context.get());
fact_manager.AddFactDataSynonym(MakeDataDescriptor(11, {}),
MakeDataDescriptor(20, {3}), context.get());
fact_manager.AddFactDataSynonym(MakeDataDescriptor(25, {}),
MakeDataDescriptor(27, {0}), context.get());
fact_manager.AddFactDataSynonym(MakeDataDescriptor(26, {}),
MakeDataDescriptor(27, {1}), context.get());
fact_manager.AddFactDataSynonym(MakeDataDescriptor(25, {}),
MakeDataDescriptor(31, {0}), context.get());
fact_manager.AddFactDataSynonym(MakeDataDescriptor(26, {}),
MakeDataDescriptor(31, {1}), context.get());
fact_manager.AddFactDataSynonym(MakeDataDescriptor(25, {}),
MakeDataDescriptor(31, {2}), context.get());
fact_manager.AddFactDataSynonym(MakeDataDescriptor(25, {}),
MakeDataDescriptor(35, {0}), context.get());
fact_manager.AddFactDataSynonym(MakeDataDescriptor(26, {}),
MakeDataDescriptor(35, {1}), context.get());
fact_manager.AddFactDataSynonym(MakeDataDescriptor(25, {}),
MakeDataDescriptor(35, {2}), context.get());
fact_manager.AddFactDataSynonym(MakeDataDescriptor(26, {}),
MakeDataDescriptor(35, {3}), context.get());
fact_manager.AddFactDataSynonym(MakeDataDescriptor(40, {}),
MakeDataDescriptor(42, {0}), context.get());
fact_manager.AddFactDataSynonym(MakeDataDescriptor(41, {}),
MakeDataDescriptor(42, {1}), context.get());
fact_manager.AddFactDataSynonym(MakeDataDescriptor(40, {}),
MakeDataDescriptor(46, {0}), context.get());
fact_manager.AddFactDataSynonym(MakeDataDescriptor(41, {}),
MakeDataDescriptor(46, {1}), context.get());
fact_manager.AddFactDataSynonym(MakeDataDescriptor(40, {}),
MakeDataDescriptor(46, {2}), context.get());
fact_manager.AddFactDataSynonym(MakeDataDescriptor(40, {}),
MakeDataDescriptor(50, {0}), context.get());
fact_manager.AddFactDataSynonym(MakeDataDescriptor(41, {}),
MakeDataDescriptor(50, {1}), context.get());
fact_manager.AddFactDataSynonym(MakeDataDescriptor(40, {}),
MakeDataDescriptor(50, {2}), context.get());
fact_manager.AddFactDataSynonym(MakeDataDescriptor(41, {}),
MakeDataDescriptor(50, {3}), context.get());
fact_manager.AddFactDataSynonym(MakeDataDescriptor(55, {}),
MakeDataDescriptor(61, {0}), context.get());
fact_manager.AddFactDataSynonym(MakeDataDescriptor(56, {}),
MakeDataDescriptor(61, {1}), context.get());
fact_manager.AddFactDataSynonym(MakeDataDescriptor(55, {}),
MakeDataDescriptor(61, {2}), context.get());
fact_manager.AddFactDataSynonym(MakeDataDescriptor(55, {}),
MakeDataDescriptor(65, {0}), context.get());
fact_manager.AddFactDataSynonym(MakeDataDescriptor(56, {}),
MakeDataDescriptor(65, {1}), context.get());
fact_manager.AddFactDataSynonym(MakeDataDescriptor(55, {}),
MakeDataDescriptor(65, {2}), context.get());
fact_manager.AddFactDataSynonym(MakeDataDescriptor(56, {}),
MakeDataDescriptor(65, {3}), context.get());
// %103 does not dominate the return instruction.
ASSERT_FALSE(TransformationVectorShuffle(
MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 103, 65,
{3, 5, 7})
.IsApplicable(context.get(), fact_manager));
// Illegal to shuffle a bvec2 and a vec3
ASSERT_FALSE(TransformationVectorShuffle(
MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 112, 61,
{0, 2, 4})
.IsApplicable(context.get(), fact_manager));
// Illegal to shuffle an ivec2 and a uvec4
ASSERT_FALSE(TransformationVectorShuffle(
MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 27, 50,
{1, 3, 5})
.IsApplicable(context.get(), fact_manager));
// Vector 1 does not exist
ASSERT_FALSE(TransformationVectorShuffle(
MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 300, 50,
{1, 3, 5})
.IsApplicable(context.get(), fact_manager));
// Vector 2 does not exist
ASSERT_FALSE(TransformationVectorShuffle(
MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 27, 300,
{1, 3, 5})
.IsApplicable(context.get(), fact_manager));
// Index out of range
ASSERT_FALSE(
TransformationVectorShuffle(
MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 12, 112, {0, 20})
.IsApplicable(context.get(), fact_manager));
// Too many indices
ASSERT_FALSE(TransformationVectorShuffle(
MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 12, 112,
{0, 1, 0, 1, 0, 1, 0, 1})
.IsApplicable(context.get(), fact_manager));
// Too few indices
ASSERT_FALSE(
TransformationVectorShuffle(
MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 12, 112, {})
.IsApplicable(context.get(), fact_manager));
// Too few indices again
ASSERT_FALSE(
TransformationVectorShuffle(
MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 12, 112, {0})
.IsApplicable(context.get(), fact_manager));
// Indices define unknown type: we do not have vec2
ASSERT_FALSE(
TransformationVectorShuffle(
MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 65, 65, {0, 1})
.IsApplicable(context.get(), fact_manager));
// The instruction to insert before does not exist
ASSERT_FALSE(TransformationVectorShuffle(
MakeInstructionDescriptor(100, SpvOpCompositeConstruct, 1),
201, 20, 12, {0xFFFFFFFF, 3, 5})
.IsApplicable(context.get(), fact_manager));
// The 'fresh' id is already in use
ASSERT_FALSE(
TransformationVectorShuffle(
MakeInstructionDescriptor(100, SpvOpReturn, 0), 12, 12, 112, {})
.IsApplicable(context.get(), fact_manager));
protobufs::DataDescriptor temp_dd;
TransformationVectorShuffle transformation1(
MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 12, 112, {1, 0});
ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
transformation1.Apply(context.get(), &fact_manager);
temp_dd = MakeDataDescriptor(200, {0});
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(11, {}), temp_dd,
context.get()));
temp_dd = MakeDataDescriptor(200, {1});
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(10, {}), temp_dd,
context.get()));
TransformationVectorShuffle transformation2(
MakeInstructionDescriptor(100, SpvOpReturn, 0), 201, 20, 12,
{0xFFFFFFFF, 3, 5});
ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
transformation2.Apply(context.get(), &fact_manager);
temp_dd = MakeDataDescriptor(201, {1});
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(11, {}), temp_dd,
context.get()));
temp_dd = MakeDataDescriptor(201, {2});
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(11, {}), temp_dd,
context.get()));
TransformationVectorShuffle transformation3(
MakeInstructionDescriptor(100, SpvOpReturn, 0), 202, 27, 35, {5, 4, 1});
ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
transformation3.Apply(context.get(), &fact_manager);
temp_dd = MakeDataDescriptor(202, {0});
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(26, {}), temp_dd,
context.get()));
temp_dd = MakeDataDescriptor(202, {1});
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(25, {}), temp_dd,
context.get()));
temp_dd = MakeDataDescriptor(202, {2});
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(26, {}), temp_dd,
context.get()));
TransformationVectorShuffle transformation4(
MakeInstructionDescriptor(100, SpvOpReturn, 0), 203, 42, 46, {0, 1});
ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager));
transformation4.Apply(context.get(), &fact_manager);
temp_dd = MakeDataDescriptor(203, {0});
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {}), temp_dd,
context.get()));
temp_dd = MakeDataDescriptor(203, {1});
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(41, {}), temp_dd,
context.get()));
TransformationVectorShuffle transformation5(
MakeInstructionDescriptor(100, SpvOpReturn, 0), 204, 42, 46, {2, 3, 4});
ASSERT_TRUE(transformation5.IsApplicable(context.get(), fact_manager));
transformation5.Apply(context.get(), &fact_manager);
temp_dd = MakeDataDescriptor(204, {0});
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {}), temp_dd,
context.get()));
temp_dd = MakeDataDescriptor(204, {1});
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(41, {}), temp_dd,
context.get()));
temp_dd = MakeDataDescriptor(204, {2});
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {}), temp_dd,
context.get()));
TransformationVectorShuffle transformation6(
MakeInstructionDescriptor(100, SpvOpReturn, 0), 205, 42, 42,
{0, 1, 2, 3});
ASSERT_TRUE(transformation6.IsApplicable(context.get(), fact_manager));
transformation6.Apply(context.get(), &fact_manager);
temp_dd = MakeDataDescriptor(205, {0});
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {}), temp_dd,
context.get()));
temp_dd = MakeDataDescriptor(205, {1});
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(41, {}), temp_dd,
context.get()));
temp_dd = MakeDataDescriptor(205, {2});
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {}), temp_dd,
context.get()));
temp_dd = MakeDataDescriptor(205, {3});
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(41, {}), temp_dd,
context.get()));
// swizzle vec4 from vec4 and vec4 using some undefs
TransformationVectorShuffle transformation7(
MakeInstructionDescriptor(100, SpvOpReturn, 0), 206, 65, 65,
{0xFFFFFFFF, 3, 6, 0xFFFFFFFF});
ASSERT_TRUE(transformation7.IsApplicable(context.get(), fact_manager));
transformation7.Apply(context.get(), &fact_manager);
temp_dd = MakeDataDescriptor(206, {1});
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(56, {}), temp_dd,
context.get()));
std::string after_transformation = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeBool
%7 = OpTypeVector %6 2
%10 = OpConstantTrue %6
%11 = OpConstantFalse %6
%12 = OpConstantComposite %7 %10 %11
%112 = OpUndef %7
%13 = OpTypeVector %6 3
%16 = OpConstantComposite %13 %10 %11 %10
%17 = OpTypeVector %6 4
%20 = OpConstantComposite %17 %10 %11 %10 %11
%21 = OpTypeInt 32 1
%22 = OpTypeVector %21 2
%25 = OpConstant %21 1
%26 = OpConstant %21 0
%27 = OpConstantComposite %22 %25 %26
%28 = OpTypeVector %21 3
%31 = OpConstantComposite %28 %25 %26 %25
%32 = OpTypeVector %21 4
%33 = OpTypePointer Function %32
%35 = OpConstantComposite %32 %25 %26 %25 %26
%36 = OpTypeInt 32 0
%37 = OpTypeVector %36 2
%40 = OpConstant %36 1
%41 = OpConstant %36 0
%42 = OpConstantComposite %37 %40 %41
%43 = OpTypeVector %36 3
%46 = OpConstantComposite %43 %40 %41 %40
%47 = OpTypeVector %36 4
%50 = OpConstantComposite %47 %40 %41 %40 %41
%51 = OpTypeFloat 32
%55 = OpConstant %51 1
%56 = OpConstant %51 0
%58 = OpTypeVector %51 3
%61 = OpConstantComposite %58 %55 %56 %55
%62 = OpTypeVector %51 4
%65 = OpConstantComposite %62 %55 %56 %55 %56
%4 = OpFunction %2 None %3
%5 = OpLabel
OpSelectionMerge %100 None
OpBranchConditional %10 %101 %102
%101 = OpLabel
%103 = OpCompositeConstruct %62 %55 %55 %55 %56
OpBranch %100
%102 = OpLabel
OpBranch %100
%100 = OpLabel
%200 = OpVectorShuffle %7 %12 %112 1 0
%201 = OpVectorShuffle %13 %20 %12 0xFFFFFFFF 3 5
%202 = OpVectorShuffle %28 %27 %35 5 4 1
%203 = OpVectorShuffle %37 %42 %46 0 1
%204 = OpVectorShuffle %43 %42 %46 2 3 4
%205 = OpVectorShuffle %47 %42 %42 0 1 2 3
%206 = OpVectorShuffle %62 %65 %65 0xFFFFFFFF 3 6 0xFFFFFFFF
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
TEST(TransformationVectorShuffleTest, IllegalInsertionPoints) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %51 %27
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %25 "buf"
OpMemberName %25 0 "value"
OpName %27 ""
OpName %51 "color"
OpMemberDecorate %25 0 Offset 0
OpDecorate %25 Block
OpDecorate %27 DescriptorSet 0
OpDecorate %27 Binding 0
OpDecorate %51 Location 0
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypeVector %6 4
%150 = OpTypeVector %6 2
%10 = OpConstant %6 0.300000012
%11 = OpConstant %6 0.400000006
%12 = OpConstant %6 0.5
%13 = OpConstant %6 1
%14 = OpConstantComposite %7 %10 %11 %12 %13
%15 = OpTypeInt 32 1
%18 = OpConstant %15 0
%25 = OpTypeStruct %6
%26 = OpTypePointer Uniform %25
%27 = OpVariable %26 Uniform
%28 = OpTypePointer Uniform %6
%32 = OpTypeBool
%103 = OpConstantTrue %32
%34 = OpConstant %6 0.100000001
%48 = OpConstant %15 1
%50 = OpTypePointer Output %7
%51 = OpVariable %50 Output
%100 = OpTypePointer Function %6
%4 = OpFunction %2 None %3
%5 = OpLabel
%101 = OpVariable %100 Function
%102 = OpVariable %100 Function
OpBranch %19
%19 = OpLabel
%60 = OpPhi %7 %14 %5 %58 %20
%59 = OpPhi %15 %18 %5 %49 %20
%29 = OpAccessChain %28 %27 %18
%30 = OpLoad %6 %29
%31 = OpConvertFToS %15 %30
%33 = OpSLessThan %32 %59 %31
OpLoopMerge %21 %20 None
OpBranchConditional %33 %20 %21
%20 = OpLabel
%39 = OpCompositeExtract %6 %60 0
%40 = OpFAdd %6 %39 %34
%55 = OpCompositeInsert %7 %40 %60 0
%44 = OpCompositeExtract %6 %60 1
%45 = OpFSub %6 %44 %34
%58 = OpCompositeInsert %7 %45 %55 1
%49 = OpIAdd %15 %59 %48
OpBranch %19
%21 = OpLabel
OpStore %51 %60
OpSelectionMerge %105 None
OpBranchConditional %103 %104 %105
%104 = OpLabel
OpBranch %105
%105 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
// Cannot insert before the OpVariables of a function.
ASSERT_FALSE(
TransformationVectorShuffle(
MakeInstructionDescriptor(101, SpvOpVariable, 0), 200, 14, 14, {0, 1})
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationVectorShuffle(
MakeInstructionDescriptor(101, SpvOpVariable, 1), 200, 14, 14, {1, 2})
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationVectorShuffle(
MakeInstructionDescriptor(102, SpvOpVariable, 0), 200, 14, 14, {1, 2})
.IsApplicable(context.get(), fact_manager));
// OK to insert right after the OpVariables.
ASSERT_FALSE(
TransformationVectorShuffle(
MakeInstructionDescriptor(102, SpvOpBranch, 1), 200, 14, 14, {1, 1})
.IsApplicable(context.get(), fact_manager));
// Cannot insert before the OpPhis of a block.
ASSERT_FALSE(
TransformationVectorShuffle(MakeInstructionDescriptor(60, SpvOpPhi, 0),
200, 14, 14, {2, 0})
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
TransformationVectorShuffle(MakeInstructionDescriptor(59, SpvOpPhi, 0),
200, 14, 14, {3, 0})
.IsApplicable(context.get(), fact_manager));
// OK to insert after the OpPhis.
ASSERT_TRUE(TransformationVectorShuffle(
MakeInstructionDescriptor(59, SpvOpAccessChain, 0), 200, 14,
14, {3, 4})
.IsApplicable(context.get(), fact_manager));
// Cannot insert before OpLoopMerge
ASSERT_FALSE(TransformationVectorShuffle(
MakeInstructionDescriptor(33, SpvOpBranchConditional, 0),
200, 14, 14, {3})
.IsApplicable(context.get(), fact_manager));
// Cannot insert before OpSelectionMerge
ASSERT_FALSE(TransformationVectorShuffle(
MakeInstructionDescriptor(21, SpvOpBranchConditional, 0),
200, 14, 14, {2})
.IsApplicable(context.get(), fact_manager));
}
} // namespace
} // namespace fuzz
} // namespace spvtools

View File

@@ -1,84 +0,0 @@
// Copyright (c) 2019 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 "source/fuzz/uniform_buffer_element_descriptor.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
namespace fuzz {
namespace {
TEST(UniformBufferElementDescriptorTest, TestEquality) {
// Test that equality works as expected for various buffer element
// descriptors.
protobufs::UniformBufferElementDescriptor descriptor1 =
MakeUniformBufferElementDescriptor(0, 0, {1, 2, 3});
protobufs::UniformBufferElementDescriptor descriptor2 =
MakeUniformBufferElementDescriptor(0, 0, {1, 2, 3});
protobufs::UniformBufferElementDescriptor descriptor3 =
MakeUniformBufferElementDescriptor(0, 1, {1, 2, 3});
protobufs::UniformBufferElementDescriptor descriptor4 =
MakeUniformBufferElementDescriptor(1, 0, {1, 2, 3});
protobufs::UniformBufferElementDescriptor descriptor5 =
MakeUniformBufferElementDescriptor(1, 1, {1, 2, 3});
protobufs::UniformBufferElementDescriptor descriptor6 =
MakeUniformBufferElementDescriptor(0, 0, {1, 2, 4});
protobufs::UniformBufferElementDescriptor descriptor7 =
MakeUniformBufferElementDescriptor(0, 0, {1, 2});
ASSERT_TRUE(
UniformBufferElementDescriptorEquals()(&descriptor1, &descriptor1));
ASSERT_TRUE(
UniformBufferElementDescriptorEquals()(&descriptor1, &descriptor2));
ASSERT_TRUE(
UniformBufferElementDescriptorEquals()(&descriptor3, &descriptor3));
ASSERT_TRUE(
UniformBufferElementDescriptorEquals()(&descriptor4, &descriptor4));
ASSERT_TRUE(
UniformBufferElementDescriptorEquals()(&descriptor5, &descriptor5));
ASSERT_TRUE(
UniformBufferElementDescriptorEquals()(&descriptor6, &descriptor6));
ASSERT_TRUE(
UniformBufferElementDescriptorEquals()(&descriptor7, &descriptor7));
ASSERT_FALSE(
UniformBufferElementDescriptorEquals()(&descriptor1, &descriptor3));
ASSERT_FALSE(
UniformBufferElementDescriptorEquals()(&descriptor3, &descriptor1));
ASSERT_FALSE(
UniformBufferElementDescriptorEquals()(&descriptor1, &descriptor4));
ASSERT_FALSE(
UniformBufferElementDescriptorEquals()(&descriptor4, &descriptor1));
ASSERT_FALSE(
UniformBufferElementDescriptorEquals()(&descriptor1, &descriptor5));
ASSERT_FALSE(
UniformBufferElementDescriptorEquals()(&descriptor5, &descriptor1));
ASSERT_FALSE(
UniformBufferElementDescriptorEquals()(&descriptor1, &descriptor6));
ASSERT_FALSE(
UniformBufferElementDescriptorEquals()(&descriptor6, &descriptor1));
ASSERT_FALSE(
UniformBufferElementDescriptorEquals()(&descriptor1, &descriptor7));
ASSERT_FALSE(
UniformBufferElementDescriptorEquals()(&descriptor7, &descriptor1));
}
} // namespace
} // namespace fuzz
} // namespace spvtools

View File

@@ -1,215 +0,0 @@
# Copyright 2018 Google Inc. All rights reserved.
#
# 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.
import("//testing/libfuzzer/fuzzer_test.gni")
import("//testing/test.gni")
config("fuzzer_config") {
configs = [ "../..:spvtools_internal_config" ]
}
group("fuzzers") {
testonly = true
deps = []
if (!build_with_chromium || use_fuzzing_engine) {
deps += [ ":fuzzers_bin" ]
}
}
if (!build_with_chromium || use_fuzzing_engine) {
group("fuzzers_bin") {
testonly = true
deps = [
":spvtools_as_fuzzer",
":spvtools_binary_parser_fuzzer",
":spvtools_dis_fuzzer",
":spvtools_opt_legalization_fuzzer",
":spvtools_opt_performance_fuzzer",
":spvtools_opt_size_fuzzer",
":spvtools_opt_webgputovulkan_fuzzer",
":spvtools_opt_vulkantowebgpu_fuzzer",
":spvtools_val_fuzzer",
":spvtools_val_webgpu_fuzzer",
]
}
}
template("spvtools_fuzzer") {
source_set(target_name) {
testonly = true
sources = invoker.sources
deps = [
"../..:spvtools",
"../..:spvtools_opt",
"../..:spvtools_val",
]
if (defined(invoker.deps)) {
deps += invoker.deps
}
configs -= [ "//build/config/compiler:chromium_code" ]
configs += [
"//build/config/compiler:no_chromium_code",
":fuzzer_config",
]
}
}
spvtools_fuzzer("spvtools_as_fuzzer_src") {
sources = [
"spvtools_as_fuzzer.cpp",
]
}
spvtools_fuzzer("spvtools_binary_parser_fuzzer_src") {
sources = [
"spvtools_binary_parser_fuzzer.cpp",
]
}
spvtools_fuzzer("spvtools_dis_fuzzer_src") {
sources = [
"spvtools_dis_fuzzer.cpp",
]
}
spvtools_fuzzer("spvtools_opt_performance_fuzzer_src") {
sources = [
"spvtools_opt_performance_fuzzer.cpp",
]
}
spvtools_fuzzer("spvtools_opt_legalization_fuzzer_src") {
sources = [
"spvtools_opt_legalization_fuzzer.cpp",
]
}
spvtools_fuzzer("spvtools_opt_size_fuzzer_src") {
sources = [
"spvtools_opt_size_fuzzer.cpp",
]
}
spvtools_fuzzer("spvtools_opt_webgputovulkan_fuzzer_src") {
sources = [
"spvtools_opt_webgputovulkan_fuzzer.cpp",
]
}
spvtools_fuzzer("spvtools_opt_vulkantowebgpu_fuzzer_src") {
sources = [
"spvtools_opt_vulkantowebgpu_fuzzer.cpp",
]
}
spvtools_fuzzer("spvtools_val_fuzzer_src") {
sources = [
"spvtools_val_fuzzer.cpp",
]
}
spvtools_fuzzer("spvtools_val_webgpu_fuzzer_src") {
sources = [
"spvtools_val_webgpu_fuzzer.cpp",
]
}
if (!build_with_chromium || use_fuzzing_engine) {
fuzzer_test("spvtools_as_fuzzer") {
sources = []
deps = [
":spvtools_as_fuzzer_src",
]
# Intentionally doesn't use the seed corpus, because it consumes
# part of the input as not part of the file.
}
fuzzer_test("spvtools_binary_parser_fuzzer") {
sources = []
deps = [
":spvtools_binary_parser_fuzzer_src",
]
# Intentionally doesn't use the seed corpus, because it consumes
# part of the input as not part of the file.
}
fuzzer_test("spvtools_dis_fuzzer") {
sources = []
deps = [
":spvtools_dis_fuzzer_src",
]
# Intentionally doesn't use the seed corpus, because it consumes
# part of the input as not part of the file.
}
fuzzer_test("spvtools_opt_performance_fuzzer") {
sources = []
deps = [
":spvtools_opt_performance_fuzzer_src",
]
seed_corpus = "corpora/spv"
}
fuzzer_test("spvtools_opt_legalization_fuzzer") {
sources = []
deps = [
":spvtools_opt_legalization_fuzzer_src",
]
seed_corpus = "corpora/spv"
}
fuzzer_test("spvtools_opt_size_fuzzer") {
sources = []
deps = [
":spvtools_opt_size_fuzzer_src",
]
seed_corpus = "corpora/spv"
}
fuzzer_test("spvtools_opt_webgputovulkan_fuzzer") {
sources = []
deps = [
":spvtools_opt_webgputovulkan_fuzzer_src",
]
seed_corpus = "corpora/spv"
}
fuzzer_test("spvtools_opt_vulkantowebgpu_fuzzer") {
sources = []
deps = [
":spvtools_opt_vulkantowebgpu_fuzzer_src",
]
seed_corpus = "corpora/spv"
}
fuzzer_test("spvtools_val_fuzzer") {
sources = []
deps = [
":spvtools_val_fuzzer_src",
]
seed_corpus = "corpora/spv"
}
fuzzer_test("spvtools_val_webgpu_fuzzer") {
sources = []
deps = [
":spvtools_val_webgpu_fuzzer_src",
]
seed_corpus = "corpora/spv"
}
}

View File

@@ -1,72 +0,0 @@
// Copyright (c) 2019 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 <cstdint>
#include <cstring> // memcpy
#include <vector>
#include "source/spirv_target_env.h"
#include "spirv-tools/libspirv.hpp"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
if (size < sizeof(spv_target_env) + 1) return 0;
const spv_context context =
spvContextCreate(*reinterpret_cast<const spv_target_env*>(data));
if (context == nullptr) return 0;
data += sizeof(spv_target_env);
size -= sizeof(spv_target_env);
std::vector<uint32_t> input;
std::vector<char> input_str;
size_t char_count = input.size() * sizeof(uint32_t) / sizeof(char);
input_str.resize(char_count);
memcpy(input_str.data(), input.data(), input.size() * sizeof(uint32_t));
spv_binary binary = nullptr;
spv_diagnostic diagnostic = nullptr;
spvTextToBinaryWithOptions(context, input_str.data(), input_str.size(),
SPV_TEXT_TO_BINARY_OPTION_NONE, &binary,
&diagnostic);
if (diagnostic) {
spvDiagnosticPrint(diagnostic);
spvDiagnosticDestroy(diagnostic);
diagnostic = nullptr;
}
if (binary) {
spvBinaryDestroy(binary);
binary = nullptr;
}
spvTextToBinaryWithOptions(context, input_str.data(), input_str.size(),
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS,
&binary, &diagnostic);
if (diagnostic) {
spvDiagnosticPrint(diagnostic);
spvDiagnosticDestroy(diagnostic);
diagnostic = nullptr;
}
if (binary) {
spvBinaryDestroy(binary);
binary = nullptr;
}
spvContextDestroy(context);
return 0;
}

View File

@@ -1,44 +0,0 @@
// Copyright (c) 2018 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 <cstdint>
#include <vector>
#include "spirv-tools/libspirv.hpp"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
if (size < sizeof(spv_target_env) + 1) return 0;
const spv_context context =
spvContextCreate(*reinterpret_cast<const spv_target_env*>(data));
if (context == nullptr) return 0;
data += sizeof(spv_target_env);
size -= sizeof(spv_target_env);
std::vector<uint32_t> input;
input.resize(size >> 2);
size_t count = 0;
for (size_t i = 0; (i + 3) < size; i += 4) {
input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) |
(data[i + 3]) << 24;
}
spvBinaryParse(context, nullptr, input.data(), input.size(), nullptr, nullptr,
nullptr);
spvContextDestroy(context);
return 0;
}

View File

@@ -1,71 +0,0 @@
// Copyright (c) 2019 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 <cstdint>
#include <cstring> // memcpy
#include <vector>
#include "source/spirv_target_env.h"
#include "spirv-tools/libspirv.hpp"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
if (size < sizeof(spv_target_env) + 1) return 0;
const spv_context context =
spvContextCreate(*reinterpret_cast<const spv_target_env*>(data));
if (context == nullptr) return 0;
data += sizeof(spv_target_env);
size -= sizeof(spv_target_env);
std::vector<uint32_t> input;
input.resize(size >> 2);
size_t count = 0;
for (size_t i = 0; (i + 3) < size; i += 4) {
input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) |
(data[i + 3]) << 24;
}
std::vector<char> input_str;
size_t char_count = input.size() * sizeof(uint32_t) / sizeof(char);
input_str.resize(char_count);
memcpy(input_str.data(), input.data(), input.size() * sizeof(uint32_t));
spv_text text = nullptr;
spv_diagnostic diagnostic = nullptr;
for (uint32_t options = SPV_BINARY_TO_TEXT_OPTION_NONE;
options <
(SPV_BINARY_TO_TEXT_OPTION_PRINT | SPV_BINARY_TO_TEXT_OPTION_COLOR |
SPV_BINARY_TO_TEXT_OPTION_INDENT |
SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET |
SPV_BINARY_TO_TEXT_OPTION_NO_HEADER |
SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
options++) {
spvBinaryToText(context, input.data(), input.size(), options, &text,
&diagnostic);
if (diagnostic) {
spvDiagnosticDestroy(diagnostic);
diagnostic = nullptr;
}
if (text) {
spvTextDestroy(text);
text = nullptr;
}
}
spvContextDestroy(context);
return 0;
}

View File

@@ -1,38 +0,0 @@
// Copyright (c) 2018 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 <cstdint>
#include <vector>
#include "spirv-tools/optimizer.hpp"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
spvtools::Optimizer optimizer(SPV_ENV_UNIVERSAL_1_3);
optimizer.SetMessageConsumer([](spv_message_level_t, const char*,
const spv_position_t&, const char*) {});
std::vector<uint32_t> input;
input.resize(size >> 2);
size_t count = 0;
for (size_t i = 0; (i + 3) < size; i += 4) {
input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) |
(data[i + 3]) << 24;
}
optimizer.RegisterLegalizationPasses();
optimizer.Run(input.data(), input.size(), &input);
return 0;
}

View File

@@ -1,38 +0,0 @@
// Copyright (c) 2018 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 <cstdint>
#include <vector>
#include "spirv-tools/optimizer.hpp"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
spvtools::Optimizer optimizer(SPV_ENV_UNIVERSAL_1_3);
optimizer.SetMessageConsumer([](spv_message_level_t, const char*,
const spv_position_t&, const char*) {});
std::vector<uint32_t> input;
input.resize(size >> 2);
size_t count = 0;
for (size_t i = 0; (i + 3) < size; i += 4) {
input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) |
(data[i + 3]) << 24;
}
optimizer.RegisterPerformancePasses();
optimizer.Run(input.data(), input.size(), &input);
return 0;
}

View File

@@ -1,38 +0,0 @@
// Copyright (c) 2018 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 <cstdint>
#include <vector>
#include "spirv-tools/optimizer.hpp"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
spvtools::Optimizer optimizer(SPV_ENV_UNIVERSAL_1_3);
optimizer.SetMessageConsumer([](spv_message_level_t, const char*,
const spv_position_t&, const char*) {});
std::vector<uint32_t> input;
input.resize(size >> 2);
size_t count = 0;
for (size_t i = 0; (i + 3) < size; i += 4) {
input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) |
(data[i + 3]) << 24;
}
optimizer.RegisterSizePasses();
optimizer.Run(input.data(), input.size(), &input);
return 0;
}

View File

@@ -1,38 +0,0 @@
// Copyright (c) 2019 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 <cstdint>
#include <vector>
#include "spirv-tools/optimizer.hpp"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
spvtools::Optimizer optimizer(SPV_ENV_VULKAN_1_1);
optimizer.SetMessageConsumer([](spv_message_level_t, const char*,
const spv_position_t&, const char*) {});
std::vector<uint32_t> input;
input.resize(size >> 2);
size_t count = 0;
for (size_t i = 0; (i + 3) < size; i += 4) {
input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) |
(data[i + 3]) << 24;
}
optimizer.RegisterVulkanToWebGPUPasses();
optimizer.Run(input.data(), input.size(), &input);
return 0;
}

View File

@@ -1,38 +0,0 @@
// Copyright (c) 2019 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 <cstdint>
#include <vector>
#include "spirv-tools/optimizer.hpp"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
spvtools::Optimizer optimizer(SPV_ENV_WEBGPU_0);
optimizer.SetMessageConsumer([](spv_message_level_t, const char*,
const spv_position_t&, const char*) {});
std::vector<uint32_t> input;
input.resize(size >> 2);
size_t count = 0;
for (size_t i = 0; (i + 3) < size; i += 4) {
input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) |
(data[i + 3]) << 24;
}
optimizer.RegisterWebGPUToVulkanPasses();
optimizer.Run(input.data(), input.size(), &input);
return 0;
}

View File

@@ -1,36 +0,0 @@
// Copyright (c) 2018 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 <cstdint>
#include <vector>
#include "spirv-tools/libspirv.hpp"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_3);
tools.SetMessageConsumer([](spv_message_level_t, const char*,
const spv_position_t&, const char*) {});
std::vector<uint32_t> input;
input.resize(size >> 2);
size_t count = 0;
for (size_t i = 0; (i + 3) < size; i += 4) {
input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) |
(data[i + 3]) << 24;
}
tools.Validate(input);
return 0;
}

View File

@@ -1,36 +0,0 @@
// Copyright (c) 2019 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 <cstdint>
#include <vector>
#include "spirv-tools/libspirv.hpp"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
spvtools::SpirvTools tools(SPV_ENV_WEBGPU_0);
tools.SetMessageConsumer([](spv_message_level_t, const char*,
const spv_position_t&, const char*) {});
std::vector<uint32_t> input;
input.resize(size >> 2);
size_t count = 0;
for (size_t i = 0; (i + 3) < size; i += 4) {
input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) |
(data[i + 3]) << 24;
}
tools.Validate(input);
return 0;
}

View File

@@ -1,62 +0,0 @@
// Copyright (c) 2015-2016 The Khronos Group 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 <limits>
#include <string>
#include <utility>
#include <vector>
#include "gmock/gmock.h"
#include "source/opcode.h"
#include "test/unit_spirv.h"
namespace spvtools {
namespace {
using ::spvtest::EnumCase;
using ::testing::Eq;
using GeneratorMagicNumberTest =
::testing::TestWithParam<EnumCase<spv_generator_t>>;
TEST_P(GeneratorMagicNumberTest, Single) {
EXPECT_THAT(std::string(spvGeneratorStr(GetParam().value())),
GetParam().name());
}
INSTANTIATE_TEST_SUITE_P(
Registered, GeneratorMagicNumberTest,
::testing::ValuesIn(std::vector<EnumCase<spv_generator_t>>{
{SPV_GENERATOR_KHRONOS, "Khronos"},
{SPV_GENERATOR_LUNARG, "LunarG"},
{SPV_GENERATOR_VALVE, "Valve"},
{SPV_GENERATOR_CODEPLAY, "Codeplay"},
{SPV_GENERATOR_NVIDIA, "NVIDIA"},
{SPV_GENERATOR_ARM, "ARM"},
{SPV_GENERATOR_KHRONOS_LLVM_TRANSLATOR,
"Khronos LLVM/SPIR-V Translator"},
{SPV_GENERATOR_KHRONOS_ASSEMBLER, "Khronos SPIR-V Tools Assembler"},
{SPV_GENERATOR_KHRONOS_GLSLANG, "Khronos Glslang Reference Front End"},
}));
INSTANTIATE_TEST_SUITE_P(
Unregistered, GeneratorMagicNumberTest,
::testing::ValuesIn(std::vector<EnumCase<spv_generator_t>>{
// We read registered entries from the SPIR-V XML Registry file
// which can change over time.
{spv_generator_t(1000), "Unknown"},
{spv_generator_t(9999), "Unknown"},
}));
} // namespace
} // namespace spvtools

File diff suppressed because it is too large Load Diff

View File

@@ -1,291 +0,0 @@
// Copyright (c) 2015-2016 The Khronos Group 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 <cassert>
#include <string>
#include <vector>
#include "gmock/gmock.h"
#include "source/util/bitutils.h"
#include "test/test_fixture.h"
namespace spvtools {
namespace utils {
namespace {
using spvtest::Concatenate;
using spvtest::MakeInstruction;
using spvtest::ScopedContext;
using spvtest::TextToBinaryTest;
using ::testing::ElementsAre;
using ::testing::Eq;
using ::testing::HasSubstr;
using ::testing::StrEq;
TEST_F(TextToBinaryTest, ImmediateIntOpCode) {
SetText("!0x00FF00FF");
ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(ScopedContext().context, text.str,
text.length, &binary, &diagnostic));
EXPECT_EQ(0x00FF00FFu, binary->code[5]);
if (diagnostic) {
spvDiagnosticPrint(diagnostic);
}
}
TEST_F(TextToBinaryTest, ImmediateIntOperand) {
SetText("OpCapability !0x00FF00FF");
EXPECT_EQ(SPV_SUCCESS, spvTextToBinary(ScopedContext().context, text.str,
text.length, &binary, &diagnostic));
EXPECT_EQ(0x00FF00FFu, binary->code[6]);
if (diagnostic) {
spvDiagnosticPrint(diagnostic);
}
}
using ImmediateIntTest = TextToBinaryTest;
TEST_F(ImmediateIntTest, AnyWordInSimpleStatement) {
EXPECT_THAT(CompiledInstructions("!0x00040018 %a %b %123"),
Eq(MakeInstruction(SpvOpTypeMatrix, {1, 2, 3})));
EXPECT_THAT(CompiledInstructions("!0x00040018 !1 %b %123"),
Eq(MakeInstruction(SpvOpTypeMatrix, {1, 1, 2})));
EXPECT_THAT(CompiledInstructions("%a = OpTypeMatrix !2 %123"),
Eq(MakeInstruction(SpvOpTypeMatrix, {1, 2, 2})));
EXPECT_THAT(CompiledInstructions("%a = OpTypeMatrix %b !123"),
Eq(MakeInstruction(SpvOpTypeMatrix, {1, 2, 123})));
EXPECT_THAT(CompiledInstructions("!0x00040018 %a !2 %123"),
Eq(MakeInstruction(SpvOpTypeMatrix, {1, 2, 2})));
EXPECT_THAT(CompiledInstructions("!0x00040018 !1 %b !123"),
Eq(MakeInstruction(SpvOpTypeMatrix, {1, 1, 123})));
EXPECT_THAT(CompiledInstructions("!0x00040018 !1 !2 !123"),
Eq(MakeInstruction(SpvOpTypeMatrix, {1, 2, 123})));
}
TEST_F(ImmediateIntTest, AnyWordAfterEqualsAndOpCode) {
EXPECT_THAT(CompiledInstructions("%a = OpArrayLength !2 %c 123"),
Eq(MakeInstruction(SpvOpArrayLength, {2, 1, 2, 123})));
EXPECT_THAT(CompiledInstructions("%a = OpArrayLength %b !3 123"),
Eq(MakeInstruction(SpvOpArrayLength, {1, 2, 3, 123})));
EXPECT_THAT(CompiledInstructions("%a = OpArrayLength %b %c !123"),
Eq(MakeInstruction(SpvOpArrayLength, {1, 2, 3, 123})));
EXPECT_THAT(CompiledInstructions("%a = OpArrayLength %b !3 !123"),
Eq(MakeInstruction(SpvOpArrayLength, {1, 2, 3, 123})));
EXPECT_THAT(CompiledInstructions("%a = OpArrayLength !2 !3 123"),
Eq(MakeInstruction(SpvOpArrayLength, {2, 1, 3, 123})));
EXPECT_THAT(CompiledInstructions("%a = OpArrayLength !2 !3 !123"),
Eq(MakeInstruction(SpvOpArrayLength, {2, 1, 3, 123})));
}
TEST_F(ImmediateIntTest, ResultIdInAssignment) {
EXPECT_EQ("!2 not allowed before =.",
CompileFailure("!2 = OpArrayLength %12 %1 123"));
EXPECT_EQ("!2 not allowed before =.",
CompileFailure("!2 = !0x00040044 %12 %1 123"));
}
TEST_F(ImmediateIntTest, OpCodeInAssignment) {
EXPECT_EQ("Invalid Opcode prefix '!0x00040044'.",
CompileFailure("%2 = !0x00040044 %12 %1 123"));
}
// Literal integers after !<integer> are handled correctly.
TEST_F(ImmediateIntTest, IntegerFollowingImmediate) {
const SpirvVector original = CompiledInstructions("%1 = OpTypeInt 8 1");
EXPECT_EQ(original, CompiledInstructions("!0x00040015 1 8 1"));
EXPECT_EQ(original, CompiledInstructions("!0x00040015 !1 8 1"));
// With !<integer>, we can (and can only) accept 32-bit number literals,
// even when we declare the return type is 64-bit.
EXPECT_EQ(Concatenate({
MakeInstruction(SpvOpTypeInt, {1, 64, 0}),
MakeInstruction(SpvOpConstant, {1, 2, 4294967295}),
}),
CompiledInstructions("%i64 = OpTypeInt 64 0\n"
"!0x0004002b %i64 !2 4294967295"));
// 64-bit integer literal.
EXPECT_EQ("Invalid word following !<integer>: 5000000000",
CompileFailure("%2 = OpConstant !1 5000000000"));
EXPECT_EQ("Invalid word following !<integer>: 5000000000",
CompileFailure("%i64 = OpTypeInt 64 0\n"
"!0x0005002b %i64 !2 5000000000"));
// Negative integer.
EXPECT_EQ(CompiledInstructions("%i64 = OpTypeInt 32 1\n"
"%2 = OpConstant %i64 -123"),
CompiledInstructions("%i64 = OpTypeInt 32 1\n"
"!0x0004002b %i64 !2 -123"));
// TODO(deki): uncomment assertions below and make them pass.
// Hex value(s).
// EXPECT_EQ(CompileSuccessfully("%1 = OpConstant %10 0x12345678"),
// CompileSuccessfully("OpConstant %10 !1 0x12345678", kCAF));
// EXPECT_EQ(
// CompileSuccessfully("%1 = OpConstant %10 0x12345678 0x87654321"),
// CompileSuccessfully("OpConstant %10 !1 0x12345678 0x87654321", kCAF));
}
// Literal floats after !<integer> are handled correctly.
TEST_F(ImmediateIntTest, FloatFollowingImmediate) {
EXPECT_EQ(
CompiledInstructions("%1 = OpTypeFloat 32\n%2 = OpConstant %1 0.123"),
CompiledInstructions("%1 = OpTypeFloat 32\n!0x0004002b %1 !2 0.123"));
EXPECT_EQ(
CompiledInstructions("%1 = OpTypeFloat 32\n%2 = OpConstant %1 -0.5"),
CompiledInstructions("%1 = OpTypeFloat 32\n!0x0004002b %1 !2 -0.5"));
EXPECT_EQ(
CompiledInstructions("%1 = OpTypeFloat 32\n%2 = OpConstant %1 0.123"),
CompiledInstructions("%1 = OpTypeFloat 32\n!0x0004002b %1 %2 0.123"));
EXPECT_EQ(
CompiledInstructions("%1 = OpTypeFloat 32\n%2 = OpConstant %1 -0.5"),
CompiledInstructions("%1 = OpTypeFloat 32\n!0x0004002b %1 %2 -0.5"));
EXPECT_EQ(Concatenate({
MakeInstruction(SpvOpTypeInt, {1, 64, 0}),
MakeInstruction(SpvOpConstant, {1, 2, 0xb, 0xa}),
MakeInstruction(SpvOpSwitch,
{2, 1234, BitwiseCast<uint32_t>(2.5f), 3}),
}),
CompiledInstructions("%i64 = OpTypeInt 64 0\n"
"%big = OpConstant %i64 0xa0000000b\n"
"OpSwitch %big !1234 2.5 %target\n"));
}
// Literal strings after !<integer> are handled correctly.
TEST_F(ImmediateIntTest, StringFollowingImmediate) {
// Try a variety of strings, including empty and single-character.
for (std::string name : {"", "s", "longish", "really looooooooooooooooong"}) {
const SpirvVector original =
CompiledInstructions("OpMemberName %10 4 \"" + name + "\"");
EXPECT_EQ(original,
CompiledInstructions("OpMemberName %10 !4 \"" + name + "\""))
<< name;
EXPECT_EQ(original,
CompiledInstructions("OpMemberName !1 !4 \"" + name + "\""))
<< name;
const uint16_t wordCount = static_cast<uint16_t>(4 + name.size() / 4);
const uint32_t firstWord = spvOpcodeMake(wordCount, SpvOpMemberName);
EXPECT_EQ(original, CompiledInstructions("!" + std::to_string(firstWord) +
" %10 !4 \"" + name + "\""))
<< name;
}
}
// IDs after !<integer> are handled correctly.
TEST_F(ImmediateIntTest, IdFollowingImmediate) {
EXPECT_EQ(CompileSuccessfully("%123 = OpDecorationGroup"),
CompileSuccessfully("!0x00020049 %123"));
EXPECT_EQ(CompileSuccessfully("%group = OpDecorationGroup"),
CompileSuccessfully("!0x00020049 %group"));
}
// !<integer> after !<integer> is handled correctly.
TEST_F(ImmediateIntTest, ImmediateFollowingImmediate) {
const SpirvVector original = CompiledInstructions("%a = OpTypeMatrix %b 7");
EXPECT_EQ(original, CompiledInstructions("%a = OpTypeMatrix !2 !7"));
EXPECT_EQ(original, CompiledInstructions("!0x00040018 %a !2 !7"));
}
TEST_F(ImmediateIntTest, InvalidStatement) {
EXPECT_THAT(Subvector(CompileSuccessfully("!4 !3 !2 !1"), kFirstInstruction),
ElementsAre(4, 3, 2, 1));
}
TEST_F(ImmediateIntTest, InvalidStatementBetweenValidOnes) {
EXPECT_THAT(Subvector(CompileSuccessfully(
"%10 = OpTypeFloat 32 !5 !6 !7 OpEmitVertex"),
kFirstInstruction),
ElementsAre(spvOpcodeMake(3, SpvOpTypeFloat), 1, 32, 5, 6, 7,
spvOpcodeMake(1, SpvOpEmitVertex)));
}
TEST_F(ImmediateIntTest, NextOpcodeRecognized) {
const SpirvVector original = CompileSuccessfully(R"(
%1 = OpLoad %10 %2 Volatile
%4 = OpCompositeInsert %11 %1 %3 0 1 2
)");
const SpirvVector alternate = CompileSuccessfully(R"(
%1 = OpLoad %10 %2 !1
%4 = OpCompositeInsert %11 %1 %3 0 1 2
)");
EXPECT_EQ(original, alternate);
}
TEST_F(ImmediateIntTest, WrongLengthButNextOpcodeStillRecognized) {
const SpirvVector original = CompileSuccessfully(R"(
%1 = OpLoad %10 %2 Volatile
OpCopyMemorySized %3 %4 %1
)");
const SpirvVector alternate = CompileSuccessfully(R"(
!0x0002003D %10 %1 %2 !1
OpCopyMemorySized %3 %4 %1
)");
EXPECT_EQ(0x0002003Du, alternate[kFirstInstruction]);
EXPECT_EQ(Subvector(original, kFirstInstruction + 1),
Subvector(alternate, kFirstInstruction + 1));
}
// Like NextOpcodeRecognized, but next statement is in assignment form.
TEST_F(ImmediateIntTest, NextAssignmentRecognized) {
const SpirvVector original = CompileSuccessfully(R"(
%1 = OpLoad %10 %2 None
%4 = OpFunctionCall %10 %3 %123
)");
const SpirvVector alternate = CompileSuccessfully(R"(
%1 = OpLoad %10 %2 !0
%4 = OpFunctionCall %10 %3 %123
)");
EXPECT_EQ(original, alternate);
}
// Two instructions in a row each have !<integer> opcode.
TEST_F(ImmediateIntTest, ConsecutiveImmediateOpcodes) {
const SpirvVector original = CompileSuccessfully(R"(
%1 = OpConstantSampler %10 Clamp 78 Linear
%4 = OpFRem %11 %3 %2
%5 = OpIsValidEvent %12 %2
)");
const SpirvVector alternate = CompileSuccessfully(R"(
!0x0006002D %10 %1 !2 78 !1
!0x0005008C %11 %4 %3 %2
%5 = OpIsValidEvent %12 %2
)");
EXPECT_EQ(original, alternate);
}
// !<integer> followed by, eg, an enum or '=' or a random bareword.
TEST_F(ImmediateIntTest, ForbiddenOperands) {
EXPECT_THAT(CompileFailure("OpMemoryModel !0 OpenCL"), HasSubstr("OpenCL"));
EXPECT_THAT(CompileFailure("!1 %0 = !2"), HasSubstr("="));
EXPECT_THAT(CompileFailure("OpMemoryModel !0 random_bareword"),
HasSubstr("random_bareword"));
// Immediate integers longer than one 32-bit word.
EXPECT_THAT(CompileFailure("!5000000000"), HasSubstr("5000000000"));
EXPECT_THAT(CompileFailure("!999999999999999999"),
HasSubstr("999999999999999999"));
EXPECT_THAT(CompileFailure("!0x00020049 !5000000000"),
HasSubstr("5000000000"));
// Negative numbers.
EXPECT_THAT(CompileFailure("!0x00020049 !-123"), HasSubstr("-123"));
}
TEST_F(ImmediateIntTest, NotInteger) {
EXPECT_THAT(CompileFailure("!abc"), StrEq("Invalid immediate integer: !abc"));
EXPECT_THAT(CompileFailure("!12.3"),
StrEq("Invalid immediate integer: !12.3"));
EXPECT_THAT(CompileFailure("!12K"), StrEq("Invalid immediate integer: !12K"));
}
} // namespace
} // namespace utils
} // namespace spvtools

View File

@@ -1,25 +0,0 @@
// Copyright (c) 2015-2016 The Khronos Group 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 "test/unit_spirv.h"
namespace spvtools {
namespace {
TEST(Macros, BitShiftInnerParens) { ASSERT_EQ(65536, SPV_BIT(2 << 3)); }
TEST(Macros, BitShiftOuterParens) { ASSERT_EQ(15, SPV_BIT(4) - 1); }
} // namespace
} // namespace spvtools

View File

@@ -1,28 +0,0 @@
# Copyright (c) 2017 Pierre Moreau
#
# 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.
add_spvtools_unittest(TARGET link
SRCS
binary_version_test.cpp
entry_points_test.cpp
global_values_amount_test.cpp
ids_limit_test.cpp
matching_imports_to_exports_test.cpp
memory_model_test.cpp
partial_linkage_test.cpp
unique_ids_test.cpp
type_match_test.cpp
LIBS SPIRV-Tools-opt SPIRV-Tools-link
)

View File

@@ -1,60 +0,0 @@
// Copyright (c) 2017 Pierre Moreau
//
// 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 "gmock/gmock.h"
#include "test/link/linker_fixture.h"
namespace spvtools {
namespace {
using BinaryVersion = spvtest::LinkerTest;
TEST_F(BinaryVersion, LinkerChoosesMaxSpirvVersion) {
// clang-format off
spvtest::Binaries binaries = {
{
SpvMagicNumber,
0x00000300u,
SPV_GENERATOR_CODEPLAY,
1u, // NOTE: Bound
0u // NOTE: Schema; reserved
},
{
SpvMagicNumber,
0x00000600u,
SPV_GENERATOR_CODEPLAY,
1u, // NOTE: Bound
0u // NOTE: Schema; reserved
},
{
SpvMagicNumber,
0x00000100u,
SPV_GENERATOR_CODEPLAY,
1u, // NOTE: Bound
0u // NOTE: Schema; reserved
}
};
// clang-format on
spvtest::Binary linked_binary;
ASSERT_EQ(SPV_SUCCESS, Link(binaries, &linked_binary));
EXPECT_THAT(GetErrorMessage(), std::string());
EXPECT_EQ(0x00000600u, linked_binary[1]);
}
} // namespace
} // namespace spvtools

View File

@@ -1,94 +0,0 @@
// Copyright (c) 2017 Pierre Moreau
//
// 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 "gmock/gmock.h"
#include "test/link/linker_fixture.h"
namespace spvtools {
namespace {
using ::testing::HasSubstr;
class EntryPoints : public spvtest::LinkerTest {};
TEST_F(EntryPoints, SameModelDifferentName) {
const std::string body1 = R"(
OpEntryPoint GLCompute %3 "foo"
%1 = OpTypeVoid
%2 = OpTypeFunction %1
%3 = OpFunction %1 None %2
OpFunctionEnd
)";
const std::string body2 = R"(
OpEntryPoint GLCompute %3 "bar"
%1 = OpTypeVoid
%2 = OpTypeFunction %1
%3 = OpFunction %1 None %2
OpFunctionEnd
)";
spvtest::Binary linked_binary;
EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary));
EXPECT_THAT(GetErrorMessage(), std::string());
}
TEST_F(EntryPoints, DifferentModelSameName) {
const std::string body1 = R"(
OpEntryPoint GLCompute %3 "foo"
%1 = OpTypeVoid
%2 = OpTypeFunction %1
%3 = OpFunction %1 None %2
OpFunctionEnd
)";
const std::string body2 = R"(
OpEntryPoint Vertex %3 "foo"
%1 = OpTypeVoid
%2 = OpTypeFunction %1
%3 = OpFunction %1 None %2
OpFunctionEnd
)";
spvtest::Binary linked_binary;
EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary));
EXPECT_THAT(GetErrorMessage(), std::string());
}
TEST_F(EntryPoints, SameModelAndName) {
const std::string body1 = R"(
OpEntryPoint GLCompute %3 "foo"
%1 = OpTypeVoid
%2 = OpTypeFunction %1
%3 = OpFunction %1 None %2
OpFunctionEnd
)";
const std::string body2 = R"(
OpEntryPoint GLCompute %3 "foo"
%1 = OpTypeVoid
%2 = OpTypeFunction %1
%3 = OpFunction %1 None %2
OpFunctionEnd
)";
spvtest::Binary linked_binary;
EXPECT_EQ(SPV_ERROR_INTERNAL,
AssembleAndLink({body1, body2}, &linked_binary));
EXPECT_THAT(GetErrorMessage(),
HasSubstr("The entry point \"foo\", with execution model "
"GLCompute, was already defined."));
}
} // namespace
} // namespace spvtools

View File

@@ -1,153 +0,0 @@
// Copyright (c) 2017 Pierre Moreau
//
// 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 "gmock/gmock.h"
#include "test/link/linker_fixture.h"
namespace spvtools {
namespace {
using ::testing::HasSubstr;
class EntryPointsAmountTest : public spvtest::LinkerTest {
public:
EntryPointsAmountTest() { binaries.reserve(0xFFFF); }
void SetUp() override {
binaries.push_back({SpvMagicNumber,
SpvVersion,
SPV_GENERATOR_CODEPLAY,
10u, // NOTE: Bound
0u, // NOTE: Schema; reserved
3u << SpvWordCountShift | SpvOpTypeFloat,
1u, // NOTE: Result ID
32u, // NOTE: Width
4u << SpvWordCountShift | SpvOpTypePointer,
2u, // NOTE: Result ID
SpvStorageClassInput,
1u, // NOTE: Type ID
2u << SpvWordCountShift | SpvOpTypeVoid,
3u, // NOTE: Result ID
3u << SpvWordCountShift | SpvOpTypeFunction,
4u, // NOTE: Result ID
3u, // NOTE: Return type
5u << SpvWordCountShift | SpvOpFunction,
3u, // NOTE: Result type
5u, // NOTE: Result ID
SpvFunctionControlMaskNone,
4u, // NOTE: Function type
2u << SpvWordCountShift | SpvOpLabel,
6u, // NOTE: Result ID
4u << SpvWordCountShift | SpvOpVariable,
2u, // NOTE: Type ID
7u, // NOTE: Result ID
SpvStorageClassFunction,
4u << SpvWordCountShift | SpvOpVariable,
2u, // NOTE: Type ID
8u, // NOTE: Result ID
SpvStorageClassFunction,
4u << SpvWordCountShift | SpvOpVariable,
2u, // NOTE: Type ID
9u, // NOTE: Result ID
SpvStorageClassFunction,
1u << SpvWordCountShift | SpvOpReturn,
1u << SpvWordCountShift | SpvOpFunctionEnd});
for (size_t i = 0u; i < 2u; ++i) {
spvtest::Binary binary = {
SpvMagicNumber,
SpvVersion,
SPV_GENERATOR_CODEPLAY,
103u, // NOTE: Bound
0u, // NOTE: Schema; reserved
3u << SpvWordCountShift | SpvOpTypeFloat,
1u, // NOTE: Result ID
32u, // NOTE: Width
4u << SpvWordCountShift | SpvOpTypePointer,
2u, // NOTE: Result ID
SpvStorageClassInput,
1u // NOTE: Type ID
};
for (uint32_t j = 0u; j < 0xFFFFu / 2u; ++j) {
binary.push_back(4u << SpvWordCountShift | SpvOpVariable);
binary.push_back(2u); // NOTE: Type ID
binary.push_back(j + 3u); // NOTE: Result ID
binary.push_back(SpvStorageClassInput);
}
binaries.push_back(binary);
}
}
void TearDown() override { binaries.clear(); }
spvtest::Binaries binaries;
};
TEST_F(EntryPointsAmountTest, UnderLimit) {
spvtest::Binary linked_binary;
EXPECT_EQ(SPV_SUCCESS, Link(binaries, &linked_binary));
EXPECT_THAT(GetErrorMessage(), std::string());
}
TEST_F(EntryPointsAmountTest, OverLimit) {
binaries.push_back({SpvMagicNumber,
SpvVersion,
SPV_GENERATOR_CODEPLAY,
5u, // NOTE: Bound
0u, // NOTE: Schema; reserved
3u << SpvWordCountShift | SpvOpTypeFloat,
1u, // NOTE: Result ID
32u, // NOTE: Width
4u << SpvWordCountShift | SpvOpTypePointer,
2u, // NOTE: Result ID
SpvStorageClassInput,
1u, // NOTE: Type ID
4u << SpvWordCountShift | SpvOpVariable,
2u, // NOTE: Type ID
3u, // NOTE: Result ID
SpvStorageClassInput,
4u << SpvWordCountShift | SpvOpVariable,
2u, // NOTE: Type ID
4u, // NOTE: Result ID
SpvStorageClassInput});
spvtest::Binary linked_binary;
EXPECT_EQ(SPV_ERROR_INTERNAL, Link(binaries, &linked_binary));
EXPECT_THAT(GetErrorMessage(),
HasSubstr("The limit of global values, 65535, was exceeded; "
"65536 global values were found."));
}
} // namespace
} // namespace spvtools

View File

@@ -1,72 +0,0 @@
// Copyright (c) 2017 Pierre Moreau
//
// 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 "gmock/gmock.h"
#include "test/link/linker_fixture.h"
namespace spvtools {
namespace {
using ::testing::HasSubstr;
using IdsLimit = spvtest::LinkerTest;
TEST_F(IdsLimit, UnderLimit) {
spvtest::Binaries binaries = {
{
SpvMagicNumber, SpvVersion, SPV_GENERATOR_CODEPLAY,
0x2FFFFFu, // NOTE: Bound
0u, // NOTE: Schema; reserved
},
{
SpvMagicNumber, SpvVersion, SPV_GENERATOR_CODEPLAY,
0x100000u, // NOTE: Bound
0u, // NOTE: Schema; reserved
}};
spvtest::Binary linked_binary;
ASSERT_EQ(SPV_SUCCESS, Link(binaries, &linked_binary));
EXPECT_THAT(GetErrorMessage(), std::string());
EXPECT_EQ(0x3FFFFEu, linked_binary[3]);
}
TEST_F(IdsLimit, OverLimit) {
spvtest::Binaries binaries = {
{
SpvMagicNumber, SpvVersion, SPV_GENERATOR_CODEPLAY,
0x2FFFFFu, // NOTE: Bound
0u, // NOTE: Schema; reserved
},
{
SpvMagicNumber, SpvVersion, SPV_GENERATOR_CODEPLAY,
0x100000u, // NOTE: Bound
0u, // NOTE: Schema; reserved
},
{
SpvMagicNumber, SpvVersion, SPV_GENERATOR_CODEPLAY,
3u, // NOTE: Bound
0u, // NOTE: Schema; reserved
}};
spvtest::Binary linked_binary;
EXPECT_EQ(SPV_ERROR_INVALID_ID, Link(binaries, &linked_binary));
EXPECT_THAT(GetErrorMessage(),
HasSubstr("The limit of IDs, 4194303, was exceeded: 4194304 is "
"the current ID bound."));
}
} // namespace
} // namespace spvtools

View File

@@ -1,222 +0,0 @@
// Copyright (c) 2017 Pierre Moreau
//
// 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.
#ifndef TEST_LINK_LINKER_FIXTURE_H_
#define TEST_LINK_LINKER_FIXTURE_H_
#include <iostream>
#include <string>
#include <vector>
#include "effcee/effcee.h"
#include "re2/re2.h"
#include "source/spirv_constant.h"
#include "spirv-tools/linker.hpp"
#include "test/unit_spirv.h"
namespace spvtest {
using Binary = std::vector<uint32_t>;
using Binaries = std::vector<Binary>;
class LinkerTest : public ::testing::Test {
public:
LinkerTest()
: context_(SPV_ENV_UNIVERSAL_1_2),
tools_(SPV_ENV_UNIVERSAL_1_2),
assemble_options_(spvtools::SpirvTools::kDefaultAssembleOption),
disassemble_options_(spvtools::SpirvTools::kDefaultDisassembleOption) {
const auto consumer = [this](spv_message_level_t level, const char*,
const spv_position_t& position,
const char* message) {
if (!error_message_.empty()) error_message_ += "\n";
switch (level) {
case SPV_MSG_FATAL:
case SPV_MSG_INTERNAL_ERROR:
case SPV_MSG_ERROR:
error_message_ += "ERROR";
break;
case SPV_MSG_WARNING:
error_message_ += "WARNING";
break;
case SPV_MSG_INFO:
error_message_ += "INFO";
break;
case SPV_MSG_DEBUG:
error_message_ += "DEBUG";
break;
}
error_message_ += ": " + std::to_string(position.index) + ": " + message;
};
context_.SetMessageConsumer(consumer);
tools_.SetMessageConsumer(consumer);
}
void TearDown() override { error_message_.clear(); }
// Assembles each of the given strings into SPIR-V binaries before linking
// them together. SPV_ERROR_INVALID_TEXT is returned if the assembling failed
// for any of the input strings, and SPV_ERROR_INVALID_POINTER if
// |linked_binary| is a null pointer.
spv_result_t AssembleAndLink(
const std::vector<std::string>& bodies, spvtest::Binary* linked_binary,
spvtools::LinkerOptions options = spvtools::LinkerOptions()) {
if (!linked_binary) return SPV_ERROR_INVALID_POINTER;
spvtest::Binaries binaries(bodies.size());
for (size_t i = 0u; i < bodies.size(); ++i)
if (!tools_.Assemble(bodies[i], binaries.data() + i, assemble_options_))
return SPV_ERROR_INVALID_TEXT;
return spvtools::Link(context_, binaries, linked_binary, options);
}
// Assembles and links a vector of SPIR-V bodies based on the |templateBody|.
// Template arguments to be replaced are written as {a,b,...}.
// SPV_ERROR_INVALID_TEXT is returned if the assembling failed for any of the
// resulting bodies (or errors in the template), and SPV_ERROR_INVALID_POINTER
// if |linked_binary| is a null pointer.
spv_result_t ExpandAndLink(
const std::string& templateBody, spvtest::Binary* linked_binary,
spvtools::LinkerOptions options = spvtools::LinkerOptions()) {
if (!linked_binary) return SPV_ERROR_INVALID_POINTER;
// Find out how many template arguments there are, we assume they all have
// the same number. We'll error later if they don't.
re2::StringPiece temp(templateBody);
re2::StringPiece x;
int cnt = 0;
if (!RE2::FindAndConsume(&temp, "{")) return SPV_ERROR_INVALID_TEXT;
while (RE2::FindAndConsume(&temp, "([,}])", &x) && x[0] == ',') cnt++;
cnt++;
if (cnt <= 1) return SPV_ERROR_INVALID_TEXT;
// Construct a regex for a single common strip and template expansion.
std::string regex("([^{]*){");
for (int i = 0; i < cnt; i++) regex += (i > 0) ? ",([^,]*)" : "([^,]*)";
regex += "}";
RE2 pattern(regex);
// Prepare the RE2::Args for processing.
re2::StringPiece common;
std::vector<re2::StringPiece> variants(cnt);
std::vector<RE2::Arg> args(cnt + 1);
args[0] = RE2::Arg(&common);
std::vector<RE2::Arg*> pargs(cnt + 1);
pargs[0] = &args[0];
for (int i = 0; i < cnt; i++) {
args[i + 1] = RE2::Arg(&variants[i]);
pargs[i + 1] = &args[i + 1];
}
// Reset and construct the bodies bit by bit.
std::vector<std::string> bodies(cnt);
re2::StringPiece temp2(templateBody);
while (RE2::ConsumeN(&temp2, pattern, pargs.data(), cnt + 1)) {
for (int i = 0; i < cnt; i++) {
bodies[i].append(common.begin(), common.end());
bodies[i].append(variants[i].begin(), variants[i].end());
}
}
RE2::Consume(&temp2, "([^{]*)", &common);
for (int i = 0; i < cnt; i++)
bodies[i].append(common.begin(), common.end());
// Run through the assemble and link stages of the process.
return AssembleAndLink(bodies, linked_binary, options);
}
// Expand the |templateBody| and link the results as with ExpandAndLink,
// then disassemble and test that the result matches the |expected|.
void ExpandAndCheck(
const std::string& templateBody, const std::string& expected,
const spvtools::LinkerOptions options = spvtools::LinkerOptions()) {
spvtest::Binary linked_binary;
spv_result_t res = ExpandAndLink(templateBody, &linked_binary, options);
EXPECT_EQ(SPV_SUCCESS, res) << GetErrorMessage() << "\nExpanded from:\n"
<< templateBody;
if (res == SPV_SUCCESS) {
std::string result;
EXPECT_TRUE(
tools_.Disassemble(linked_binary, &result, disassemble_options_))
<< GetErrorMessage();
EXPECT_EQ(expected, result);
}
}
// An alternative to ExpandAndCheck, which uses the |templateBody| as the
// match pattern for the disassembled linked result.
void ExpandAndMatch(
const std::string& templateBody,
const spvtools::LinkerOptions options = spvtools::LinkerOptions()) {
spvtest::Binary linked_binary;
spv_result_t res = ExpandAndLink(templateBody, &linked_binary, options);
EXPECT_EQ(SPV_SUCCESS, res) << GetErrorMessage() << "\nExpanded from:\n"
<< templateBody;
if (res == SPV_SUCCESS) {
std::string result;
EXPECT_TRUE(
tools_.Disassemble(linked_binary, &result, disassemble_options_))
<< GetErrorMessage();
auto match_res = effcee::Match(result, templateBody);
EXPECT_EQ(effcee::Result::Status::Ok, match_res.status())
<< match_res.message() << "\nExpanded from:\n"
<< templateBody << "\nChecking result:\n"
<< result;
}
}
// Links the given SPIR-V binaries together; SPV_ERROR_INVALID_POINTER is
// returned if |linked_binary| is a null pointer.
spv_result_t Link(
const spvtest::Binaries& binaries, spvtest::Binary* linked_binary,
spvtools::LinkerOptions options = spvtools::LinkerOptions()) {
if (!linked_binary) return SPV_ERROR_INVALID_POINTER;
return spvtools::Link(context_, binaries, linked_binary, options);
}
// Disassembles |binary| and outputs the result in |text|. If |text| is a
// null pointer, SPV_ERROR_INVALID_POINTER is returned.
spv_result_t Disassemble(const spvtest::Binary& binary, std::string* text) {
if (!text) return SPV_ERROR_INVALID_POINTER;
return tools_.Disassemble(binary, text, disassemble_options_)
? SPV_SUCCESS
: SPV_ERROR_INVALID_BINARY;
}
// Sets the options for the assembler.
void SetAssembleOptions(uint32_t assemble_options) {
assemble_options_ = assemble_options;
}
// Sets the options used by the disassembler.
void SetDisassembleOptions(uint32_t disassemble_options) {
disassemble_options_ = disassemble_options;
}
// Returns the accumulated error messages for the test.
std::string GetErrorMessage() const { return error_message_; }
private:
spvtools::Context context_;
spvtools::SpirvTools
tools_; // An instance for calling SPIRV-Tools functionalities.
uint32_t assemble_options_;
uint32_t disassemble_options_;
std::string error_message_;
};
} // namespace spvtest
#endif // TEST_LINK_LINKER_FIXTURE_H_

View File

@@ -1,403 +0,0 @@
// Copyright (c) 2017 Pierre Moreau
//
// 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 "gmock/gmock.h"
#include "test/link/linker_fixture.h"
namespace spvtools {
namespace {
using ::testing::HasSubstr;
using MatchingImportsToExports = spvtest::LinkerTest;
TEST_F(MatchingImportsToExports, Default) {
const std::string body1 = R"(
OpCapability Linkage
OpDecorate %1 LinkageAttributes "foo" Import
%2 = OpTypeFloat 32
%1 = OpVariable %2 Uniform
%3 = OpVariable %2 Input
)";
const std::string body2 = R"(
OpCapability Linkage
OpDecorate %1 LinkageAttributes "foo" Export
%2 = OpTypeFloat 32
%3 = OpConstant %2 42
%1 = OpVariable %2 Uniform %3
)";
spvtest::Binary linked_binary;
EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary))
<< GetErrorMessage();
const std::string expected_res =
R"(OpModuleProcessed "Linked by SPIR-V Tools Linker"
%1 = OpTypeFloat 32
%2 = OpVariable %1 Input
%3 = OpConstant %1 42
%4 = OpVariable %1 Uniform %3
)";
std::string res_body;
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body))
<< GetErrorMessage();
EXPECT_EQ(expected_res, res_body);
}
TEST_F(MatchingImportsToExports, NotALibraryExtraExports) {
const std::string body = R"(
OpCapability Linkage
OpDecorate %1 LinkageAttributes "foo" Export
%2 = OpTypeFloat 32
%1 = OpVariable %2 Uniform
)";
spvtest::Binary linked_binary;
EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body}, &linked_binary))
<< GetErrorMessage();
const std::string expected_res =
R"(OpModuleProcessed "Linked by SPIR-V Tools Linker"
%1 = OpTypeFloat 32
%2 = OpVariable %1 Uniform
)";
std::string res_body;
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body))
<< GetErrorMessage();
EXPECT_EQ(expected_res, res_body);
}
TEST_F(MatchingImportsToExports, LibraryExtraExports) {
const std::string body = R"(
OpCapability Linkage
OpDecorate %1 LinkageAttributes "foo" Export
%2 = OpTypeFloat 32
%1 = OpVariable %2 Uniform
)";
spvtest::Binary linked_binary;
LinkerOptions options;
options.SetCreateLibrary(true);
EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body}, &linked_binary, options))
<< GetErrorMessage();
const std::string expected_res = R"(OpCapability Linkage
OpModuleProcessed "Linked by SPIR-V Tools Linker"
OpDecorate %1 LinkageAttributes "foo" Export
%2 = OpTypeFloat 32
%1 = OpVariable %2 Uniform
)";
std::string res_body;
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body))
<< GetErrorMessage();
EXPECT_EQ(expected_res, res_body);
}
TEST_F(MatchingImportsToExports, UnresolvedImports) {
const std::string body1 = R"(
OpCapability Linkage
OpDecorate %1 LinkageAttributes "foo" Import
%2 = OpTypeFloat 32
%1 = OpVariable %2 Uniform
)";
const std::string body2 = R"()";
spvtest::Binary linked_binary;
EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
AssembleAndLink({body1, body2}, &linked_binary));
EXPECT_THAT(GetErrorMessage(),
HasSubstr("Unresolved external reference to \"foo\"."));
}
TEST_F(MatchingImportsToExports, TypeMismatch) {
const std::string body1 = R"(
OpCapability Linkage
OpDecorate %1 LinkageAttributes "foo" Import
%2 = OpTypeFloat 32
%1 = OpVariable %2 Uniform
%3 = OpVariable %2 Input
)";
const std::string body2 = R"(
OpCapability Linkage
OpDecorate %1 LinkageAttributes "foo" Export
%2 = OpTypeInt 32 0
%3 = OpConstant %2 42
%1 = OpVariable %2 Uniform %3
)";
spvtest::Binary linked_binary;
EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
AssembleAndLink({body1, body2}, &linked_binary))
<< GetErrorMessage();
EXPECT_THAT(
GetErrorMessage(),
HasSubstr("Type mismatch on symbol \"foo\" between imported "
"variable/function %1 and exported variable/function %4"));
}
TEST_F(MatchingImportsToExports, MultipleDefinitions) {
const std::string body1 = R"(
OpCapability Linkage
OpDecorate %1 LinkageAttributes "foo" Import
%2 = OpTypeFloat 32
%1 = OpVariable %2 Uniform
%3 = OpVariable %2 Input
)";
const std::string body2 = R"(
OpCapability Linkage
OpDecorate %1 LinkageAttributes "foo" Export
%2 = OpTypeFloat 32
%3 = OpConstant %2 42
%1 = OpVariable %2 Uniform %3
)";
const std::string body3 = R"(
OpCapability Linkage
OpDecorate %1 LinkageAttributes "foo" Export
%2 = OpTypeFloat 32
%3 = OpConstant %2 -1
%1 = OpVariable %2 Uniform %3
)";
spvtest::Binary linked_binary;
EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
AssembleAndLink({body1, body2, body3}, &linked_binary))
<< GetErrorMessage();
EXPECT_THAT(GetErrorMessage(),
HasSubstr("Too many external references, 2, were found "
"for \"foo\"."));
}
TEST_F(MatchingImportsToExports, SameNameDifferentTypes) {
const std::string body1 = R"(
OpCapability Linkage
OpDecorate %1 LinkageAttributes "foo" Import
%2 = OpTypeFloat 32
%1 = OpVariable %2 Uniform
%3 = OpVariable %2 Input
)";
const std::string body2 = R"(
OpCapability Linkage
OpDecorate %1 LinkageAttributes "foo" Export
%2 = OpTypeInt 32 0
%3 = OpConstant %2 42
%1 = OpVariable %2 Uniform %3
)";
const std::string body3 = R"(
OpCapability Linkage
OpDecorate %1 LinkageAttributes "foo" Export
%2 = OpTypeFloat 32
%3 = OpConstant %2 12
%1 = OpVariable %2 Uniform %3
)";
spvtest::Binary linked_binary;
EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
AssembleAndLink({body1, body2, body3}, &linked_binary))
<< GetErrorMessage();
EXPECT_THAT(GetErrorMessage(),
HasSubstr("Too many external references, 2, were found "
"for \"foo\"."));
}
TEST_F(MatchingImportsToExports, DecorationMismatch) {
const std::string body1 = R"(
OpCapability Linkage
OpDecorate %1 LinkageAttributes "foo" Import
OpDecorate %2 Constant
%2 = OpTypeFloat 32
%1 = OpVariable %2 Uniform
%3 = OpVariable %2 Input
)";
const std::string body2 = R"(
OpCapability Linkage
OpDecorate %1 LinkageAttributes "foo" Export
%2 = OpTypeFloat 32
%3 = OpConstant %2 42
%1 = OpVariable %2 Uniform %3
)";
spvtest::Binary linked_binary;
EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
AssembleAndLink({body1, body2}, &linked_binary))
<< GetErrorMessage();
EXPECT_THAT(
GetErrorMessage(),
HasSubstr("Type mismatch on symbol \"foo\" between imported "
"variable/function %1 and exported variable/function %4"));
}
TEST_F(MatchingImportsToExports,
FuncParamAttrDifferButStillMatchExportToImport) {
const std::string body1 = R"(
OpCapability Kernel
OpCapability Linkage
OpDecorate %1 LinkageAttributes "foo" Import
OpDecorate %2 FuncParamAttr Zext
%3 = OpTypeVoid
%4 = OpTypeInt 32 0
%5 = OpTypeFunction %3 %4
%1 = OpFunction %3 None %5
%2 = OpFunctionParameter %4
OpFunctionEnd
)";
const std::string body2 = R"(
OpCapability Kernel
OpCapability Linkage
OpDecorate %1 LinkageAttributes "foo" Export
OpDecorate %2 FuncParamAttr Sext
%3 = OpTypeVoid
%4 = OpTypeInt 32 0
%5 = OpTypeFunction %3 %4
%1 = OpFunction %3 None %5
%2 = OpFunctionParameter %4
%6 = OpLabel
OpReturn
OpFunctionEnd
)";
spvtest::Binary linked_binary;
EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary))
<< GetErrorMessage();
const std::string expected_res = R"(OpCapability Kernel
OpModuleProcessed "Linked by SPIR-V Tools Linker"
OpDecorate %1 FuncParamAttr Sext
%2 = OpTypeVoid
%3 = OpTypeInt 32 0
%4 = OpTypeFunction %2 %3
%5 = OpFunction %2 None %4
%1 = OpFunctionParameter %3
%6 = OpLabel
OpReturn
OpFunctionEnd
)";
std::string res_body;
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body))
<< GetErrorMessage();
EXPECT_EQ(expected_res, res_body);
}
TEST_F(MatchingImportsToExports, FunctionCtrl) {
const std::string body1 = R"(
OpCapability Linkage
OpDecorate %1 LinkageAttributes "foo" Import
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%4 = OpTypeFloat 32
%5 = OpVariable %4 Uniform
%1 = OpFunction %2 None %3
OpFunctionEnd
)";
const std::string body2 = R"(
OpCapability Linkage
OpDecorate %1 LinkageAttributes "foo" Export
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%1 = OpFunction %2 Inline %3
%4 = OpLabel
OpReturn
OpFunctionEnd
)";
spvtest::Binary linked_binary;
EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary))
<< GetErrorMessage();
const std::string expected_res =
R"(OpModuleProcessed "Linked by SPIR-V Tools Linker"
%1 = OpTypeVoid
%2 = OpTypeFunction %1
%3 = OpTypeFloat 32
%4 = OpVariable %3 Uniform
%5 = OpFunction %1 Inline %2
%6 = OpLabel
OpReturn
OpFunctionEnd
)";
std::string res_body;
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body))
<< GetErrorMessage();
EXPECT_EQ(expected_res, res_body);
}
TEST_F(MatchingImportsToExports, UseExportedFuncParamAttr) {
const std::string body1 = R"(
OpCapability Kernel
OpCapability Linkage
OpDecorate %1 LinkageAttributes "foo" Import
OpDecorate %2 FuncParamAttr Zext
%2 = OpDecorationGroup
OpGroupDecorate %2 %3 %4
%5 = OpTypeVoid
%6 = OpTypeInt 32 0
%7 = OpTypeFunction %5 %6
%1 = OpFunction %5 None %7
%3 = OpFunctionParameter %6
OpFunctionEnd
%8 = OpFunction %5 None %7
%4 = OpFunctionParameter %6
OpFunctionEnd
)";
const std::string body2 = R"(
OpCapability Kernel
OpCapability Linkage
OpDecorate %1 LinkageAttributes "foo" Export
OpDecorate %2 FuncParamAttr Sext
%3 = OpTypeVoid
%4 = OpTypeInt 32 0
%5 = OpTypeFunction %3 %4
%1 = OpFunction %3 None %5
%2 = OpFunctionParameter %4
%6 = OpLabel
OpReturn
OpFunctionEnd
)";
spvtest::Binary linked_binary;
EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary))
<< GetErrorMessage();
const std::string expected_res = R"(OpCapability Kernel
OpModuleProcessed "Linked by SPIR-V Tools Linker"
OpDecorate %1 FuncParamAttr Zext
%1 = OpDecorationGroup
OpGroupDecorate %1 %2
OpDecorate %3 FuncParamAttr Sext
%4 = OpTypeVoid
%5 = OpTypeInt 32 0
%6 = OpTypeFunction %4 %5
%7 = OpFunction %4 None %6
%2 = OpFunctionParameter %5
OpFunctionEnd
%8 = OpFunction %4 None %6
%3 = OpFunctionParameter %5
%9 = OpLabel
OpReturn
OpFunctionEnd
)";
std::string res_body;
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body))
<< GetErrorMessage();
EXPECT_EQ(expected_res, res_body);
}
} // namespace
} // namespace spvtools

View File

@@ -1,74 +0,0 @@
// Copyright (c) 2017 Pierre Moreau
//
// 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 "gmock/gmock.h"
#include "test/link/linker_fixture.h"
namespace spvtools {
namespace {
using ::testing::HasSubstr;
using MemoryModel = spvtest::LinkerTest;
TEST_F(MemoryModel, Default) {
const std::string body1 = R"(
OpMemoryModel Logical Simple
)";
const std::string body2 = R"(
OpMemoryModel Logical Simple
)";
spvtest::Binary linked_binary;
ASSERT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary));
EXPECT_THAT(GetErrorMessage(), std::string());
EXPECT_EQ(SpvAddressingModelLogical, linked_binary[6]);
EXPECT_EQ(SpvMemoryModelSimple, linked_binary[7]);
}
TEST_F(MemoryModel, AddressingMismatch) {
const std::string body1 = R"(
OpMemoryModel Logical Simple
)";
const std::string body2 = R"(
OpMemoryModel Physical32 Simple
)";
spvtest::Binary linked_binary;
EXPECT_EQ(SPV_ERROR_INTERNAL,
AssembleAndLink({body1, body2}, &linked_binary));
EXPECT_THAT(
GetErrorMessage(),
HasSubstr("Conflicting addressing models: Logical vs Physical32."));
}
TEST_F(MemoryModel, MemoryMismatch) {
const std::string body1 = R"(
OpMemoryModel Logical Simple
)";
const std::string body2 = R"(
OpMemoryModel Logical GLSL450
)";
spvtest::Binary linked_binary;
EXPECT_EQ(SPV_ERROR_INTERNAL,
AssembleAndLink({body1, body2}, &linked_binary));
EXPECT_THAT(GetErrorMessage(),
HasSubstr("Conflicting memory models: Simple vs GLSL450."));
}
} // namespace
} // namespace spvtools

View File

@@ -1,89 +0,0 @@
// Copyright (c) 2018 Pierre Moreau
//
// 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 "gmock/gmock.h"
#include "test/link/linker_fixture.h"
namespace spvtools {
namespace {
using ::testing::HasSubstr;
using PartialLinkage = spvtest::LinkerTest;
TEST_F(PartialLinkage, Allowed) {
const std::string body1 = R"(
OpCapability Linkage
OpDecorate %1 LinkageAttributes "foo" Import
OpDecorate %2 LinkageAttributes "bar" Import
%3 = OpTypeFloat 32
%1 = OpVariable %3 Uniform
%2 = OpVariable %3 Uniform
)";
const std::string body2 = R"(
OpCapability Linkage
OpDecorate %1 LinkageAttributes "bar" Export
%2 = OpTypeFloat 32
%3 = OpConstant %2 3.1415
%1 = OpVariable %2 Uniform %3
)";
spvtest::Binary linked_binary;
LinkerOptions linker_options;
linker_options.SetAllowPartialLinkage(true);
ASSERT_EQ(SPV_SUCCESS,
AssembleAndLink({body1, body2}, &linked_binary, linker_options));
const std::string expected_res = R"(OpCapability Linkage
OpModuleProcessed "Linked by SPIR-V Tools Linker"
OpDecorate %1 LinkageAttributes "foo" Import
%2 = OpTypeFloat 32
%1 = OpVariable %2 Uniform
%3 = OpConstant %2 3.1415
%4 = OpVariable %2 Uniform %3
)";
std::string res_body;
SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
ASSERT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body))
<< GetErrorMessage();
EXPECT_EQ(expected_res, res_body);
}
TEST_F(PartialLinkage, Disallowed) {
const std::string body1 = R"(
OpCapability Linkage
OpDecorate %1 LinkageAttributes "foo" Import
OpDecorate %2 LinkageAttributes "bar" Import
%3 = OpTypeFloat 32
%1 = OpVariable %3 Uniform
%2 = OpVariable %3 Uniform
)";
const std::string body2 = R"(
OpCapability Linkage
OpDecorate %1 LinkageAttributes "bar" Export
%2 = OpTypeFloat 32
%3 = OpConstant %2 3.1415
%1 = OpVariable %2 Uniform %3
)";
spvtest::Binary linked_binary;
EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
AssembleAndLink({body1, body2}, &linked_binary));
EXPECT_THAT(GetErrorMessage(),
HasSubstr("Unresolved external reference to \"foo\"."));
}
} // namespace
} // namespace spvtools

View File

@@ -1,148 +0,0 @@
// Copyright (c) 2019 The Khronos Group 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 "gmock/gmock.h"
#include "test/link/linker_fixture.h"
namespace spvtools {
namespace {
using TypeMatch = spvtest::LinkerTest;
// Basic types
#define PartInt(D, N) D(N) " = OpTypeInt 32 0"
#define PartFloat(D, N) D(N) " = OpTypeFloat 32"
#define PartOpaque(D, N) D(N) " = OpTypeOpaque \"bar\""
#define PartSampler(D, N) D(N) " = OpTypeSampler"
#define PartEvent(D, N) D(N) " = OpTypeEvent"
#define PartDeviceEvent(D, N) D(N) " = OpTypeDeviceEvent"
#define PartReserveId(D, N) D(N) " = OpTypeReserveId"
#define PartQueue(D, N) D(N) " = OpTypeQueue"
#define PartPipe(D, N) D(N) " = OpTypePipe ReadWrite"
#define PartPipeStorage(D, N) D(N) " = OpTypePipeStorage"
#define PartNamedBarrier(D, N) D(N) " = OpTypeNamedBarrier"
// Compound types
#define PartVector(DR, DA, N, T) DR(N) " = OpTypeVector " DA(T) " 3"
#define PartMatrix(DR, DA, N, T) DR(N) " = OpTypeMatrix " DA(T) " 4"
#define PartImage(DR, DA, N, T) \
DR(N) " = OpTypeImage " DA(T) " 2D 0 0 0 0 Rgba32f"
#define PartSampledImage(DR, DA, N, T) DR(N) " = OpTypeSampledImage " DA(T)
#define PartArray(DR, DA, N, T) DR(N) " = OpTypeArray " DA(T) " " DA(const)
#define PartRuntimeArray(DR, DA, N, T) DR(N) " = OpTypeRuntimeArray " DA(T)
#define PartStruct(DR, DA, N, T) DR(N) " = OpTypeStruct " DA(T) " " DA(T)
#define PartPointer(DR, DA, N, T) DR(N) " = OpTypePointer Workgroup " DA(T)
#define PartFunction(DR, DA, N, T) DR(N) " = OpTypeFunction " DA(T) " " DA(T)
#define CheckDecoRes(S) "[[" #S ":%\\w+]]"
#define CheckDecoArg(S) "[[" #S "]]"
#define InstDeco(S) "%" #S
#define MatchPart1(F, N) \
"; CHECK: " Part##F(CheckDecoRes, N) "\n" Part##F(InstDeco, N) "\n"
#define MatchPart2(F, N, T) \
"; CHECK: " Part##F(CheckDecoRes, CheckDecoArg, N, T) "\n" Part##F( \
InstDeco, InstDeco, N, T) "\n"
#define MatchF(N, CODE) \
TEST_F(TypeMatch, N) { \
const std::string base = \
"OpCapability Linkage\n" \
"OpCapability NamedBarrier\n" \
"OpCapability PipeStorage\n" \
"OpCapability Pipes\n" \
"OpCapability DeviceEnqueue\n" \
"OpCapability Kernel\n" \
"OpCapability Shader\n" \
"OpCapability Addresses\n" \
"OpDecorate %var LinkageAttributes \"foo\" " \
"{Import,Export}\n" \
"; CHECK: [[baseint:%\\w+]] = OpTypeInt 32 1\n" \
"%baseint = OpTypeInt 32 1\n" \
"; CHECK: [[const:%\\w+]] = OpConstant [[baseint]] 3\n" \
"%const = OpConstant %baseint 3\n" CODE \
"; CHECK: OpVariable [[type]] Uniform\n" \
"%var = OpVariable %type Uniform"; \
ExpandAndMatch(base); \
}
#define Match1(T) MatchF(Type##T, MatchPart1(T, type))
#define Match2(T, A) \
MatchF(T##OfType##A, MatchPart1(A, a) MatchPart2(T, type, a))
#define Match3(T, A, B) \
MatchF(T##Of##A##Of##B, \
MatchPart1(B, b) MatchPart2(A, a, b) MatchPart2(T, type, a))
// clang-format off
// Basic types
Match1(Int)
Match1(Float)
Match1(Opaque)
Match1(Sampler)
Match1(Event)
Match1(DeviceEvent)
Match1(ReserveId)
Match1(Queue)
Match1(Pipe)
Match1(PipeStorage)
Match1(NamedBarrier)
// Simpler (restricted) compound types
Match2(Vector, Float)
Match3(Matrix, Vector, Float)
Match2(Image, Float)
// Unrestricted compound types
#define MatchCompounds1(A) \
Match2(RuntimeArray, A) \
Match2(Struct, A) \
Match2(Pointer, A) \
Match2(Function, A) \
Match2(Array, A)
#define MatchCompounds2(A, B) \
Match3(RuntimeArray, A, B) \
Match3(Struct, A, B) \
Match3(Pointer, A, B) \
Match3(Function, A, B) \
Match3(Array, A, B)
MatchCompounds1(Float)
MatchCompounds2(Array, Float)
MatchCompounds2(RuntimeArray, Float)
MatchCompounds2(Struct, Float)
MatchCompounds2(Pointer, Float)
MatchCompounds2(Function, Float)
// clang-format on
// ForwardPointer tests, which don't fit into the previous mold
#define MatchFpF(N, CODE) \
MatchF(N, \
"; CHECK: OpTypeForwardPointer [[type:%\\w+]] Workgroup\n" \
"OpTypeForwardPointer %type Workgroup\n" CODE \
"; CHECK: [[type]] = OpTypePointer Workgroup [[realtype]]\n" \
"%type = OpTypePointer Workgroup %realtype\n")
#define MatchFp1(T) MatchFpF(ForwardPointerOf##T, MatchPart1(T, realtype))
#define MatchFp2(T, A) \
MatchFpF(ForwardPointerOf##T, MatchPart1(A, a) MatchPart2(T, realtype, a))
// clang-format off
MatchFp1(Float)
MatchFp2(Array, Float)
MatchFp2(RuntimeArray, Float)
MatchFp2(Struct, Float)
MatchFp2(Function, Float)
// clang-format on
} // namespace
} // namespace spvtools

View File

@@ -1,142 +0,0 @@
// 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 "test/link/linker_fixture.h"
namespace spvtools {
namespace {
using UniqueIds = spvtest::LinkerTest;
TEST_F(UniqueIds, UniquelyMerged) {
std::vector<std::string> bodies(2);
bodies[0] =
// clang-format off
"OpCapability Shader\n"
"%1 = OpExtInstImport \"GLSL.std.450\"\n"
"OpMemoryModel Logical GLSL450\n"
"OpEntryPoint Vertex %main \"main\"\n"
"OpSource ESSL 310\n"
"OpName %main \"main\"\n"
"OpName %f_ \"f(\"\n"
"OpName %gv1 \"gv1\"\n"
"OpName %gv2 \"gv2\"\n"
"OpName %lv1 \"lv1\"\n"
"OpName %lv2 \"lv2\"\n"
"OpName %lv1_0 \"lv1\"\n"
"%void = OpTypeVoid\n"
"%10 = OpTypeFunction %void\n"
"%float = OpTypeFloat 32\n"
"%12 = OpTypeFunction %float\n"
"%_ptr_Private_float = OpTypePointer Private %float\n"
"%gv1 = OpVariable %_ptr_Private_float Private\n"
"%float_10 = OpConstant %float 10\n"
"%gv2 = OpVariable %_ptr_Private_float Private\n"
"%float_100 = OpConstant %float 100\n"
"%_ptr_Function_float = OpTypePointer Function %float\n"
"%main = OpFunction %void None %10\n"
"%17 = OpLabel\n"
"%lv1_0 = OpVariable %_ptr_Function_float Function\n"
"OpStore %gv1 %float_10\n"
"OpStore %gv2 %float_100\n"
"%18 = OpLoad %float %gv1\n"
"%19 = OpLoad %float %gv2\n"
"%20 = OpFSub %float %18 %19\n"
"OpStore %lv1_0 %20\n"
"OpReturn\n"
"OpFunctionEnd\n"
"%f_ = OpFunction %float None %12\n"
"%21 = OpLabel\n"
"%lv1 = OpVariable %_ptr_Function_float Function\n"
"%lv2 = OpVariable %_ptr_Function_float Function\n"
"%22 = OpLoad %float %gv1\n"
"%23 = OpLoad %float %gv2\n"
"%24 = OpFAdd %float %22 %23\n"
"OpStore %lv1 %24\n"
"%25 = OpLoad %float %gv1\n"
"%26 = OpLoad %float %gv2\n"
"%27 = OpFMul %float %25 %26\n"
"OpStore %lv2 %27\n"
"%28 = OpLoad %float %lv1\n"
"%29 = OpLoad %float %lv2\n"
"%30 = OpFDiv %float %28 %29\n"
"OpReturnValue %30\n"
"OpFunctionEnd\n";
// clang-format on
bodies[1] =
// clang-format off
"OpCapability Shader\n"
"%1 = OpExtInstImport \"GLSL.std.450\"\n"
"OpMemoryModel Logical GLSL450\n"
"OpSource ESSL 310\n"
"OpName %main \"main2\"\n"
"OpName %f_ \"f(\"\n"
"OpName %gv1 \"gv12\"\n"
"OpName %gv2 \"gv22\"\n"
"OpName %lv1 \"lv12\"\n"
"OpName %lv2 \"lv22\"\n"
"OpName %lv1_0 \"lv12\"\n"
"%void = OpTypeVoid\n"
"%10 = OpTypeFunction %void\n"
"%float = OpTypeFloat 32\n"
"%12 = OpTypeFunction %float\n"
"%_ptr_Private_float = OpTypePointer Private %float\n"
"%gv1 = OpVariable %_ptr_Private_float Private\n"
"%float_10 = OpConstant %float 10\n"
"%gv2 = OpVariable %_ptr_Private_float Private\n"
"%float_100 = OpConstant %float 100\n"
"%_ptr_Function_float = OpTypePointer Function %float\n"
"%main = OpFunction %void None %10\n"
"%17 = OpLabel\n"
"%lv1_0 = OpVariable %_ptr_Function_float Function\n"
"OpStore %gv1 %float_10\n"
"OpStore %gv2 %float_100\n"
"%18 = OpLoad %float %gv1\n"
"%19 = OpLoad %float %gv2\n"
"%20 = OpFSub %float %18 %19\n"
"OpStore %lv1_0 %20\n"
"OpReturn\n"
"OpFunctionEnd\n"
"%f_ = OpFunction %float None %12\n"
"%21 = OpLabel\n"
"%lv1 = OpVariable %_ptr_Function_float Function\n"
"%lv2 = OpVariable %_ptr_Function_float Function\n"
"%22 = OpLoad %float %gv1\n"
"%23 = OpLoad %float %gv2\n"
"%24 = OpFAdd %float %22 %23\n"
"OpStore %lv1 %24\n"
"%25 = OpLoad %float %gv1\n"
"%26 = OpLoad %float %gv2\n"
"%27 = OpFMul %float %25 %26\n"
"OpStore %lv2 %27\n"
"%28 = OpLoad %float %lv1\n"
"%29 = OpLoad %float %lv2\n"
"%30 = OpFDiv %float %28 %29\n"
"OpReturnValue %30\n"
"OpFunctionEnd\n";
// clang-format on
spvtest::Binary linked_binary;
LinkerOptions options;
options.SetVerifyIds(true);
spv_result_t res = AssembleAndLink(bodies, &linked_binary, options);
EXPECT_EQ(SPV_SUCCESS, res);
}
} // namespace
} // namespace spvtools

View File

@@ -1,53 +0,0 @@
// Copyright (c) 2016 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 "source/opt/log.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
namespace spvtools {
namespace {
using ::testing::MatchesRegex;
TEST(Log, Unimplemented) {
int invocation = 0;
auto consumer = [&invocation](spv_message_level_t level, const char* source,
const spv_position_t&, const char* message) {
++invocation;
EXPECT_EQ(SPV_MSG_INTERNAL_ERROR, level);
EXPECT_THAT(source, MatchesRegex(".*log_test.cpp$"));
EXPECT_STREQ("unimplemented: the-ultimite-feature", message);
};
SPIRV_UNIMPLEMENTED(consumer, "the-ultimite-feature");
EXPECT_EQ(1, invocation);
}
TEST(Log, Unreachable) {
int invocation = 0;
auto consumer = [&invocation](spv_message_level_t level, const char* source,
const spv_position_t&, const char* message) {
++invocation;
EXPECT_EQ(SPV_MSG_INTERNAL_ERROR, level);
EXPECT_THAT(source, MatchesRegex(".*log_test.cpp$"));
EXPECT_STREQ("unreachable", message);
};
SPIRV_UNREACHABLE(consumer);
EXPECT_EQ(1, invocation);
}
} // namespace
} // namespace spvtools

View File

@@ -1,347 +0,0 @@
// Copyright (c) 2016 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 "source/name_mapper.h"
#include "test/test_fixture.h"
#include "test/unit_spirv.h"
namespace spvtools {
namespace {
using spvtest::ScopedContext;
using ::testing::Eq;
TEST(TrivialNameTest, Samples) {
auto mapper = GetTrivialNameMapper();
EXPECT_EQ(mapper(1), "1");
EXPECT_EQ(mapper(1999), "1999");
EXPECT_EQ(mapper(1024), "1024");
}
// A test case for the name mappers that actually look at an assembled module.
struct NameIdCase {
std::string assembly; // Input assembly text
uint32_t id;
std::string expected_name;
};
using FriendlyNameTest =
spvtest::TextToBinaryTestBase<::testing::TestWithParam<NameIdCase>>;
TEST_P(FriendlyNameTest, SingleMapping) {
ScopedContext context(SPV_ENV_UNIVERSAL_1_1);
auto words = CompileSuccessfully(GetParam().assembly, SPV_ENV_UNIVERSAL_1_1);
auto friendly_mapper =
FriendlyNameMapper(context.context, words.data(), words.size());
NameMapper mapper = friendly_mapper.GetNameMapper();
EXPECT_THAT(mapper(GetParam().id), Eq(GetParam().expected_name))
<< GetParam().assembly << std::endl
<< " for id " << GetParam().id;
}
INSTANTIATE_TEST_SUITE_P(ScalarType, FriendlyNameTest,
::testing::ValuesIn(std::vector<NameIdCase>{
{"%1 = OpTypeVoid", 1, "void"},
{"%1 = OpTypeBool", 1, "bool"},
{"%1 = OpTypeInt 8 0", 1, "uchar"},
{"%1 = OpTypeInt 8 1", 1, "char"},
{"%1 = OpTypeInt 16 0", 1, "ushort"},
{"%1 = OpTypeInt 16 1", 1, "short"},
{"%1 = OpTypeInt 32 0", 1, "uint"},
{"%1 = OpTypeInt 32 1", 1, "int"},
{"%1 = OpTypeInt 64 0", 1, "ulong"},
{"%1 = OpTypeInt 64 1", 1, "long"},
{"%1 = OpTypeInt 1 0", 1, "u1"},
{"%1 = OpTypeInt 1 1", 1, "i1"},
{"%1 = OpTypeInt 33 0", 1, "u33"},
{"%1 = OpTypeInt 33 1", 1, "i33"},
{"%1 = OpTypeFloat 16", 1, "half"},
{"%1 = OpTypeFloat 32", 1, "float"},
{"%1 = OpTypeFloat 64", 1, "double"},
{"%1 = OpTypeFloat 10", 1, "fp10"},
{"%1 = OpTypeFloat 55", 1, "fp55"},
}));
INSTANTIATE_TEST_SUITE_P(
VectorType, FriendlyNameTest,
::testing::ValuesIn(std::vector<NameIdCase>{
{"%1 = OpTypeBool %2 = OpTypeVector %1 1", 2, "v1bool"},
{"%1 = OpTypeBool %2 = OpTypeVector %1 2", 2, "v2bool"},
{"%1 = OpTypeBool %2 = OpTypeVector %1 3", 2, "v3bool"},
{"%1 = OpTypeBool %2 = OpTypeVector %1 4", 2, "v4bool"},
{"%1 = OpTypeInt 8 0 %2 = OpTypeVector %1 2", 2, "v2uchar"},
{"%1 = OpTypeInt 16 1 %2 = OpTypeVector %1 3", 2, "v3short"},
{"%1 = OpTypeInt 32 0 %2 = OpTypeVector %1 4", 2, "v4uint"},
{"%1 = OpTypeInt 64 1 %2 = OpTypeVector %1 3", 2, "v3long"},
{"%1 = OpTypeInt 20 0 %2 = OpTypeVector %1 4", 2, "v4u20"},
{"%1 = OpTypeInt 21 1 %2 = OpTypeVector %1 3", 2, "v3i21"},
{"%1 = OpTypeFloat 32 %2 = OpTypeVector %1 2", 2, "v2float"},
// OpName overrides the element name.
{"OpName %1 \"time\" %1 = OpTypeFloat 32 %2 = OpTypeVector %1 2", 2,
"v2time"},
}));
INSTANTIATE_TEST_SUITE_P(
MatrixType, FriendlyNameTest,
::testing::ValuesIn(std::vector<NameIdCase>{
{"%1 = OpTypeBool %2 = OpTypeVector %1 2 %3 = OpTypeMatrix %2 2", 3,
"mat2v2bool"},
{"%1 = OpTypeFloat 32 %2 = OpTypeVector %1 2 %3 = OpTypeMatrix %2 3", 3,
"mat3v2float"},
{"%1 = OpTypeFloat 32 %2 = OpTypeVector %1 2 %3 = OpTypeMatrix %2 4", 3,
"mat4v2float"},
{"OpName %1 \"time\" %1 = OpTypeFloat 32 %2 = OpTypeVector %1 2 %3 = "
"OpTypeMatrix %2 4",
3, "mat4v2time"},
{"OpName %2 \"lat_long\" %1 = OpTypeFloat 32 %2 = OpTypeVector %1 2 %3 "
"= OpTypeMatrix %2 4",
3, "mat4lat_long"},
}));
INSTANTIATE_TEST_SUITE_P(
OpName, FriendlyNameTest,
::testing::ValuesIn(std::vector<NameIdCase>{
{"OpName %1 \"abcdefg\"", 1, "abcdefg"},
{"OpName %1 \"Hello world!\"", 1, "Hello_world_"},
{"OpName %1 \"0123456789\"", 1, "0123456789"},
{"OpName %1 \"_\"", 1, "_"},
// An empty string is not valid for SPIR-V assembly IDs.
{"OpName %1 \"\"", 1, "_"},
// Test uniqueness when presented with things mapping to "_"
{"OpName %1 \"\" OpName %2 \"\"", 1, "_"},
{"OpName %1 \"\" OpName %2 \"\"", 2, "__0"},
{"OpName %1 \"\" OpName %2 \"\" OpName %3 \"_\"", 3, "__1"},
// Test uniqueness of names that are forced to be
// numbers.
{"OpName %1 \"2\" OpName %2 \"2\"", 1, "2"},
{"OpName %1 \"2\" OpName %2 \"2\"", 2, "2_0"},
// Test uniqueness in the face of forward references
// for Ids that don't already have friendly names.
// In particular, the first OpDecorate assigns the name, and
// the second one can't override it.
{"OpDecorate %1 Volatile OpDecorate %1 Restrict", 1, "1"},
// But a forced name can override the name that
// would have been assigned via the OpDecorate
// forward reference.
{"OpName %1 \"mememe\" OpDecorate %1 Volatile OpDecorate %1 Restrict",
1, "mememe"},
// OpName can override other inferences. We assume valid instruction
// ordering, where OpName precedes type definitions.
{"OpName %1 \"myfloat\" %1 = OpTypeFloat 32", 1, "myfloat"},
}));
INSTANTIATE_TEST_SUITE_P(
UniquenessHeuristic, FriendlyNameTest,
::testing::ValuesIn(std::vector<NameIdCase>{
{"%1 = OpTypeVoid %2 = OpTypeVoid %3 = OpTypeVoid", 1, "void"},
{"%1 = OpTypeVoid %2 = OpTypeVoid %3 = OpTypeVoid", 2, "void_0"},
{"%1 = OpTypeVoid %2 = OpTypeVoid %3 = OpTypeVoid", 3, "void_1"},
}));
INSTANTIATE_TEST_SUITE_P(Arrays, FriendlyNameTest,
::testing::ValuesIn(std::vector<NameIdCase>{
{"OpName %2 \"FortyTwo\" %1 = OpTypeFloat 32 "
"%2 = OpConstant %1 42 %3 = OpTypeArray %1 %2",
3, "_arr_float_FortyTwo"},
{"%1 = OpTypeInt 32 0 "
"%2 = OpTypeRuntimeArray %1",
2, "_runtimearr_uint"},
}));
INSTANTIATE_TEST_SUITE_P(Structs, FriendlyNameTest,
::testing::ValuesIn(std::vector<NameIdCase>{
{"%1 = OpTypeBool "
"%2 = OpTypeStruct %1 %1 %1",
2, "_struct_2"},
{"%1 = OpTypeBool "
"%2 = OpTypeStruct %1 %1 %1 "
"%3 = OpTypeStruct %2 %2",
3, "_struct_3"},
}));
INSTANTIATE_TEST_SUITE_P(
Pointer, FriendlyNameTest,
::testing::ValuesIn(std::vector<NameIdCase>{
{"%1 = OpTypeFloat 32 %2 = OpTypePointer Workgroup %1", 2,
"_ptr_Workgroup_float"},
{"%1 = OpTypeBool %2 = OpTypePointer Private %1", 2,
"_ptr_Private_bool"},
// OpTypeForwardPointer doesn't force generation of the name for its
// target type.
{"%1 = OpTypeBool OpTypeForwardPointer %2 Private %2 = OpTypePointer "
"Private %1",
2, "_ptr_Private_bool"},
}));
INSTANTIATE_TEST_SUITE_P(ExoticTypes, FriendlyNameTest,
::testing::ValuesIn(std::vector<NameIdCase>{
{"%1 = OpTypeEvent", 1, "Event"},
{"%1 = OpTypeDeviceEvent", 1, "DeviceEvent"},
{"%1 = OpTypeReserveId", 1, "ReserveId"},
{"%1 = OpTypeQueue", 1, "Queue"},
{"%1 = OpTypeOpaque \"hello world!\"", 1,
"Opaque_hello_world_"},
{"%1 = OpTypePipe ReadOnly", 1, "PipeReadOnly"},
{"%1 = OpTypePipe WriteOnly", 1, "PipeWriteOnly"},
{"%1 = OpTypePipe ReadWrite", 1, "PipeReadWrite"},
{"%1 = OpTypePipeStorage", 1, "PipeStorage"},
{"%1 = OpTypeNamedBarrier", 1, "NamedBarrier"},
}));
// Makes a test case for a BuiltIn variable declaration.
NameIdCase BuiltInCase(std::string assembly_name, std::string expected) {
return NameIdCase{std::string("OpDecorate %1 BuiltIn ") + assembly_name +
" %1 = OpVariable %2 Input",
1, expected};
}
// Makes a test case for a BuiltIn variable declaration. In this overload,
// the expected result is the same as the assembly name.
NameIdCase BuiltInCase(std::string assembly_name) {
return BuiltInCase(assembly_name, assembly_name);
}
// Makes a test case for a BuiltIn variable declaration. In this overload,
// the expected result is the same as the assembly name, but with a "gl_"
// prefix.
NameIdCase BuiltInGLCase(std::string assembly_name) {
return BuiltInCase(assembly_name, std::string("gl_") + assembly_name);
}
INSTANTIATE_TEST_SUITE_P(
BuiltIns, FriendlyNameTest,
::testing::ValuesIn(std::vector<NameIdCase>{
BuiltInGLCase("Position"),
BuiltInGLCase("PointSize"),
BuiltInGLCase("ClipDistance"),
BuiltInGLCase("CullDistance"),
BuiltInCase("VertexId", "gl_VertexID"),
BuiltInCase("InstanceId", "gl_InstanceID"),
BuiltInCase("PrimitiveId", "gl_PrimitiveID"),
BuiltInCase("InvocationId", "gl_InvocationID"),
BuiltInGLCase("Layer"),
BuiltInGLCase("ViewportIndex"),
BuiltInGLCase("TessLevelOuter"),
BuiltInGLCase("TessLevelInner"),
BuiltInGLCase("TessCoord"),
BuiltInGLCase("PatchVertices"),
BuiltInGLCase("FragCoord"),
BuiltInGLCase("PointCoord"),
BuiltInGLCase("FrontFacing"),
BuiltInCase("SampleId", "gl_SampleID"),
BuiltInGLCase("SamplePosition"),
BuiltInGLCase("SampleMask"),
BuiltInGLCase("FragDepth"),
BuiltInGLCase("HelperInvocation"),
BuiltInCase("NumWorkgroups", "gl_NumWorkGroups"),
BuiltInCase("WorkgroupSize", "gl_WorkGroupSize"),
BuiltInCase("WorkgroupId", "gl_WorkGroupID"),
BuiltInCase("LocalInvocationId", "gl_LocalInvocationID"),
BuiltInCase("GlobalInvocationId", "gl_GlobalInvocationID"),
BuiltInGLCase("LocalInvocationIndex"),
BuiltInCase("WorkDim"),
BuiltInCase("GlobalSize"),
BuiltInCase("EnqueuedWorkgroupSize"),
BuiltInCase("GlobalOffset"),
BuiltInCase("GlobalLinearId"),
BuiltInCase("SubgroupSize"),
BuiltInCase("SubgroupMaxSize"),
BuiltInCase("NumSubgroups"),
BuiltInCase("NumEnqueuedSubgroups"),
BuiltInCase("SubgroupId"),
BuiltInCase("SubgroupLocalInvocationId"),
BuiltInGLCase("VertexIndex"),
BuiltInGLCase("InstanceIndex"),
BuiltInCase("SubgroupEqMaskKHR"),
BuiltInCase("SubgroupGeMaskKHR"),
BuiltInCase("SubgroupGtMaskKHR"),
BuiltInCase("SubgroupLeMaskKHR"),
BuiltInCase("SubgroupLtMaskKHR"),
}));
INSTANTIATE_TEST_SUITE_P(DebugNameOverridesBuiltin, FriendlyNameTest,
::testing::ValuesIn(std::vector<NameIdCase>{
{"OpName %1 \"foo\" OpDecorate %1 BuiltIn WorkDim "
"%1 = OpVariable %2 Input",
1, "foo"}}));
INSTANTIATE_TEST_SUITE_P(
SimpleIntegralConstants, FriendlyNameTest,
::testing::ValuesIn(std::vector<NameIdCase>{
{"%1 = OpTypeInt 32 0 %2 = OpConstant %1 0", 2, "uint_0"},
{"%1 = OpTypeInt 32 0 %2 = OpConstant %1 1", 2, "uint_1"},
{"%1 = OpTypeInt 32 0 %2 = OpConstant %1 2", 2, "uint_2"},
{"%1 = OpTypeInt 32 0 %2 = OpConstant %1 9", 2, "uint_9"},
{"%1 = OpTypeInt 32 0 %2 = OpConstant %1 42", 2, "uint_42"},
{"%1 = OpTypeInt 32 1 %2 = OpConstant %1 0", 2, "int_0"},
{"%1 = OpTypeInt 32 1 %2 = OpConstant %1 1", 2, "int_1"},
{"%1 = OpTypeInt 32 1 %2 = OpConstant %1 2", 2, "int_2"},
{"%1 = OpTypeInt 32 1 %2 = OpConstant %1 9", 2, "int_9"},
{"%1 = OpTypeInt 32 1 %2 = OpConstant %1 42", 2, "int_42"},
{"%1 = OpTypeInt 32 1 %2 = OpConstant %1 -42", 2, "int_n42"},
// Exotic bit widths
{"%1 = OpTypeInt 33 0 %2 = OpConstant %1 0", 2, "u33_0"},
{"%1 = OpTypeInt 33 1 %2 = OpConstant %1 10", 2, "i33_10"},
{"%1 = OpTypeInt 33 1 %2 = OpConstant %1 -19", 2, "i33_n19"},
}));
INSTANTIATE_TEST_SUITE_P(
SimpleFloatConstants, FriendlyNameTest,
::testing::ValuesIn(std::vector<NameIdCase>{
{"%1 = OpTypeFloat 16\n%2 = OpConstant %1 0x1.ff4p+16", 2,
"half_0x1_ff4p_16"},
{"%1 = OpTypeFloat 16\n%2 = OpConstant %1 -0x1.d2cp-10", 2,
"half_n0x1_d2cpn10"},
// 32-bit floats
{"%1 = OpTypeFloat 32\n%2 = OpConstant %1 -3.125", 2, "float_n3_125"},
{"%1 = OpTypeFloat 32\n%2 = OpConstant %1 0x1.8p+128", 2,
"float_0x1_8p_128"}, // NaN
{"%1 = OpTypeFloat 32\n%2 = OpConstant %1 -0x1.0002p+128", 2,
"float_n0x1_0002p_128"}, // NaN
{"%1 = OpTypeFloat 32\n%2 = OpConstant %1 0x1p+128", 2,
"float_0x1p_128"}, // Inf
{"%1 = OpTypeFloat 32\n%2 = OpConstant %1 -0x1p+128", 2,
"float_n0x1p_128"}, // -Inf
// 64-bit floats
{"%1 = OpTypeFloat 64\n%2 = OpConstant %1 -3.125", 2, "double_n3_125"},
{"%1 = OpTypeFloat 64\n%2 = OpConstant %1 0x1.ffffffffffffap-1023", 2,
"double_0x1_ffffffffffffapn1023"}, // small normal
{"%1 = OpTypeFloat 64\n%2 = OpConstant %1 -0x1.ffffffffffffap-1023", 2,
"double_n0x1_ffffffffffffapn1023"},
{"%1 = OpTypeFloat 64\n%2 = OpConstant %1 0x1.8p+1024", 2,
"double_0x1_8p_1024"}, // NaN
{"%1 = OpTypeFloat 64\n%2 = OpConstant %1 -0x1.0002p+1024", 2,
"double_n0x1_0002p_1024"}, // NaN
{"%1 = OpTypeFloat 64\n%2 = OpConstant %1 0x1p+1024", 2,
"double_0x1p_1024"}, // Inf
{"%1 = OpTypeFloat 64\n%2 = OpConstant %1 -0x1p+1024", 2,
"double_n0x1p_1024"}, // -Inf
}));
INSTANTIATE_TEST_SUITE_P(
BooleanConstants, FriendlyNameTest,
::testing::ValuesIn(std::vector<NameIdCase>{
{"%1 = OpTypeBool\n%2 = OpConstantTrue %1", 2, "true"},
{"%1 = OpTypeBool\n%2 = OpConstantFalse %1", 2, "false"},
}));
} // namespace
} // namespace spvtools

View File

@@ -1,87 +0,0 @@
// Copyright (c) 2015-2016 The Khronos Group 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 "test/test_fixture.h"
#include "test/unit_spirv.h"
namespace spvtools {
namespace {
using NamedIdTest = spvtest::TextToBinaryTest;
TEST_F(NamedIdTest, Default) {
const std::string input = R"(
OpCapability Shader
OpMemoryModel Logical Simple
OpEntryPoint Vertex %main "foo"
%void = OpTypeVoid
%fnMain = OpTypeFunction %void
%main = OpFunction %void None %fnMain
%lbMain = OpLabel
OpReturn
OpFunctionEnd)";
const std::string output =
"OpCapability Shader\n"
"OpMemoryModel Logical Simple\n"
"OpEntryPoint Vertex %1 \"foo\"\n"
"%2 = OpTypeVoid\n"
"%3 = OpTypeFunction %2\n"
"%1 = OpFunction %2 None %3\n"
"%4 = OpLabel\n"
"OpReturn\n"
"OpFunctionEnd\n";
EXPECT_EQ(output, EncodeAndDecodeSuccessfully(input));
}
struct IdCheckCase {
std::string id;
bool valid;
};
using IdValidityTest =
spvtest::TextToBinaryTestBase<::testing::TestWithParam<IdCheckCase>>;
TEST_P(IdValidityTest, IdTypes) {
const std::string input = GetParam().id + " = OpTypeVoid";
SetText(input);
if (GetParam().valid) {
CompileSuccessfully(input);
} else {
CompileFailure(input);
}
}
INSTANTIATE_TEST_SUITE_P(
ValidAndInvalidIds, IdValidityTest,
::testing::ValuesIn(std::vector<IdCheckCase>(
{{"%1", true}, {"%2abc", true}, {"%3Def", true},
{"%4GHI", true}, {"%5_j_k", true}, {"%6J_M", true},
{"%n", true}, {"%O", true}, {"%p7", true},
{"%Q8", true}, {"%R_S", true}, {"%T_10_U", true},
{"%V_11", true}, {"%W_X_13", true}, {"%_A", true},
{"%_", true}, {"%__", true}, {"%A_", true},
{"%_A_", true},
{"%@", false}, {"%!", false}, {"%ABC!", false},
{"%__A__@", false}, {"%%", false}, {"%-", false},
{"%foo_@_bar", false}, {"%", false},
{"5", false}, {"32", false}, {"foo", false},
{"a%bar", false}})));
} // namespace
} // namespace spvtools

View File

@@ -1,44 +0,0 @@
// Copyright (c) 2015-2016 The Khronos Group 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 "test/unit_spirv.h"
namespace spvtools {
namespace {
// A sampling of word counts. Covers extreme points well, and all bit
// positions, and some combinations of bit positions.
const uint16_t kSampleWordCounts[] = {
0, 1, 2, 3, 4, 8, 16, 32, 64, 127, 128,
256, 511, 512, 1024, 2048, 4096, 8192, 16384, 32768, 0xfffe, 0xffff};
// A sampling of opcode values. Covers the lower values well, a few samples
// around the number of core instructions (as of this writing), and some
// higher values.
const uint16_t kSampleOpcodes[] = {0, 1, 2, 3, 4, 100,
300, 305, 1023, 0xfffe, 0xffff};
TEST(OpcodeMake, Samples) {
for (auto wordCount : kSampleWordCounts) {
for (auto opcode : kSampleOpcodes) {
uint32_t word = 0;
word |= uint32_t(opcode);
word |= uint32_t(wordCount) << 16;
EXPECT_EQ(word, spvOpcodeMake(wordCount, SpvOp(opcode)));
}
}
}
} // namespace
} // namespace spvtools

View File

@@ -1,78 +0,0 @@
// Copyright (c) 2015-2016 The Khronos Group 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 "test/unit_spirv.h"
#include "source/enum_set.h"
namespace spvtools {
namespace {
using spvtest::ElementsIn;
// Capabilities required by an Opcode.
struct ExpectedOpCodeCapabilities {
SpvOp opcode;
CapabilitySet capabilities;
};
using OpcodeTableCapabilitiesTest =
::testing::TestWithParam<ExpectedOpCodeCapabilities>;
TEST_P(OpcodeTableCapabilitiesTest, TableEntryMatchesExpectedCapabilities) {
auto env = SPV_ENV_UNIVERSAL_1_1;
spv_opcode_table opcodeTable;
ASSERT_EQ(SPV_SUCCESS, spvOpcodeTableGet(&opcodeTable, env));
spv_opcode_desc entry;
ASSERT_EQ(SPV_SUCCESS, spvOpcodeTableValueLookup(env, opcodeTable,
GetParam().opcode, &entry));
EXPECT_EQ(
ElementsIn(GetParam().capabilities),
ElementsIn(CapabilitySet(entry->numCapabilities, entry->capabilities)));
}
INSTANTIATE_TEST_SUITE_P(
TableRowTest, OpcodeTableCapabilitiesTest,
// Spot-check a few opcodes.
::testing::Values(
ExpectedOpCodeCapabilities{
SpvOpImageQuerySize,
CapabilitySet{SpvCapabilityKernel, SpvCapabilityImageQuery}},
ExpectedOpCodeCapabilities{
SpvOpImageQuerySizeLod,
CapabilitySet{SpvCapabilityKernel, SpvCapabilityImageQuery}},
ExpectedOpCodeCapabilities{
SpvOpImageQueryLevels,
CapabilitySet{SpvCapabilityKernel, SpvCapabilityImageQuery}},
ExpectedOpCodeCapabilities{
SpvOpImageQuerySamples,
CapabilitySet{SpvCapabilityKernel, SpvCapabilityImageQuery}},
ExpectedOpCodeCapabilities{SpvOpImageSparseSampleImplicitLod,
CapabilitySet{SpvCapabilitySparseResidency}},
ExpectedOpCodeCapabilities{SpvOpCopyMemorySized,
CapabilitySet{SpvCapabilityAddresses}},
ExpectedOpCodeCapabilities{SpvOpArrayLength,
CapabilitySet{SpvCapabilityShader}},
ExpectedOpCodeCapabilities{SpvOpFunction, CapabilitySet()},
ExpectedOpCodeCapabilities{SpvOpConvertFToS, CapabilitySet()},
ExpectedOpCodeCapabilities{SpvOpEmitStreamVertex,
CapabilitySet{SpvCapabilityGeometryStreams}},
ExpectedOpCodeCapabilities{SpvOpTypeNamedBarrier,
CapabilitySet{SpvCapabilityNamedBarrier}},
ExpectedOpCodeCapabilities{
SpvOpGetKernelMaxNumSubgroups,
CapabilitySet{SpvCapabilitySubgroupDispatch}}));
} // namespace
} // namespace spvtools

View File

@@ -1,30 +0,0 @@
// Copyright (c) 2015-2016 The Khronos Group 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 "test/unit_spirv.h"
namespace spvtools {
namespace {
TEST(OpcodeSplit, Default) {
uint32_t word = spvOpcodeMake(42, (SpvOp)23);
uint16_t wordCount = 0;
uint16_t opcode;
spvOpcodeSplit(word, &wordCount, &opcode);
ASSERT_EQ(42, wordCount);
ASSERT_EQ(23, opcode);
}
} // namespace
} // namespace spvtools

View File

@@ -1,39 +0,0 @@
// Copyright (c) 2015-2016 The Khronos Group 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 "gmock/gmock.h"
#include "test/unit_spirv.h"
namespace spvtools {
namespace {
using GetTargetOpcodeTableGetTest = ::testing::TestWithParam<spv_target_env>;
using ::testing::ValuesIn;
TEST_P(GetTargetOpcodeTableGetTest, SanityCheck) {
spv_opcode_table table;
ASSERT_EQ(SPV_SUCCESS, spvOpcodeTableGet(&table, GetParam()));
ASSERT_NE(0u, table->count);
ASSERT_NE(nullptr, table->entries);
}
TEST_P(GetTargetOpcodeTableGetTest, InvalidPointerTable) {
ASSERT_EQ(SPV_ERROR_INVALID_POINTER, spvOpcodeTableGet(nullptr, GetParam()));
}
INSTANTIATE_TEST_SUITE_P(OpcodeTableGet, GetTargetOpcodeTableGetTest,
ValuesIn(spvtest::AllTargetEnvironments()));
} // namespace
} // namespace spvtools

View File

@@ -1,43 +0,0 @@
Operand class,Example instruction,Notes,example unit test,negative-enum coverage location
" OperandNone,",UNUSED,not in grammar,,not enum
" OperandId,",many,ID,too many to count,not enum
" OperandOptionalId,","Source, Variable",OPTIONAL_ID,OpSourceAcceptsOptionalFileId,not enum
" OperandOptionalImage,",ImageFetch,,ImageOperandsTest,"TEST_F(ImageOperandsTest, WrongOperand)"
" OperandVariableIds,",ExtInst,,,not enum
" OperandOptionalLiteral,",ExecutionMode,,AnyExecutionMode,not enum
" OperandOptionalLiteralString,",Source,,OpSourceAcceptsOptionalSourceText,not enum
" OperandVariableLiterals,",Decorate,,OpDecorateSimpleTest,not enum
" OperandVariableIdLiteral,",GroupMemberDecorate,,GroupMemberDecorate*,not enum
" OperandVariableLiteralId,",Switch,,Switch*,not enum
" OperandLiteralNumber,","Source, Switch, ...",,Switch*,not enum
" OperandLiteralString,",SourceContinued,,OpSourceContinued,not enum
" OperandSource,",Source,,OpSource,not enum
" OperandExecutionModel,",EntryPoint,,OpEntryPointTest,"TEST_F(OpEntryPointTest, WrongModel)"
" OperandAddressing,",OpMemoryModel,,OpMemoryModelTest,"TEST_F(OpMemoryModelTest, WrongModel)"
" OperandMemory,",OpMemoryModel,,OpMemoryModelTest,"TEST_F(OpMemoryModelTest, WrongModel)"
" OperandExecutionMode,",OpExecutionMode,,OpExecutionModeTest,"TEST_F(OpExecutionModeTest, WrongMode)"
" OperandStorage,","TypePointer, TypeForwardPointer, Variable",,StorageClassTest,"TEST_F(OpTypeForwardPointerTest, WrongClass)"
" OperandDimensionality,",TypeImage,,DimTest/AnyDim,"TEST_F(DimTest, WrongDim)"
" OperandSamplerAddressingMode,",ConstantSampler,,SamplerAddressingModeTest,"TEST_F(SamplerAddressingModeTest, WrongMode)"
" OperandSamplerFilterMode,",ConstantSampler,,AnySamplerFilterMode,"TEST_F(SamplerFilterModeTest, WrongMode)"
" OperandSamplerImageFormat,",TypeImage,SAMPLER_IMAGE_FORMAT,ImageFormatTest,"TEST_F(ImageFormatTest, WrongFormat)"
" OperandImageChannelOrder,",UNUSED,returned as result value only,,
" OperandImageChannelDataType,",UNUSED,returned as result value only,,
" OperandImageOperands,",UNUSED,used to make a spec section,,see OperandOptionalImage
" OperandFPFastMath,",OpDecorate,,CombinedFPFastMathMask,"TEST_F(OpDecorateEnumTest, WrongFPFastMathMode)"
" OperandFPRoundingMode,",OpDecorate,,,"TEST_F(OpDecorateEnumTest, WrongFPRoundingMode)"
" OperandLinkageType,",OpDecorate,,OpDecorateLinkageTest,"TEST_F(OpDecorateLinkageTest, WrongType)"
" OperandAccessQualifier,",OpTypePipe,,AnyAccessQualifier,"TEST_F(OpTypePipeTest, WrongAccessQualifier)"
" OperandFuncParamAttr,",OpDecorate,,TextToBinaryDecorateFuncParamAttr,"TEST_F(OpDecorateEnumTest, WrongFuncParamAttr)"
" OperandDecoration,",OpDecorate,,AnyAccessQualifier,"TEST_F(OpTypePipeTest, WrongAccessQualifier)"
" OperandBuiltIn,",OpDecorate,,TextToBinaryDecorateBultIn,"TEST_F(OpDecorateEnumTest, WrongBuiltIn)"
" OperandSelect,",SelectionMerge,,TextToBinarySelectionMerge,"TEST_F(OpSelectionMergeTest, WrongSelectionControl)"
" OperandLoop,",LoopMerge,,CombinedLoopControlMask,"TEST_F(OpLoopMergeTest, WrongLoopControl)"
" OperandFunction,",Function,,AnySingleFunctionControlMask,"TEST_F(OpFunctionControlTest, WrongFunctionControl)"
" OperandMemorySemantics,",OpMemoryBarrier,"it's an ID, not in grammar",OpMemoryBarrier*,not enum
" OperandMemoryAccess,",UNUSED,"should be on opstore, but hacked in opcode.cpp",,not enum
" OperandScope,",MemoryBarrier,"it's an ID, not in grammar",OpMemoryBarrier*,not enum
" OperandGroupOperation,",GroupIAdd,,GroupOperationTest,"TEST_F(GroupOperationTest, WrongGroupOperation)"
" OperandKernelEnqueueFlags,",OpEnqueueKernel,"it's an ID, not in grammar",should not have one,not enum
" OperandKernelProfilingInfo,",OpCaptureEventProfilingInfo,"it's an ID, not in grammar",should not have one,not enum
" OperandCapability,",Capability,,OpCapabilityTest,"TEST_F(TextToBinaryCapability, BadInvalidCapability)"
1 Operand class Example instruction Notes example unit test negative-enum coverage location
2 OperandNone, UNUSED not in grammar not enum
3 OperandId, many ID too many to count not enum
4 OperandOptionalId, Source, Variable OPTIONAL_ID OpSourceAcceptsOptionalFileId not enum
5 OperandOptionalImage, ImageFetch ImageOperandsTest TEST_F(ImageOperandsTest, WrongOperand)
6 OperandVariableIds, ExtInst not enum
7 OperandOptionalLiteral, ExecutionMode AnyExecutionMode not enum
8 OperandOptionalLiteralString, Source OpSourceAcceptsOptionalSourceText not enum
9 OperandVariableLiterals, Decorate OpDecorateSimpleTest not enum
10 OperandVariableIdLiteral, GroupMemberDecorate GroupMemberDecorate* not enum
11 OperandVariableLiteralId, Switch Switch* not enum
12 OperandLiteralNumber, Source, Switch, ... Switch* not enum
13 OperandLiteralString, SourceContinued OpSourceContinued not enum
14 OperandSource, Source OpSource not enum
15 OperandExecutionModel, EntryPoint OpEntryPointTest TEST_F(OpEntryPointTest, WrongModel)
16 OperandAddressing, OpMemoryModel OpMemoryModelTest TEST_F(OpMemoryModelTest, WrongModel)
17 OperandMemory, OpMemoryModel OpMemoryModelTest TEST_F(OpMemoryModelTest, WrongModel)
18 OperandExecutionMode, OpExecutionMode OpExecutionModeTest TEST_F(OpExecutionModeTest, WrongMode)
19 OperandStorage, TypePointer, TypeForwardPointer, Variable StorageClassTest TEST_F(OpTypeForwardPointerTest, WrongClass)
20 OperandDimensionality, TypeImage DimTest/AnyDim TEST_F(DimTest, WrongDim)
21 OperandSamplerAddressingMode, ConstantSampler SamplerAddressingModeTest TEST_F(SamplerAddressingModeTest, WrongMode)
22 OperandSamplerFilterMode, ConstantSampler AnySamplerFilterMode TEST_F(SamplerFilterModeTest, WrongMode)
23 OperandSamplerImageFormat, TypeImage SAMPLER_IMAGE_FORMAT ImageFormatTest TEST_F(ImageFormatTest, WrongFormat)
24 OperandImageChannelOrder, UNUSED returned as result value only
25 OperandImageChannelDataType, UNUSED returned as result value only
26 OperandImageOperands, UNUSED used to make a spec section see OperandOptionalImage
27 OperandFPFastMath, OpDecorate CombinedFPFastMathMask TEST_F(OpDecorateEnumTest, WrongFPFastMathMode)
28 OperandFPRoundingMode, OpDecorate TEST_F(OpDecorateEnumTest, WrongFPRoundingMode)
29 OperandLinkageType, OpDecorate OpDecorateLinkageTest TEST_F(OpDecorateLinkageTest, WrongType)
30 OperandAccessQualifier, OpTypePipe AnyAccessQualifier TEST_F(OpTypePipeTest, WrongAccessQualifier)
31 OperandFuncParamAttr, OpDecorate TextToBinaryDecorateFuncParamAttr TEST_F(OpDecorateEnumTest, WrongFuncParamAttr)
32 OperandDecoration, OpDecorate AnyAccessQualifier TEST_F(OpTypePipeTest, WrongAccessQualifier)
33 OperandBuiltIn, OpDecorate TextToBinaryDecorateBultIn TEST_F(OpDecorateEnumTest, WrongBuiltIn)
34 OperandSelect, SelectionMerge TextToBinarySelectionMerge TEST_F(OpSelectionMergeTest, WrongSelectionControl)
35 OperandLoop, LoopMerge CombinedLoopControlMask TEST_F(OpLoopMergeTest, WrongLoopControl)
36 OperandFunction, Function AnySingleFunctionControlMask TEST_F(OpFunctionControlTest, WrongFunctionControl)
37 OperandMemorySemantics, OpMemoryBarrier it's an ID, not in grammar OpMemoryBarrier* not enum
38 OperandMemoryAccess, UNUSED should be on opstore, but hacked in opcode.cpp not enum
39 OperandScope, MemoryBarrier it's an ID, not in grammar OpMemoryBarrier* not enum
40 OperandGroupOperation, GroupIAdd GroupOperationTest TEST_F(GroupOperationTest, WrongGroupOperation)
41 OperandKernelEnqueueFlags, OpEnqueueKernel it's an ID, not in grammar should not have one not enum
42 OperandKernelProfilingInfo, OpCaptureEventProfilingInfo it's an ID, not in grammar should not have one not enum
43 OperandCapability, Capability OpCapabilityTest TEST_F(TextToBinaryCapability, BadInvalidCapability)

View File

@@ -1,748 +0,0 @@
// Copyright (c) 2015-2016 The Khronos Group 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.
// Test capability dependencies for enums.
#include <tuple>
#include <vector>
#include "gmock/gmock.h"
#include "source/enum_set.h"
#include "test/unit_spirv.h"
namespace spvtools {
namespace {
using spvtest::ElementsIn;
using ::testing::Combine;
using ::testing::Eq;
using ::testing::TestWithParam;
using ::testing::Values;
using ::testing::ValuesIn;
// A test case for mapping an enum to a capability mask.
struct EnumCapabilityCase {
spv_operand_type_t type;
uint32_t value;
CapabilitySet expected_capabilities;
};
// Test fixture for testing EnumCapabilityCases.
using EnumCapabilityTest =
TestWithParam<std::tuple<spv_target_env, EnumCapabilityCase>>;
TEST_P(EnumCapabilityTest, Sample) {
const auto env = std::get<0>(GetParam());
const auto context = spvContextCreate(env);
const AssemblyGrammar grammar(context);
spv_operand_desc entry;
ASSERT_EQ(SPV_SUCCESS,
grammar.lookupOperand(std::get<1>(GetParam()).type,
std::get<1>(GetParam()).value, &entry));
const auto cap_set = grammar.filterCapsAgainstTargetEnv(
entry->capabilities, entry->numCapabilities);
EXPECT_THAT(ElementsIn(cap_set),
Eq(ElementsIn(std::get<1>(GetParam()).expected_capabilities)))
<< " capability value " << std::get<1>(GetParam()).value;
spvContextDestroy(context);
}
#define CASE0(TYPE, VALUE) \
{ \
SPV_OPERAND_TYPE_##TYPE, uint32_t(Spv##VALUE), {} \
}
#define CASE1(TYPE, VALUE, CAP) \
{ \
SPV_OPERAND_TYPE_##TYPE, uint32_t(Spv##VALUE), CapabilitySet { \
SpvCapability##CAP \
} \
}
#define CASE2(TYPE, VALUE, CAP1, CAP2) \
{ \
SPV_OPERAND_TYPE_##TYPE, uint32_t(Spv##VALUE), CapabilitySet { \
SpvCapability##CAP1, SpvCapability##CAP2 \
} \
}
#define CASE3(TYPE, VALUE, CAP1, CAP2, CAP3) \
{ \
SPV_OPERAND_TYPE_##TYPE, uint32_t(Spv##VALUE), CapabilitySet { \
SpvCapability##CAP1, SpvCapability##CAP2, SpvCapability##CAP3 \
} \
}
#define CASE5(TYPE, VALUE, CAP1, CAP2, CAP3, CAP4, CAP5) \
{ \
SPV_OPERAND_TYPE_##TYPE, uint32_t(Spv##VALUE), CapabilitySet { \
SpvCapability##CAP1, SpvCapability##CAP2, SpvCapability##CAP3, \
SpvCapability##CAP4, SpvCapability##CAP5 \
} \
}
// See SPIR-V Section 3.3 Execution Model
INSTANTIATE_TEST_SUITE_P(
ExecutionModel, EnumCapabilityTest,
Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
ValuesIn(std::vector<EnumCapabilityCase>{
CASE1(EXECUTION_MODEL, ExecutionModelVertex, Shader),
CASE1(EXECUTION_MODEL, ExecutionModelTessellationControl,
Tessellation),
CASE1(EXECUTION_MODEL, ExecutionModelTessellationEvaluation,
Tessellation),
CASE1(EXECUTION_MODEL, ExecutionModelGeometry, Geometry),
CASE1(EXECUTION_MODEL, ExecutionModelFragment, Shader),
CASE1(EXECUTION_MODEL, ExecutionModelGLCompute, Shader),
CASE1(EXECUTION_MODEL, ExecutionModelKernel, Kernel),
})));
// See SPIR-V Section 3.4 Addressing Model
INSTANTIATE_TEST_SUITE_P(
AddressingModel, EnumCapabilityTest,
Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
ValuesIn(std::vector<EnumCapabilityCase>{
CASE0(ADDRESSING_MODEL, AddressingModelLogical),
CASE1(ADDRESSING_MODEL, AddressingModelPhysical32, Addresses),
CASE1(ADDRESSING_MODEL, AddressingModelPhysical64, Addresses),
})));
// See SPIR-V Section 3.5 Memory Model
INSTANTIATE_TEST_SUITE_P(
MemoryModel, EnumCapabilityTest,
Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
ValuesIn(std::vector<EnumCapabilityCase>{
CASE1(MEMORY_MODEL, MemoryModelSimple, Shader),
CASE1(MEMORY_MODEL, MemoryModelGLSL450, Shader),
CASE1(MEMORY_MODEL, MemoryModelOpenCL, Kernel),
})));
// See SPIR-V Section 3.6 Execution Mode
INSTANTIATE_TEST_SUITE_P(
ExecutionMode, EnumCapabilityTest,
Combine(
Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
ValuesIn(std::vector<EnumCapabilityCase>{
CASE1(EXECUTION_MODE, ExecutionModeInvocations, Geometry),
CASE1(EXECUTION_MODE, ExecutionModeSpacingEqual, Tessellation),
CASE1(EXECUTION_MODE, ExecutionModeSpacingFractionalEven,
Tessellation),
CASE1(EXECUTION_MODE, ExecutionModeSpacingFractionalOdd,
Tessellation),
CASE1(EXECUTION_MODE, ExecutionModeVertexOrderCw, Tessellation),
CASE1(EXECUTION_MODE, ExecutionModeVertexOrderCcw, Tessellation),
CASE1(EXECUTION_MODE, ExecutionModePixelCenterInteger, Shader),
CASE1(EXECUTION_MODE, ExecutionModeOriginUpperLeft, Shader),
CASE1(EXECUTION_MODE, ExecutionModeOriginLowerLeft, Shader),
CASE1(EXECUTION_MODE, ExecutionModeEarlyFragmentTests, Shader),
CASE1(EXECUTION_MODE, ExecutionModePointMode, Tessellation),
CASE1(EXECUTION_MODE, ExecutionModeXfb, TransformFeedback),
CASE1(EXECUTION_MODE, ExecutionModeDepthReplacing, Shader),
CASE1(EXECUTION_MODE, ExecutionModeDepthGreater, Shader),
CASE1(EXECUTION_MODE, ExecutionModeDepthLess, Shader),
CASE1(EXECUTION_MODE, ExecutionModeDepthUnchanged, Shader),
CASE0(EXECUTION_MODE, ExecutionModeLocalSize),
CASE1(EXECUTION_MODE, ExecutionModeLocalSizeHint, Kernel),
CASE1(EXECUTION_MODE, ExecutionModeInputPoints, Geometry),
CASE1(EXECUTION_MODE, ExecutionModeInputLines, Geometry),
CASE1(EXECUTION_MODE, ExecutionModeInputLinesAdjacency, Geometry),
CASE2(EXECUTION_MODE, ExecutionModeTriangles, Geometry,
Tessellation),
CASE1(EXECUTION_MODE, ExecutionModeInputTrianglesAdjacency,
Geometry),
CASE1(EXECUTION_MODE, ExecutionModeQuads, Tessellation),
CASE1(EXECUTION_MODE, ExecutionModeIsolines, Tessellation),
CASE3(EXECUTION_MODE, ExecutionModeOutputVertices, Geometry,
Tessellation, MeshShadingNV),
CASE2(EXECUTION_MODE, ExecutionModeOutputPoints, Geometry,
MeshShadingNV),
CASE1(EXECUTION_MODE, ExecutionModeOutputLineStrip, Geometry),
CASE1(EXECUTION_MODE, ExecutionModeOutputTriangleStrip, Geometry),
CASE1(EXECUTION_MODE, ExecutionModeVecTypeHint, Kernel),
CASE1(EXECUTION_MODE, ExecutionModeContractionOff, Kernel),
})));
INSTANTIATE_TEST_SUITE_P(
ExecutionModeV11, EnumCapabilityTest,
Combine(Values(SPV_ENV_UNIVERSAL_1_1),
ValuesIn(std::vector<EnumCapabilityCase>{
CASE1(EXECUTION_MODE, ExecutionModeInitializer, Kernel),
CASE1(EXECUTION_MODE, ExecutionModeFinalizer, Kernel),
CASE1(EXECUTION_MODE, ExecutionModeSubgroupSize,
SubgroupDispatch),
CASE1(EXECUTION_MODE, ExecutionModeSubgroupsPerWorkgroup,
SubgroupDispatch)})));
// See SPIR-V Section 3.7 Storage Class
INSTANTIATE_TEST_SUITE_P(
StorageClass, EnumCapabilityTest,
Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
ValuesIn(std::vector<EnumCapabilityCase>{
CASE0(STORAGE_CLASS, StorageClassUniformConstant),
CASE1(STORAGE_CLASS, StorageClassUniform, Shader),
CASE1(STORAGE_CLASS, StorageClassOutput, Shader),
CASE0(STORAGE_CLASS, StorageClassWorkgroup),
CASE0(STORAGE_CLASS, StorageClassCrossWorkgroup),
CASE1(STORAGE_CLASS, StorageClassPrivate, Shader),
CASE0(STORAGE_CLASS, StorageClassFunction),
CASE1(STORAGE_CLASS, StorageClassGeneric,
GenericPointer), // Bug 14287
CASE1(STORAGE_CLASS, StorageClassPushConstant, Shader),
CASE1(STORAGE_CLASS, StorageClassAtomicCounter, AtomicStorage),
CASE0(STORAGE_CLASS, StorageClassImage),
})));
// See SPIR-V Section 3.8 Dim
INSTANTIATE_TEST_SUITE_P(
Dim, EnumCapabilityTest,
Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
ValuesIn(std::vector<EnumCapabilityCase>{
CASE2(DIMENSIONALITY, Dim1D, Sampled1D, Image1D),
CASE3(DIMENSIONALITY, Dim2D, Kernel, Shader, ImageMSArray),
CASE0(DIMENSIONALITY, Dim3D),
CASE2(DIMENSIONALITY, DimCube, Shader, ImageCubeArray),
CASE2(DIMENSIONALITY, DimRect, SampledRect, ImageRect),
CASE2(DIMENSIONALITY, DimBuffer, SampledBuffer, ImageBuffer),
CASE1(DIMENSIONALITY, DimSubpassData, InputAttachment),
})));
// See SPIR-V Section 3.9 Sampler Addressing Mode
INSTANTIATE_TEST_SUITE_P(
SamplerAddressingMode, EnumCapabilityTest,
Combine(
Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
ValuesIn(std::vector<EnumCapabilityCase>{
CASE1(SAMPLER_ADDRESSING_MODE, SamplerAddressingModeNone, Kernel),
CASE1(SAMPLER_ADDRESSING_MODE, SamplerAddressingModeClampToEdge,
Kernel),
CASE1(SAMPLER_ADDRESSING_MODE, SamplerAddressingModeClamp, Kernel),
CASE1(SAMPLER_ADDRESSING_MODE, SamplerAddressingModeRepeat, Kernel),
CASE1(SAMPLER_ADDRESSING_MODE, SamplerAddressingModeRepeatMirrored,
Kernel),
})));
// See SPIR-V Section 3.10 Sampler Filter Mode
INSTANTIATE_TEST_SUITE_P(
SamplerFilterMode, EnumCapabilityTest,
Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
ValuesIn(std::vector<EnumCapabilityCase>{
CASE1(SAMPLER_FILTER_MODE, SamplerFilterModeNearest, Kernel),
CASE1(SAMPLER_FILTER_MODE, SamplerFilterModeLinear, Kernel),
})));
// See SPIR-V Section 3.11 Image Format
INSTANTIATE_TEST_SUITE_P(
ImageFormat, EnumCapabilityTest,
Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
ValuesIn(std::vector<EnumCapabilityCase>{
// clang-format off
CASE0(SAMPLER_IMAGE_FORMAT, ImageFormatUnknown),
CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgba32f, Shader),
CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgba16f, Shader),
CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatR32f, Shader),
CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgba8, Shader),
CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgba8Snorm, Shader),
CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRg32f, StorageImageExtendedFormats),
CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRg16f, StorageImageExtendedFormats),
CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatR11fG11fB10f, StorageImageExtendedFormats),
CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatR16f, StorageImageExtendedFormats),
CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgba16, StorageImageExtendedFormats),
CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgb10A2, StorageImageExtendedFormats),
CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRg16, StorageImageExtendedFormats),
CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRg8, StorageImageExtendedFormats),
CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatR16, StorageImageExtendedFormats),
CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatR8, StorageImageExtendedFormats),
CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgba16Snorm, StorageImageExtendedFormats),
CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRg16Snorm, StorageImageExtendedFormats),
CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRg8Snorm, StorageImageExtendedFormats),
CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatR16Snorm, StorageImageExtendedFormats),
CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatR8Snorm, StorageImageExtendedFormats),
CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgba32i, Shader),
CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgba16i, Shader),
CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgba8i, Shader),
CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatR32i, Shader),
CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRg32i, StorageImageExtendedFormats),
CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRg16i, StorageImageExtendedFormats),
CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRg8i, StorageImageExtendedFormats),
CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatR16i, StorageImageExtendedFormats),
CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatR8i, StorageImageExtendedFormats),
CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgba32ui, Shader),
CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgba16ui, Shader),
CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgba8ui, Shader),
CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgba8ui, Shader),
CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgb10a2ui, StorageImageExtendedFormats),
CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRg32ui, StorageImageExtendedFormats),
CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRg16ui, StorageImageExtendedFormats),
CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRg8ui, StorageImageExtendedFormats),
CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatR16ui, StorageImageExtendedFormats),
CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatR8ui, StorageImageExtendedFormats),
// clang-format on
})));
// See SPIR-V Section 3.12 Image Channel Order
INSTANTIATE_TEST_SUITE_P(
ImageChannelOrder, EnumCapabilityTest,
Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
ValuesIn(std::vector<EnumCapabilityCase>{
CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderR, Kernel),
CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderA, Kernel),
CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderRG, Kernel),
CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderRA, Kernel),
CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderRGB, Kernel),
CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderRGBA, Kernel),
CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderBGRA, Kernel),
CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderARGB, Kernel),
CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderIntensity, Kernel),
CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderLuminance, Kernel),
CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderRx, Kernel),
CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderRGx, Kernel),
CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderRGBx, Kernel),
CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderDepth, Kernel),
CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderDepthStencil,
Kernel),
CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrdersRGB, Kernel),
CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrdersRGBx, Kernel),
CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrdersRGBA, Kernel),
CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrdersBGRA, Kernel),
CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderABGR, Kernel),
})));
// See SPIR-V Section 3.13 Image Channel Data Type
INSTANTIATE_TEST_SUITE_P(
ImageChannelDataType, EnumCapabilityTest,
Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
ValuesIn(std::vector<EnumCapabilityCase>{
// clang-format off
CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeSnormInt8, Kernel),
CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeSnormInt16, Kernel),
CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeUnormInt8, Kernel),
CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeUnormInt16, Kernel),
CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeUnormShort565, Kernel),
CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeUnormShort555, Kernel),
CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeUnormInt101010, Kernel),
CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeSignedInt8, Kernel),
CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeSignedInt16, Kernel),
CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeSignedInt32, Kernel),
CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeUnsignedInt8, Kernel),
CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeUnsignedInt16, Kernel),
CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeUnsignedInt32, Kernel),
CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeHalfFloat, Kernel),
CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeFloat, Kernel),
CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeUnormInt24, Kernel),
CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeUnormInt101010_2, Kernel),
// clang-format on
})));
// See SPIR-V Section 3.14 Image Operands
INSTANTIATE_TEST_SUITE_P(
ImageOperands, EnumCapabilityTest,
Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
ValuesIn(std::vector<EnumCapabilityCase>{
// clang-format off
CASE0(OPTIONAL_IMAGE, ImageOperandsMaskNone),
CASE1(OPTIONAL_IMAGE, ImageOperandsBiasMask, Shader),
CASE0(OPTIONAL_IMAGE, ImageOperandsLodMask),
CASE0(OPTIONAL_IMAGE, ImageOperandsGradMask),
CASE0(OPTIONAL_IMAGE, ImageOperandsConstOffsetMask),
CASE1(OPTIONAL_IMAGE, ImageOperandsOffsetMask, ImageGatherExtended),
CASE1(OPTIONAL_IMAGE, ImageOperandsConstOffsetsMask, ImageGatherExtended),
CASE0(OPTIONAL_IMAGE, ImageOperandsSampleMask),
CASE1(OPTIONAL_IMAGE, ImageOperandsMinLodMask, MinLod),
// clang-format on
})));
// See SPIR-V Section 3.15 FP Fast Math Mode
INSTANTIATE_TEST_SUITE_P(
FPFastMathMode, EnumCapabilityTest,
Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
ValuesIn(std::vector<EnumCapabilityCase>{
CASE0(FP_FAST_MATH_MODE, FPFastMathModeMaskNone),
CASE1(FP_FAST_MATH_MODE, FPFastMathModeNotNaNMask, Kernel),
CASE1(FP_FAST_MATH_MODE, FPFastMathModeNotInfMask, Kernel),
CASE1(FP_FAST_MATH_MODE, FPFastMathModeNSZMask, Kernel),
CASE1(FP_FAST_MATH_MODE, FPFastMathModeAllowRecipMask, Kernel),
CASE1(FP_FAST_MATH_MODE, FPFastMathModeFastMask, Kernel),
})));
// See SPIR-V Section 3.17 Linkage Type
INSTANTIATE_TEST_SUITE_P(
LinkageType, EnumCapabilityTest,
Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
ValuesIn(std::vector<EnumCapabilityCase>{
CASE1(LINKAGE_TYPE, LinkageTypeExport, Linkage),
CASE1(LINKAGE_TYPE, LinkageTypeImport, Linkage),
})));
// See SPIR-V Section 3.18 Access Qualifier
INSTANTIATE_TEST_SUITE_P(
AccessQualifier, EnumCapabilityTest,
Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
ValuesIn(std::vector<EnumCapabilityCase>{
CASE1(ACCESS_QUALIFIER, AccessQualifierReadOnly, Kernel),
CASE1(ACCESS_QUALIFIER, AccessQualifierWriteOnly, Kernel),
CASE1(ACCESS_QUALIFIER, AccessQualifierReadWrite, Kernel),
})));
// See SPIR-V Section 3.19 Function Parameter Attribute
INSTANTIATE_TEST_SUITE_P(
FunctionParameterAttribute, EnumCapabilityTest,
Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
ValuesIn(std::vector<EnumCapabilityCase>{
// clang-format off
CASE1(FUNCTION_PARAMETER_ATTRIBUTE, FunctionParameterAttributeZext, Kernel),
CASE1(FUNCTION_PARAMETER_ATTRIBUTE, FunctionParameterAttributeSext, Kernel),
CASE1(FUNCTION_PARAMETER_ATTRIBUTE, FunctionParameterAttributeByVal, Kernel),
CASE1(FUNCTION_PARAMETER_ATTRIBUTE, FunctionParameterAttributeSret, Kernel),
CASE1(FUNCTION_PARAMETER_ATTRIBUTE, FunctionParameterAttributeNoAlias, Kernel),
CASE1(FUNCTION_PARAMETER_ATTRIBUTE, FunctionParameterAttributeNoCapture, Kernel),
CASE1(FUNCTION_PARAMETER_ATTRIBUTE, FunctionParameterAttributeNoWrite, Kernel),
CASE1(FUNCTION_PARAMETER_ATTRIBUTE, FunctionParameterAttributeNoReadWrite, Kernel),
// clang-format on
})));
// See SPIR-V Section 3.20 Decoration
INSTANTIATE_TEST_SUITE_P(
Decoration, EnumCapabilityTest,
Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
ValuesIn(std::vector<EnumCapabilityCase>{
CASE1(DECORATION, DecorationRelaxedPrecision, Shader),
// DecorationSpecId handled below.
CASE1(DECORATION, DecorationBlock, Shader),
CASE1(DECORATION, DecorationBufferBlock, Shader),
CASE1(DECORATION, DecorationRowMajor, Matrix),
CASE1(DECORATION, DecorationColMajor, Matrix),
CASE1(DECORATION, DecorationArrayStride, Shader),
CASE1(DECORATION, DecorationMatrixStride, Matrix), // Bug 15234
CASE1(DECORATION, DecorationGLSLShared, Shader),
CASE1(DECORATION, DecorationGLSLPacked, Shader),
CASE1(DECORATION, DecorationCPacked, Kernel),
CASE0(DECORATION, DecorationBuiltIn), // Bug 15248
// Value 12 placeholder
CASE1(DECORATION, DecorationNoPerspective, Shader),
CASE1(DECORATION, DecorationFlat, Shader),
CASE1(DECORATION, DecorationPatch, Tessellation),
CASE1(DECORATION, DecorationCentroid, Shader),
CASE1(DECORATION, DecorationSample,
SampleRateShading), // Bug 15234
CASE1(DECORATION, DecorationInvariant, Shader),
CASE0(DECORATION, DecorationRestrict),
CASE0(DECORATION, DecorationAliased),
CASE0(DECORATION, DecorationVolatile),
CASE1(DECORATION, DecorationConstant, Kernel),
CASE0(DECORATION, DecorationCoherent),
CASE0(DECORATION, DecorationNonWritable),
CASE0(DECORATION, DecorationNonReadable),
CASE1(DECORATION, DecorationUniform, Shader),
// Value 27 is an intentional gap in the spec numbering.
CASE1(DECORATION, DecorationSaturatedConversion, Kernel),
CASE1(DECORATION, DecorationStream, GeometryStreams),
CASE1(DECORATION, DecorationLocation, Shader),
CASE1(DECORATION, DecorationComponent, Shader),
CASE1(DECORATION, DecorationIndex, Shader),
CASE1(DECORATION, DecorationBinding, Shader),
CASE1(DECORATION, DecorationDescriptorSet, Shader),
CASE1(DECORATION, DecorationOffset, Shader), // Bug 15268
CASE1(DECORATION, DecorationXfbBuffer, TransformFeedback),
CASE1(DECORATION, DecorationXfbStride, TransformFeedback),
CASE1(DECORATION, DecorationFuncParamAttr, Kernel),
CASE1(DECORATION, DecorationFPFastMathMode, Kernel),
CASE1(DECORATION, DecorationLinkageAttributes, Linkage),
CASE1(DECORATION, DecorationNoContraction, Shader),
CASE1(DECORATION, DecorationInputAttachmentIndex,
InputAttachment),
CASE1(DECORATION, DecorationAlignment, Kernel),
})));
#if 0
// SpecId has different requirements in v1.0 and v1.1:
INSTANTIATE_TEST_SUITE_P(DecorationSpecIdV10, EnumCapabilityTest,
Combine(Values(SPV_ENV_UNIVERSAL_1_0),
ValuesIn(std::vector<EnumCapabilityCase>{CASE1(
DECORATION, DecorationSpecId, Shader)})));
#endif
INSTANTIATE_TEST_SUITE_P(
DecorationV11, EnumCapabilityTest,
Combine(Values(SPV_ENV_UNIVERSAL_1_1),
ValuesIn(std::vector<EnumCapabilityCase>{
CASE2(DECORATION, DecorationSpecId, Shader, Kernel),
CASE1(DECORATION, DecorationMaxByteOffset, Addresses)})));
// See SPIR-V Section 3.21 BuiltIn
INSTANTIATE_TEST_SUITE_P(
BuiltIn, EnumCapabilityTest,
Combine(
Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
ValuesIn(std::vector<EnumCapabilityCase>{
// clang-format off
CASE1(BUILT_IN, BuiltInPosition, Shader),
CASE1(BUILT_IN, BuiltInPointSize, Shader),
// 2 is an intentional gap in the spec numbering.
CASE1(BUILT_IN, BuiltInClipDistance, ClipDistance), // Bug 1407, 15234
CASE1(BUILT_IN, BuiltInCullDistance, CullDistance), // Bug 1407, 15234
CASE1(BUILT_IN, BuiltInVertexId, Shader),
CASE1(BUILT_IN, BuiltInInstanceId, Shader),
CASE3(BUILT_IN, BuiltInPrimitiveId, Geometry, Tessellation,
RayTracingNV),
CASE2(BUILT_IN, BuiltInInvocationId, Geometry, Tessellation),
CASE2(BUILT_IN, BuiltInLayer, Geometry, ShaderViewportIndexLayerEXT),
CASE2(BUILT_IN, BuiltInViewportIndex, MultiViewport, ShaderViewportIndexLayerEXT), // Bug 15234
CASE1(BUILT_IN, BuiltInTessLevelOuter, Tessellation),
CASE1(BUILT_IN, BuiltInTessLevelInner, Tessellation),
CASE1(BUILT_IN, BuiltInTessCoord, Tessellation),
CASE1(BUILT_IN, BuiltInPatchVertices, Tessellation),
CASE1(BUILT_IN, BuiltInFragCoord, Shader),
CASE1(BUILT_IN, BuiltInPointCoord, Shader),
CASE1(BUILT_IN, BuiltInFrontFacing, Shader),
CASE1(BUILT_IN, BuiltInSampleId, SampleRateShading), // Bug 15234
CASE1(BUILT_IN, BuiltInSamplePosition, SampleRateShading), // Bug 15234
CASE1(BUILT_IN, BuiltInSampleMask, Shader), // Bug 15234, Issue 182
// Value 21 intentionally missing
CASE1(BUILT_IN, BuiltInFragDepth, Shader),
CASE1(BUILT_IN, BuiltInHelperInvocation, Shader),
CASE0(BUILT_IN, BuiltInNumWorkgroups),
CASE0(BUILT_IN, BuiltInWorkgroupSize),
CASE0(BUILT_IN, BuiltInWorkgroupId),
CASE0(BUILT_IN, BuiltInLocalInvocationId),
CASE0(BUILT_IN, BuiltInGlobalInvocationId),
CASE0(BUILT_IN, BuiltInLocalInvocationIndex),
CASE1(BUILT_IN, BuiltInWorkDim, Kernel),
CASE1(BUILT_IN, BuiltInGlobalSize, Kernel),
CASE1(BUILT_IN, BuiltInEnqueuedWorkgroupSize, Kernel),
CASE1(BUILT_IN, BuiltInGlobalOffset, Kernel),
CASE1(BUILT_IN, BuiltInGlobalLinearId, Kernel),
// Value 35 intentionally missing
CASE2(BUILT_IN, BuiltInSubgroupSize, Kernel, SubgroupBallotKHR),
CASE1(BUILT_IN, BuiltInSubgroupMaxSize, Kernel),
CASE1(BUILT_IN, BuiltInNumSubgroups, Kernel),
CASE1(BUILT_IN, BuiltInNumEnqueuedSubgroups, Kernel),
CASE1(BUILT_IN, BuiltInSubgroupId, Kernel),
CASE2(BUILT_IN, BuiltInSubgroupLocalInvocationId, Kernel, SubgroupBallotKHR),
CASE1(BUILT_IN, BuiltInVertexIndex, Shader),
CASE1(BUILT_IN, BuiltInInstanceIndex, Shader),
// clang-format on
})));
INSTANTIATE_TEST_SUITE_P(
BuiltInV1_5, EnumCapabilityTest,
Combine(
Values(SPV_ENV_UNIVERSAL_1_5),
ValuesIn(std::vector<EnumCapabilityCase>{
// SPIR-V 1.5 adds new capabilities to enable these two builtins.
CASE3(BUILT_IN, BuiltInLayer, Geometry, ShaderLayer,
ShaderViewportIndexLayerEXT),
CASE3(BUILT_IN, BuiltInViewportIndex, MultiViewport,
ShaderViewportIndex, ShaderViewportIndexLayerEXT),
})));
// See SPIR-V Section 3.22 Selection Control
INSTANTIATE_TEST_SUITE_P(
SelectionControl, EnumCapabilityTest,
Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
ValuesIn(std::vector<EnumCapabilityCase>{
CASE0(SELECTION_CONTROL, SelectionControlMaskNone),
CASE0(SELECTION_CONTROL, SelectionControlFlattenMask),
CASE0(SELECTION_CONTROL, SelectionControlDontFlattenMask),
})));
// See SPIR-V Section 3.23 Loop Control
INSTANTIATE_TEST_SUITE_P(
LoopControl, EnumCapabilityTest,
Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
ValuesIn(std::vector<EnumCapabilityCase>{
CASE0(LOOP_CONTROL, LoopControlMaskNone),
CASE0(LOOP_CONTROL, LoopControlUnrollMask),
CASE0(LOOP_CONTROL, LoopControlDontUnrollMask),
})));
INSTANTIATE_TEST_SUITE_P(
LoopControlV11, EnumCapabilityTest,
Combine(Values(SPV_ENV_UNIVERSAL_1_1),
ValuesIn(std::vector<EnumCapabilityCase>{
CASE0(LOOP_CONTROL, LoopControlDependencyInfiniteMask),
CASE0(LOOP_CONTROL, LoopControlDependencyLengthMask),
})));
// See SPIR-V Section 3.24 Function Control
INSTANTIATE_TEST_SUITE_P(
FunctionControl, EnumCapabilityTest,
Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
ValuesIn(std::vector<EnumCapabilityCase>{
CASE0(FUNCTION_CONTROL, FunctionControlMaskNone),
CASE0(FUNCTION_CONTROL, FunctionControlInlineMask),
CASE0(FUNCTION_CONTROL, FunctionControlDontInlineMask),
CASE0(FUNCTION_CONTROL, FunctionControlPureMask),
CASE0(FUNCTION_CONTROL, FunctionControlConstMask),
})));
// See SPIR-V Section 3.25 Memory Semantics <id>
INSTANTIATE_TEST_SUITE_P(
MemorySemantics, EnumCapabilityTest,
Combine(
Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
ValuesIn(std::vector<EnumCapabilityCase>{
CASE0(MEMORY_SEMANTICS_ID, MemorySemanticsMaskNone),
CASE0(MEMORY_SEMANTICS_ID, MemorySemanticsAcquireMask),
CASE0(MEMORY_SEMANTICS_ID, MemorySemanticsReleaseMask),
CASE0(MEMORY_SEMANTICS_ID, MemorySemanticsAcquireReleaseMask),
CASE0(MEMORY_SEMANTICS_ID,
MemorySemanticsSequentiallyConsistentMask),
CASE1(MEMORY_SEMANTICS_ID, MemorySemanticsUniformMemoryMask,
Shader),
CASE0(MEMORY_SEMANTICS_ID, MemorySemanticsSubgroupMemoryMask),
CASE0(MEMORY_SEMANTICS_ID, MemorySemanticsWorkgroupMemoryMask),
CASE0(MEMORY_SEMANTICS_ID, MemorySemanticsCrossWorkgroupMemoryMask),
CASE1(MEMORY_SEMANTICS_ID, MemorySemanticsAtomicCounterMemoryMask,
AtomicStorage), // Bug 15234
CASE0(MEMORY_SEMANTICS_ID, MemorySemanticsImageMemoryMask),
})));
// See SPIR-V Section 3.26 Memory Access
INSTANTIATE_TEST_SUITE_P(
MemoryAccess, EnumCapabilityTest,
Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
ValuesIn(std::vector<EnumCapabilityCase>{
CASE0(OPTIONAL_MEMORY_ACCESS, MemoryAccessMaskNone),
CASE0(OPTIONAL_MEMORY_ACCESS, MemoryAccessVolatileMask),
CASE0(OPTIONAL_MEMORY_ACCESS, MemoryAccessAlignedMask),
CASE0(OPTIONAL_MEMORY_ACCESS, MemoryAccessNontemporalMask),
})));
// See SPIR-V Section 3.27 Scope <id>
INSTANTIATE_TEST_SUITE_P(
Scope, EnumCapabilityTest,
Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1,
SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_3),
ValuesIn(std::vector<EnumCapabilityCase>{
CASE0(SCOPE_ID, ScopeCrossDevice),
CASE0(SCOPE_ID, ScopeDevice),
CASE0(SCOPE_ID, ScopeWorkgroup),
CASE0(SCOPE_ID, ScopeSubgroup),
CASE0(SCOPE_ID, ScopeInvocation),
CASE1(SCOPE_ID, ScopeQueueFamilyKHR, VulkanMemoryModelKHR),
})));
// See SPIR-V Section 3.28 Group Operation
INSTANTIATE_TEST_SUITE_P(
GroupOperation, EnumCapabilityTest,
Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
ValuesIn(std::vector<EnumCapabilityCase>{
CASE3(GROUP_OPERATION, GroupOperationReduce, Kernel,
GroupNonUniformArithmetic, GroupNonUniformBallot),
CASE3(GROUP_OPERATION, GroupOperationInclusiveScan, Kernel,
GroupNonUniformArithmetic, GroupNonUniformBallot),
CASE3(GROUP_OPERATION, GroupOperationExclusiveScan, Kernel,
GroupNonUniformArithmetic, GroupNonUniformBallot),
})));
// See SPIR-V Section 3.29 Kernel Enqueue Flags
INSTANTIATE_TEST_SUITE_P(
KernelEnqueueFlags, EnumCapabilityTest,
Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
ValuesIn(std::vector<EnumCapabilityCase>{
CASE1(KERNEL_ENQ_FLAGS, KernelEnqueueFlagsNoWait, Kernel),
CASE1(KERNEL_ENQ_FLAGS, KernelEnqueueFlagsWaitKernel, Kernel),
CASE1(KERNEL_ENQ_FLAGS, KernelEnqueueFlagsWaitWorkGroup,
Kernel),
})));
// See SPIR-V Section 3.30 Kernel Profiling Info
INSTANTIATE_TEST_SUITE_P(
KernelProfilingInfo, EnumCapabilityTest,
Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
ValuesIn(std::vector<EnumCapabilityCase>{
CASE0(KERNEL_PROFILING_INFO, KernelProfilingInfoMaskNone),
CASE1(KERNEL_PROFILING_INFO, KernelProfilingInfoCmdExecTimeMask,
Kernel),
})));
// See SPIR-V Section 3.31 Capability
INSTANTIATE_TEST_SUITE_P(
CapabilityDependsOn, EnumCapabilityTest,
Combine(
Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1),
ValuesIn(std::vector<EnumCapabilityCase>{
// clang-format off
CASE0(CAPABILITY, CapabilityMatrix),
CASE1(CAPABILITY, CapabilityShader, Matrix),
CASE1(CAPABILITY, CapabilityGeometry, Shader),
CASE1(CAPABILITY, CapabilityTessellation, Shader),
CASE0(CAPABILITY, CapabilityAddresses),
CASE0(CAPABILITY, CapabilityLinkage),
CASE0(CAPABILITY, CapabilityKernel),
CASE1(CAPABILITY, CapabilityVector16, Kernel),
CASE1(CAPABILITY, CapabilityFloat16Buffer, Kernel),
CASE0(CAPABILITY, CapabilityFloat16), // Bug 15234
CASE0(CAPABILITY, CapabilityFloat64),
CASE0(CAPABILITY, CapabilityInt64),
CASE1(CAPABILITY, CapabilityInt64Atomics, Int64),
CASE1(CAPABILITY, CapabilityImageBasic, Kernel),
CASE1(CAPABILITY, CapabilityImageReadWrite, ImageBasic),
CASE1(CAPABILITY, CapabilityImageMipmap, ImageBasic),
// Value 16 intentionally missing.
CASE1(CAPABILITY, CapabilityPipes, Kernel),
CASE0(CAPABILITY, CapabilityGroups),
CASE1(CAPABILITY, CapabilityDeviceEnqueue, Kernel),
CASE1(CAPABILITY, CapabilityLiteralSampler, Kernel),
CASE1(CAPABILITY, CapabilityAtomicStorage, Shader),
CASE0(CAPABILITY, CapabilityInt16),
CASE1(CAPABILITY, CapabilityTessellationPointSize, Tessellation),
CASE1(CAPABILITY, CapabilityGeometryPointSize, Geometry),
CASE1(CAPABILITY, CapabilityImageGatherExtended, Shader),
// Value 26 intentionally missing.
CASE1(CAPABILITY, CapabilityStorageImageMultisample, Shader),
CASE1(CAPABILITY, CapabilityUniformBufferArrayDynamicIndexing, Shader),
CASE1(CAPABILITY, CapabilitySampledImageArrayDynamicIndexing, Shader),
CASE1(CAPABILITY, CapabilityStorageBufferArrayDynamicIndexing, Shader),
CASE1(CAPABILITY, CapabilityStorageImageArrayDynamicIndexing, Shader),
CASE1(CAPABILITY, CapabilityClipDistance, Shader),
CASE1(CAPABILITY, CapabilityCullDistance, Shader),
CASE1(CAPABILITY, CapabilityImageCubeArray, SampledCubeArray),
CASE1(CAPABILITY, CapabilitySampleRateShading, Shader),
CASE1(CAPABILITY, CapabilityImageRect, SampledRect),
CASE1(CAPABILITY, CapabilitySampledRect, Shader),
CASE1(CAPABILITY, CapabilityGenericPointer, Addresses),
CASE0(CAPABILITY, CapabilityInt8),
CASE1(CAPABILITY, CapabilityInputAttachment, Shader),
CASE1(CAPABILITY, CapabilitySparseResidency, Shader),
CASE1(CAPABILITY, CapabilityMinLod, Shader),
CASE1(CAPABILITY, CapabilityImage1D, Sampled1D),
CASE1(CAPABILITY, CapabilitySampledCubeArray, Shader),
CASE1(CAPABILITY, CapabilityImageBuffer, SampledBuffer),
CASE1(CAPABILITY, CapabilityImageMSArray, Shader),
CASE1(CAPABILITY, CapabilityStorageImageExtendedFormats, Shader),
CASE1(CAPABILITY, CapabilityImageQuery, Shader),
CASE1(CAPABILITY, CapabilityDerivativeControl, Shader),
CASE1(CAPABILITY, CapabilityInterpolationFunction, Shader),
CASE1(CAPABILITY, CapabilityTransformFeedback, Shader),
CASE1(CAPABILITY, CapabilityGeometryStreams, Geometry),
CASE1(CAPABILITY, CapabilityStorageImageReadWithoutFormat, Shader),
CASE1(CAPABILITY, CapabilityStorageImageWriteWithoutFormat, Shader),
CASE1(CAPABILITY, CapabilityMultiViewport, Geometry),
// clang-format on
})));
INSTANTIATE_TEST_SUITE_P(
CapabilityDependsOnV11, EnumCapabilityTest,
Combine(Values(SPV_ENV_UNIVERSAL_1_1),
ValuesIn(std::vector<EnumCapabilityCase>{
CASE1(CAPABILITY, CapabilitySubgroupDispatch, DeviceEnqueue),
CASE1(CAPABILITY, CapabilityNamedBarrier, Kernel),
CASE1(CAPABILITY, CapabilityPipeStorage, Pipes),
})));
#undef CASE0
#undef CASE1
#undef CASE2
} // namespace
} // namespace spvtools

View File

@@ -1,270 +0,0 @@
// Copyright (c) 2015-2016 The Khronos Group 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 <vector>
#include "gmock/gmock.h"
#include "source/operand.h"
#include "test/unit_spirv.h"
namespace spvtools {
namespace {
using ::testing::Eq;
TEST(OperandPattern, InitiallyEmpty) {
spv_operand_pattern_t empty;
EXPECT_THAT(empty, Eq(spv_operand_pattern_t{}));
EXPECT_EQ(0u, empty.size());
EXPECT_TRUE(empty.empty());
}
TEST(OperandPattern, PushBacksAreOnTheRight) {
spv_operand_pattern_t pattern;
pattern.push_back(SPV_OPERAND_TYPE_ID);
EXPECT_THAT(pattern, Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_ID}));
EXPECT_EQ(1u, pattern.size());
EXPECT_TRUE(!pattern.empty());
EXPECT_EQ(SPV_OPERAND_TYPE_ID, pattern.back());
pattern.push_back(SPV_OPERAND_TYPE_NONE);
EXPECT_THAT(pattern, Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_ID,
SPV_OPERAND_TYPE_NONE}));
EXPECT_EQ(2u, pattern.size());
EXPECT_TRUE(!pattern.empty());
EXPECT_EQ(SPV_OPERAND_TYPE_NONE, pattern.back());
}
TEST(OperandPattern, PopBacksAreOnTheRight) {
spv_operand_pattern_t pattern{SPV_OPERAND_TYPE_ID,
SPV_OPERAND_TYPE_LITERAL_INTEGER};
pattern.pop_back();
EXPECT_THAT(pattern, Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_ID}));
pattern.pop_back();
EXPECT_THAT(pattern, Eq(spv_operand_pattern_t{}));
}
// A test case for typed mask expansion
struct MaskExpansionCase {
spv_operand_type_t type;
uint32_t mask;
spv_operand_pattern_t initial;
spv_operand_pattern_t expected;
};
using MaskExpansionTest = ::testing::TestWithParam<MaskExpansionCase>;
TEST_P(MaskExpansionTest, Sample) {
spv_operand_table operandTable = nullptr;
auto env = SPV_ENV_UNIVERSAL_1_0;
ASSERT_EQ(SPV_SUCCESS, spvOperandTableGet(&operandTable, env));
spv_operand_pattern_t pattern(GetParam().initial);
spvPushOperandTypesForMask(env, operandTable, GetParam().type,
GetParam().mask, &pattern);
EXPECT_THAT(pattern, Eq(GetParam().expected));
}
// These macros let us write non-trivial examples without too much text.
#define PREFIX0 SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE
#define PREFIX1 \
SPV_OPERAND_TYPE_STORAGE_CLASS, SPV_OPERAND_TYPE_SAMPLER_FILTER_MODE, \
SPV_OPERAND_TYPE_ID
INSTANTIATE_TEST_SUITE_P(
OperandPattern, MaskExpansionTest,
::testing::ValuesIn(std::vector<MaskExpansionCase>{
// No bits means no change.
{SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS, 0, {PREFIX0}, {PREFIX0}},
// Unknown bits means no change. Use all bits that aren't in the
// grammar.
// The last mask enum is 0x20
{SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS,
0xffffffc0,
{PREFIX1},
{PREFIX1}},
// Volatile has no operands.
{SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS,
SpvMemoryAccessVolatileMask,
{PREFIX0},
{PREFIX0}},
// Aligned has one literal number operand.
{SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS,
SpvMemoryAccessAlignedMask,
{PREFIX1},
{PREFIX1, SPV_OPERAND_TYPE_LITERAL_INTEGER}},
// Volatile with Aligned still has just one literal number operand.
{SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS,
SpvMemoryAccessVolatileMask | SpvMemoryAccessAlignedMask,
{PREFIX1},
{PREFIX1, SPV_OPERAND_TYPE_LITERAL_INTEGER}},
}));
#undef PREFIX0
#undef PREFIX1
// Returns a vector of all operand types that can be used in a pattern.
std::vector<spv_operand_type_t> allOperandTypes() {
std::vector<spv_operand_type_t> result;
for (int i = 0; i < SPV_OPERAND_TYPE_NUM_OPERAND_TYPES; i++) {
result.push_back(spv_operand_type_t(i));
}
return result;
}
using MatchableOperandExpansionTest =
::testing::TestWithParam<spv_operand_type_t>;
TEST_P(MatchableOperandExpansionTest, MatchableOperandsDontExpand) {
const spv_operand_type_t type = GetParam();
if (!spvOperandIsVariable(type)) {
spv_operand_pattern_t pattern;
const bool did_expand = spvExpandOperandSequenceOnce(type, &pattern);
EXPECT_FALSE(did_expand);
EXPECT_THAT(pattern, Eq(spv_operand_pattern_t{}));
}
}
INSTANTIATE_TEST_SUITE_P(MatchableOperandExpansion,
MatchableOperandExpansionTest,
::testing::ValuesIn(allOperandTypes()));
using VariableOperandExpansionTest =
::testing::TestWithParam<spv_operand_type_t>;
TEST_P(VariableOperandExpansionTest, NonMatchableOperandsExpand) {
const spv_operand_type_t type = GetParam();
if (spvOperandIsVariable(type)) {
spv_operand_pattern_t pattern;
const bool did_expand = spvExpandOperandSequenceOnce(type, &pattern);
EXPECT_TRUE(did_expand);
EXPECT_FALSE(pattern.empty());
// For the existing rules, the first expansion of a zero-or-more operand
// type yields a matchable operand type. This isn't strictly necessary.
EXPECT_FALSE(spvOperandIsVariable(pattern.back()));
}
}
INSTANTIATE_TEST_SUITE_P(NonMatchableOperandExpansion,
VariableOperandExpansionTest,
::testing::ValuesIn(allOperandTypes()));
TEST(AlternatePatternFollowingImmediate, Empty) {
EXPECT_THAT(spvAlternatePatternFollowingImmediate({}),
Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_OPTIONAL_CIV}));
}
TEST(AlternatePatternFollowingImmediate, SingleElement) {
// Spot-check a random selection of types.
EXPECT_THAT(spvAlternatePatternFollowingImmediate(
{SPV_OPERAND_TYPE_VARIABLE_ID_LITERAL_INTEGER}),
Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_OPTIONAL_CIV}));
EXPECT_THAT(
spvAlternatePatternFollowingImmediate({SPV_OPERAND_TYPE_CAPABILITY}),
Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_OPTIONAL_CIV}));
EXPECT_THAT(
spvAlternatePatternFollowingImmediate({SPV_OPERAND_TYPE_LOOP_CONTROL}),
Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_OPTIONAL_CIV}));
EXPECT_THAT(spvAlternatePatternFollowingImmediate(
{SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER}),
Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_OPTIONAL_CIV}));
EXPECT_THAT(spvAlternatePatternFollowingImmediate({SPV_OPERAND_TYPE_ID}),
Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_OPTIONAL_CIV}));
}
TEST(AlternatePatternFollowingImmediate, SingleResultId) {
EXPECT_THAT(
spvAlternatePatternFollowingImmediate({SPV_OPERAND_TYPE_RESULT_ID}),
Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_OPTIONAL_CIV,
SPV_OPERAND_TYPE_RESULT_ID}));
}
TEST(AlternatePatternFollowingImmediate, MultipleNonResultIds) {
EXPECT_THAT(
spvAlternatePatternFollowingImmediate(
{SPV_OPERAND_TYPE_VARIABLE_ID_LITERAL_INTEGER,
SPV_OPERAND_TYPE_CAPABILITY, SPV_OPERAND_TYPE_LOOP_CONTROL,
SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER, SPV_OPERAND_TYPE_ID}),
Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_OPTIONAL_CIV}));
}
TEST(AlternatePatternFollowingImmediate, ResultIdFront) {
EXPECT_THAT(spvAlternatePatternFollowingImmediate(
{SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}),
Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_OPTIONAL_CIV,
SPV_OPERAND_TYPE_RESULT_ID,
SPV_OPERAND_TYPE_OPTIONAL_CIV}));
EXPECT_THAT(
spvAlternatePatternFollowingImmediate({SPV_OPERAND_TYPE_RESULT_ID,
SPV_OPERAND_TYPE_FP_ROUNDING_MODE,
SPV_OPERAND_TYPE_ID}),
Eq(spv_operand_pattern_t{
SPV_OPERAND_TYPE_OPTIONAL_CIV, SPV_OPERAND_TYPE_RESULT_ID,
SPV_OPERAND_TYPE_OPTIONAL_CIV, SPV_OPERAND_TYPE_OPTIONAL_CIV}));
EXPECT_THAT(
spvAlternatePatternFollowingImmediate(
{SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_DIMENSIONALITY,
SPV_OPERAND_TYPE_LINKAGE_TYPE,
SPV_OPERAND_TYPE_FUNCTION_PARAMETER_ATTRIBUTE,
SPV_OPERAND_TYPE_FP_ROUNDING_MODE, SPV_OPERAND_TYPE_ID,
SPV_OPERAND_TYPE_VARIABLE_ID}),
Eq(spv_operand_pattern_t{
SPV_OPERAND_TYPE_OPTIONAL_CIV, SPV_OPERAND_TYPE_RESULT_ID,
SPV_OPERAND_TYPE_OPTIONAL_CIV, SPV_OPERAND_TYPE_OPTIONAL_CIV,
SPV_OPERAND_TYPE_OPTIONAL_CIV, SPV_OPERAND_TYPE_OPTIONAL_CIV,
SPV_OPERAND_TYPE_OPTIONAL_CIV, SPV_OPERAND_TYPE_OPTIONAL_CIV}));
}
TEST(AlternatePatternFollowingImmediate, ResultIdMiddle) {
EXPECT_THAT(spvAlternatePatternFollowingImmediate(
{SPV_OPERAND_TYPE_FP_ROUNDING_MODE,
SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}),
Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_OPTIONAL_CIV,
SPV_OPERAND_TYPE_RESULT_ID,
SPV_OPERAND_TYPE_OPTIONAL_CIV}));
EXPECT_THAT(
spvAlternatePatternFollowingImmediate(
{SPV_OPERAND_TYPE_DIMENSIONALITY, SPV_OPERAND_TYPE_LINKAGE_TYPE,
SPV_OPERAND_TYPE_FUNCTION_PARAMETER_ATTRIBUTE,
SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_FP_ROUNDING_MODE,
SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_VARIABLE_ID}),
Eq(spv_operand_pattern_t{
SPV_OPERAND_TYPE_OPTIONAL_CIV, SPV_OPERAND_TYPE_RESULT_ID,
SPV_OPERAND_TYPE_OPTIONAL_CIV, SPV_OPERAND_TYPE_OPTIONAL_CIV,
SPV_OPERAND_TYPE_OPTIONAL_CIV}));
}
TEST(AlternatePatternFollowingImmediate, ResultIdBack) {
EXPECT_THAT(spvAlternatePatternFollowingImmediate(
{SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID}),
Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_OPTIONAL_CIV,
SPV_OPERAND_TYPE_RESULT_ID}));
EXPECT_THAT(spvAlternatePatternFollowingImmediate(
{SPV_OPERAND_TYPE_FP_ROUNDING_MODE, SPV_OPERAND_TYPE_ID,
SPV_OPERAND_TYPE_RESULT_ID}),
Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_OPTIONAL_CIV,
SPV_OPERAND_TYPE_RESULT_ID}));
EXPECT_THAT(
spvAlternatePatternFollowingImmediate(
{SPV_OPERAND_TYPE_DIMENSIONALITY, SPV_OPERAND_TYPE_LINKAGE_TYPE,
SPV_OPERAND_TYPE_FUNCTION_PARAMETER_ATTRIBUTE,
SPV_OPERAND_TYPE_FP_ROUNDING_MODE, SPV_OPERAND_TYPE_ID,
SPV_OPERAND_TYPE_VARIABLE_ID, SPV_OPERAND_TYPE_RESULT_ID}),
Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_OPTIONAL_CIV,
SPV_OPERAND_TYPE_RESULT_ID}));
}
} // namespace
} // namespace spvtools

View File

@@ -1,75 +0,0 @@
// Copyright (c) 2015-2016 The Khronos Group 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 <vector>
#include "test/unit_spirv.h"
namespace spvtools {
namespace {
using GetTargetTest = ::testing::TestWithParam<spv_target_env>;
using ::testing::ValuesIn;
TEST_P(GetTargetTest, Default) {
spv_operand_table table;
ASSERT_EQ(SPV_SUCCESS, spvOperandTableGet(&table, GetParam()));
ASSERT_NE(0u, table->count);
ASSERT_NE(nullptr, table->types);
}
TEST_P(GetTargetTest, InvalidPointerTable) {
ASSERT_EQ(SPV_ERROR_INVALID_POINTER, spvOperandTableGet(nullptr, GetParam()));
}
INSTANTIATE_TEST_SUITE_P(OperandTableGet, GetTargetTest,
ValuesIn(std::vector<spv_target_env>{
SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1,
SPV_ENV_VULKAN_1_0}));
TEST(OperandString, AllAreDefinedExceptVariable) {
// None has no string, so don't test it.
EXPECT_EQ(0u, SPV_OPERAND_TYPE_NONE);
// Start testing at enum with value 1, skipping None.
for (int i = 1; i < int(SPV_OPERAND_TYPE_FIRST_VARIABLE_TYPE); i++) {
EXPECT_NE(nullptr, spvOperandTypeStr(static_cast<spv_operand_type_t>(i)))
<< " Operand type " << i;
}
}
TEST(OperandIsConcreteMask, Sample) {
// Check a few operand types preceding the concrete mask types.
EXPECT_FALSE(spvOperandIsConcreteMask(SPV_OPERAND_TYPE_NONE));
EXPECT_FALSE(spvOperandIsConcreteMask(SPV_OPERAND_TYPE_ID));
EXPECT_FALSE(spvOperandIsConcreteMask(SPV_OPERAND_TYPE_LITERAL_INTEGER));
EXPECT_FALSE(spvOperandIsConcreteMask(SPV_OPERAND_TYPE_CAPABILITY));
// Check all the concrete mask operand types.
EXPECT_TRUE(spvOperandIsConcreteMask(SPV_OPERAND_TYPE_IMAGE));
EXPECT_TRUE(spvOperandIsConcreteMask(SPV_OPERAND_TYPE_FP_FAST_MATH_MODE));
EXPECT_TRUE(spvOperandIsConcreteMask(SPV_OPERAND_TYPE_SELECTION_CONTROL));
EXPECT_TRUE(spvOperandIsConcreteMask(SPV_OPERAND_TYPE_LOOP_CONTROL));
EXPECT_TRUE(spvOperandIsConcreteMask(SPV_OPERAND_TYPE_FUNCTION_CONTROL));
EXPECT_TRUE(spvOperandIsConcreteMask(SPV_OPERAND_TYPE_MEMORY_ACCESS));
// Check a few operand types after the concrete mask types, including the
// optional forms for Image and MemoryAccess.
EXPECT_FALSE(spvOperandIsConcreteMask(SPV_OPERAND_TYPE_OPTIONAL_ID));
EXPECT_FALSE(spvOperandIsConcreteMask(SPV_OPERAND_TYPE_OPTIONAL_IMAGE));
EXPECT_FALSE(
spvOperandIsConcreteMask(SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS));
}
} // namespace
} // namespace spvtools

View File

@@ -1,107 +0,0 @@
# Copyright (c) 2016 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.
add_subdirectory(dominator_tree)
add_subdirectory(loop_optimizations)
add_spvtools_unittest(TARGET opt
SRCS aggressive_dead_code_elim_test.cpp
amd_ext_to_khr.cpp
assembly_builder_test.cpp
block_merge_test.cpp
ccp_test.cpp
cfg_cleanup_test.cpp
cfg_test.cpp
code_sink_test.cpp
combine_access_chains_test.cpp
compact_ids_test.cpp
constants_test.cpp
constant_manager_test.cpp
convert_relaxed_to_half_test.cpp
copy_prop_array_test.cpp
dead_branch_elim_test.cpp
dead_insert_elim_test.cpp
dead_variable_elim_test.cpp
decompose_initialized_variables_test.cpp
decoration_manager_test.cpp
def_use_test.cpp
desc_sroa_test.cpp
eliminate_dead_const_test.cpp
eliminate_dead_functions_test.cpp
eliminate_dead_member_test.cpp
feature_manager_test.cpp
fix_storage_class_test.cpp
flatten_decoration_test.cpp
fold_spec_const_op_composite_test.cpp
fold_test.cpp
freeze_spec_const_test.cpp
function_test.cpp
generate_webgpu_initializers_test.cpp
graphics_robust_access_test.cpp
if_conversion_test.cpp
inline_opaque_test.cpp
inline_test.cpp
insert_extract_elim_test.cpp
inst_bindless_check_test.cpp
inst_buff_addr_check_test.cpp
instruction_list_test.cpp
instruction_test.cpp
ir_builder.cpp
ir_context_test.cpp
ir_loader_test.cpp
iterator_test.cpp
legalize_vector_shuffle_test.cpp
line_debug_info_test.cpp
local_access_chain_convert_test.cpp
local_redundancy_elimination_test.cpp
local_single_block_elim.cpp
local_single_store_elim_test.cpp
local_ssa_elim_test.cpp
module_test.cpp
module_utils.h
optimizer_test.cpp
pass_manager_test.cpp
pass_merge_return_test.cpp
pass_remove_duplicates_test.cpp
pass_utils.cpp
private_to_local_test.cpp
process_lines_test.cpp
propagator_test.cpp
reduce_load_size_test.cpp
redundancy_elimination_test.cpp
register_liveness.cpp
relax_float_ops_test.cpp
replace_invalid_opc_test.cpp
scalar_analysis.cpp
scalar_replacement_test.cpp
set_spec_const_default_value_test.cpp
simplification_test.cpp
split_invalid_unreachable_test.cpp
strength_reduction_test.cpp
strip_atomic_counter_memory_test.cpp
strip_debug_info_test.cpp
strip_reflect_info_test.cpp
struct_cfg_analysis_test.cpp
type_manager_test.cpp
types_test.cpp
unify_const_test.cpp
upgrade_memory_model_test.cpp
utils_test.cpp pass_utils.cpp
value_table_test.cpp
vector_dce_test.cpp
workaround1209_test.cpp
wrap_opkill_test.cpp
LIBS SPIRV-Tools-opt
PCH_FILE pch_test_opt
)

File diff suppressed because it is too large Load Diff

View File

@@ -1,918 +0,0 @@
// Copyright (c) 2019 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 <vector>
#include "gmock/gmock.h"
#include "test/opt/pass_fixture.h"
#include "test/opt/pass_utils.h"
namespace spvtools {
namespace opt {
namespace {
using AmdExtToKhrTest = PassTest<::testing::Test>;
using ::testing::HasSubstr;
std::string GetTest(std::string op_code, std::string new_op_code) {
const std::string text = R"(
; CHECK: OpCapability Shader
; CHECK-NOT: OpExtension "SPV_AMD_shader_ballot"
; CHECK: OpFunction
; CHECK-NEXT: OpLabel
; CHECK-NEXT: [[undef:%\w+]] = OpUndef %uint
; CHECK-NEXT: )" + new_op_code +
R"( %uint %uint_3 Reduce [[undef]]
OpCapability Shader
OpCapability Groups
OpExtension "SPV_AMD_shader_ballot"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "func"
OpExecutionMode %1 OriginUpperLeft
%void = OpTypeVoid
%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%uint_3 = OpConstant %uint 3
%1 = OpFunction %void None %3
%6 = OpLabel
%7 = OpUndef %uint
%8 = )" + op_code +
R"( %uint %uint_3 Reduce %7
OpReturn
OpFunctionEnd
)";
return text;
}
TEST_F(AmdExtToKhrTest, ReplaceGroupIAddNonUniformAMD) {
std::string text =
GetTest("OpGroupIAddNonUniformAMD", "OpGroupNonUniformIAdd");
SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true);
}
TEST_F(AmdExtToKhrTest, ReplaceGroupFAddNonUniformAMD) {
std::string text =
GetTest("OpGroupFAddNonUniformAMD", "OpGroupNonUniformFAdd");
SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true);
}
TEST_F(AmdExtToKhrTest, ReplaceGroupUMinNonUniformAMD) {
std::string text =
GetTest("OpGroupUMinNonUniformAMD", "OpGroupNonUniformUMin");
SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true);
}
TEST_F(AmdExtToKhrTest, ReplaceGroupSMinNonUniformAMD) {
std::string text =
GetTest("OpGroupSMinNonUniformAMD", "OpGroupNonUniformSMin");
SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true);
}
TEST_F(AmdExtToKhrTest, ReplaceGroupFMinNonUniformAMD) {
std::string text =
GetTest("OpGroupFMinNonUniformAMD", "OpGroupNonUniformFMin");
SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true);
}
TEST_F(AmdExtToKhrTest, ReplaceGroupUMaxNonUniformAMD) {
std::string text =
GetTest("OpGroupUMaxNonUniformAMD", "OpGroupNonUniformUMax");
SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true);
}
TEST_F(AmdExtToKhrTest, ReplaceGroupSMaxNonUniformAMD) {
std::string text =
GetTest("OpGroupSMaxNonUniformAMD", "OpGroupNonUniformSMax");
SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true);
}
TEST_F(AmdExtToKhrTest, ReplaceGroupFMaxNonUniformAMD) {
std::string text =
GetTest("OpGroupFMaxNonUniformAMD", "OpGroupNonUniformFMax");
SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true);
}
TEST_F(AmdExtToKhrTest, ReplaceMbcntAMD) {
const std::string text = R"(
; CHECK: OpCapability Shader
; CHECK-NOT: OpExtension "SPV_AMD_shader_ballot"
; CHECK-NOT: OpExtInstImport "SPV_AMD_shader_ballot"
; CHECK: OpDecorate [[var:%\w+]] BuiltIn SubgroupLtMask
; CHECK: [[var]] = OpVariable %_ptr_Input_v4uint Input
; CHECK: OpFunction
; CHECK-NEXT: OpLabel
; CHECK-NEXT: [[ld:%\w+]] = OpLoad %v4uint [[var]]
; CHECK-NEXT: [[shuffle:%\w+]] = OpVectorShuffle %v2uint [[ld]] [[ld]] 0 1
; CHECK-NEXT: [[bitcast:%\w+]] = OpBitcast %ulong [[shuffle]]
; CHECK-NEXT: [[and:%\w+]] = OpBitwiseAnd %ulong [[bitcast]] %ulong_0
; CHECK-NEXT: [[result:%\w+]] = OpBitCount %uint [[and]]
OpCapability Shader
OpCapability Int64
OpExtension "SPV_AMD_shader_ballot"
%1 = OpExtInstImport "SPV_AMD_shader_ballot"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "func"
OpExecutionMode %2 OriginUpperLeft
%void = OpTypeVoid
%4 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%ulong = OpTypeInt 64 0
%ulong_0 = OpConstant %ulong 0
%2 = OpFunction %void None %4
%8 = OpLabel
%9 = OpExtInst %uint %1 MbcntAMD %ulong_0
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true);
}
TEST_F(AmdExtToKhrTest, ReplaceSwizzleInvocationsAMD) {
const std::string text = R"(
; CHECK: OpCapability Shader
; CHECK-NOT: OpExtension "SPV_AMD_shader_ballot"
; CHECK-NOT: OpExtInstImport "SPV_AMD_shader_ballot"
; CHECK: OpDecorate [[var:%\w+]] BuiltIn SubgroupLocalInvocationId
; CHECK: [[subgroup:%\w+]] = OpConstant %uint 3
; CHECK: [[offset:%\w+]] = OpConstantComposite %v4uint
; CHECK: [[var]] = OpVariable %_ptr_Input_uint Input
; CHECK: [[uint_max:%\w+]] = OpConstant %uint 4294967295
; CHECK: [[ballot_value:%\w+]] = OpConstantComposite %v4uint [[uint_max]] [[uint_max]] [[uint_max]] [[uint_max]]
; CHECK: [[null:%\w+]] = OpConstantNull [[type:%\w+]]
; CHECK: OpFunction
; CHECK-NEXT: OpLabel
; CHECK-NEXT: [[data:%\w+]] = OpUndef [[type]]
; CHECK-NEXT: [[id:%\w+]] = OpLoad %uint [[var]]
; CHECK-NEXT: [[quad_idx:%\w+]] = OpBitwiseAnd %uint [[id]] %uint_3
; CHECK-NEXT: [[quad_ldr:%\w+]] = OpBitwiseXor %uint [[id]] [[quad_idx]]
; CHECK-NEXT: [[my_offset:%\w+]] = OpVectorExtractDynamic %uint [[offset]] [[quad_idx]]
; CHECK-NEXT: [[target_inv:%\w+]] = OpIAdd %uint [[quad_ldr]] [[my_offset]]
; CHECK-NEXT: [[is_active:%\w+]] = OpGroupNonUniformBallotBitExtract %bool [[subgroup]] [[ballot_value]] [[target_inv]]
; CHECK-NEXT: [[shuffle:%\w+]] = OpGroupNonUniformShuffle [[type]] [[subgroup]] [[data]] [[target_inv]]
; CHECK-NEXT: [[result:%\w+]] = OpSelect [[type]] [[is_active]] [[shuffle]] [[null]]
OpCapability Shader
OpExtension "SPV_AMD_shader_ballot"
%ext = OpExtInstImport "SPV_AMD_shader_ballot"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "func"
OpExecutionMode %1 OriginUpperLeft
%void = OpTypeVoid
%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%uint_x = OpConstant %uint 1
%uint_y = OpConstant %uint 2
%uint_z = OpConstant %uint 3
%uint_w = OpConstant %uint 0
%v4uint = OpTypeVector %uint 4
%offset = OpConstantComposite %v4uint %uint_x %uint_y %uint_z %uint_x
%1 = OpFunction %void None %3
%6 = OpLabel
%data = OpUndef %uint
%9 = OpExtInst %uint %ext SwizzleInvocationsAMD %data %offset
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true);
}
TEST_F(AmdExtToKhrTest, ReplaceSwizzleInvocationsMaskedAMD) {
const std::string text = R"(
; CHECK: OpCapability Shader
; CHECK-NOT: OpExtension "SPV_AMD_shader_ballot"
; CHECK-NOT: OpExtInstImport "SPV_AMD_shader_ballot"
; CHECK: OpDecorate [[var:%\w+]] BuiltIn SubgroupLocalInvocationId
; CHECK: [[x:%\w+]] = OpConstant %uint 19
; CHECK: [[y:%\w+]] = OpConstant %uint 12
; CHECK: [[z:%\w+]] = OpConstant %uint 16
; CHECK: [[var]] = OpVariable %_ptr_Input_uint Input
; CHECK: [[mask_extend:%\w+]] = OpConstant %uint 4294967264
; CHECK: [[uint_max:%\w+]] = OpConstant %uint 4294967295
; CHECK: [[subgroup:%\w+]] = OpConstant %uint 3
; CHECK: [[ballot_value:%\w+]] = OpConstantComposite %v4uint [[uint_max]] [[uint_max]] [[uint_max]] [[uint_max]]
; CHECK: [[null:%\w+]] = OpConstantNull [[type:%\w+]]
; CHECK: OpFunction
; CHECK-NEXT: OpLabel
; CHECK-NEXT: [[data:%\w+]] = OpUndef [[type]]
; CHECK-NEXT: [[id:%\w+]] = OpLoad %uint [[var]]
; CHECK-NEXT: [[and_mask:%\w+]] = OpBitwiseOr %uint [[x]] [[mask_extend]]
; CHECK-NEXT: [[and:%\w+]] = OpBitwiseAnd %uint [[id]] [[and_mask]]
; CHECK-NEXT: [[or:%\w+]] = OpBitwiseOr %uint [[and]] [[y]]
; CHECK-NEXT: [[target_inv:%\w+]] = OpBitwiseXor %uint [[or]] [[z]]
; CHECK-NEXT: [[is_active:%\w+]] = OpGroupNonUniformBallotBitExtract %bool [[subgroup]] [[ballot_value]] [[target_inv]]
; CHECK-NEXT: [[shuffle:%\w+]] = OpGroupNonUniformShuffle [[type]] [[subgroup]] [[data]] [[target_inv]]
; CHECK-NEXT: [[result:%\w+]] = OpSelect [[type]] [[is_active]] [[shuffle]] [[null]]
OpCapability Shader
OpExtension "SPV_AMD_shader_ballot"
%ext = OpExtInstImport "SPV_AMD_shader_ballot"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "func"
OpExecutionMode %1 OriginUpperLeft
%void = OpTypeVoid
%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%uint_x = OpConstant %uint 19
%uint_y = OpConstant %uint 12
%uint_z = OpConstant %uint 16
%v3uint = OpTypeVector %uint 3
%mask = OpConstantComposite %v3uint %uint_x %uint_y %uint_z
%1 = OpFunction %void None %3
%6 = OpLabel
%data = OpUndef %uint
%9 = OpExtInst %uint %ext SwizzleInvocationsMaskedAMD %data %mask
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true);
}
TEST_F(AmdExtToKhrTest, ReplaceWriteInvocationAMD) {
const std::string text = R"(
; CHECK: OpCapability Shader
; CHECK-NOT: OpExtension "SPV_AMD_shader_ballot"
; CHECK-NOT: OpExtInstImport "SPV_AMD_shader_ballot"
; CHECK: OpDecorate [[var:%\w+]] BuiltIn SubgroupLocalInvocationId
; CHECK: [[var]] = OpVariable %_ptr_Input_uint Input
; CHECK: OpFunction
; CHECK-NEXT: OpLabel
; CHECK-NEXT: [[input_val:%\w+]] = OpUndef %uint
; CHECK-NEXT: [[write_val:%\w+]] = OpUndef %uint
; CHECK-NEXT: [[ld:%\w+]] = OpLoad %uint [[var]]
; CHECK-NEXT: [[cmp:%\w+]] = OpIEqual %bool [[ld]] %uint_3
; CHECK-NEXT: [[result:%\w+]] = OpSelect %uint [[cmp]] [[write_val]] [[input_val]]
OpCapability Shader
OpExtension "SPV_AMD_shader_ballot"
%ext = OpExtInstImport "SPV_AMD_shader_ballot"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "func"
OpExecutionMode %1 OriginUpperLeft
%void = OpTypeVoid
%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%uint_3 = OpConstant %uint 3
%1 = OpFunction %void None %3
%6 = OpLabel
%7 = OpUndef %uint
%8 = OpUndef %uint
%9 = OpExtInst %uint %ext WriteInvocationAMD %7 %8 %uint_3
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true);
}
TEST_F(AmdExtToKhrTest, ReplaceFMin3AMD) {
const std::string text = R"(
; CHECK: OpCapability Shader
; CHECK-NOT: OpExtension "SPV_AMD_shader_trinary_minmax"
; CHECK-NOT: OpExtInstImport "SPV_AMD_shader_trinary_minmax"
; CHECK: [[ext:%\w+]] = OpExtInstImport "GLSL.std.450"
; CHECK: [[type:%\w+]] = OpTypeFloat 32
; CHECK: OpFunction
; CHECK-NEXT: OpLabel
; CHECK-NEXT: [[x:%\w+]] = OpUndef [[type]]
; CHECK-NEXT: [[y:%\w+]] = OpUndef [[type]]
; CHECK-NEXT: [[z:%\w+]] = OpUndef [[type]]
; CHECK-NEXT: [[temp:%\w+]] = OpExtInst [[type]] [[ext]] FMin [[x]] [[y]]
; CHECK-NEXT: [[result:%\w+]] = OpExtInst [[type]] [[ext]] FMin [[temp]] [[z]]
OpCapability Shader
OpExtension "SPV_AMD_shader_trinary_minmax"
%ext = OpExtInstImport "SPV_AMD_shader_trinary_minmax"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "func"
OpExecutionMode %1 OriginUpperLeft
%void = OpTypeVoid
%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%float = OpTypeFloat 32
%uint_3 = OpConstant %uint 3
%1 = OpFunction %void None %3
%6 = OpLabel
%7 = OpUndef %float
%8 = OpUndef %float
%9 = OpUndef %float
%10 = OpExtInst %float %ext FMin3AMD %7 %8 %9
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true);
}
TEST_F(AmdExtToKhrTest, ReplaceSMin3AMD) {
const std::string text = R"(
; CHECK: OpCapability Shader
; CHECK-NOT: OpExtension "SPV_AMD_shader_trinary_minmax"
; CHECK-NOT: OpExtInstImport "SPV_AMD_shader_trinary_minmax"
; CHECK: [[ext:%\w+]] = OpExtInstImport "GLSL.std.450"
; CHECK: [[type:%\w+]] = OpTypeInt 32 1
; CHECK: OpFunction
; CHECK-NEXT: OpLabel
; CHECK-NEXT: [[x:%\w+]] = OpUndef [[type]]
; CHECK-NEXT: [[y:%\w+]] = OpUndef [[type]]
; CHECK-NEXT: [[z:%\w+]] = OpUndef [[type]]
; CHECK-NEXT: [[temp:%\w+]] = OpExtInst [[type]] [[ext]] SMin [[x]] [[y]]
; CHECK-NEXT: [[result:%\w+]] = OpExtInst [[type]] [[ext]] SMin [[temp]] [[z]]
OpCapability Shader
OpExtension "SPV_AMD_shader_trinary_minmax"
%ext = OpExtInstImport "SPV_AMD_shader_trinary_minmax"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "func"
OpExecutionMode %1 OriginUpperLeft
%void = OpTypeVoid
%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%int = OpTypeInt 32 1
%float = OpTypeFloat 32
%uint_3 = OpConstant %uint 3
%1 = OpFunction %void None %3
%6 = OpLabel
%7 = OpUndef %int
%8 = OpUndef %int
%9 = OpUndef %int
%10 = OpExtInst %int %ext SMin3AMD %7 %8 %9
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true);
}
TEST_F(AmdExtToKhrTest, ReplaceUMin3AMD) {
const std::string text = R"(
; CHECK: OpCapability Shader
; CHECK-NOT: OpExtension "SPV_AMD_shader_trinary_minmax"
; CHECK-NOT: OpExtInstImport "SPV_AMD_shader_trinary_minmax"
; CHECK: [[ext:%\w+]] = OpExtInstImport "GLSL.std.450"
; CHECK: [[type:%\w+]] = OpTypeInt 32 0
; CHECK: OpFunction
; CHECK-NEXT: OpLabel
; CHECK-NEXT: [[x:%\w+]] = OpUndef [[type]]
; CHECK-NEXT: [[y:%\w+]] = OpUndef [[type]]
; CHECK-NEXT: [[z:%\w+]] = OpUndef [[type]]
; CHECK-NEXT: [[temp:%\w+]] = OpExtInst [[type]] [[ext]] UMin [[x]] [[y]]
; CHECK-NEXT: [[result:%\w+]] = OpExtInst [[type]] [[ext]] UMin [[temp]] [[z]]
OpCapability Shader
OpExtension "SPV_AMD_shader_trinary_minmax"
%ext = OpExtInstImport "SPV_AMD_shader_trinary_minmax"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "func"
OpExecutionMode %1 OriginUpperLeft
%void = OpTypeVoid
%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%int = OpTypeInt 32 1
%float = OpTypeFloat 32
%uint_3 = OpConstant %uint 3
%1 = OpFunction %void None %3
%6 = OpLabel
%7 = OpUndef %uint
%8 = OpUndef %uint
%9 = OpUndef %uint
%10 = OpExtInst %uint %ext UMin3AMD %7 %8 %9
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true);
}
TEST_F(AmdExtToKhrTest, ReplaceFMax3AMD) {
const std::string text = R"(
; CHECK: OpCapability Shader
; CHECK-NOT: OpExtension "SPV_AMD_shader_trinary_minmax"
; CHECK-NOT: OpExtInstImport "SPV_AMD_shader_trinary_minmax"
; CHECK: [[ext:%\w+]] = OpExtInstImport "GLSL.std.450"
; CHECK: [[type:%\w+]] = OpTypeFloat 32
; CHECK: OpFunction
; CHECK-NEXT: OpLabel
; CHECK-NEXT: [[x:%\w+]] = OpUndef [[type]]
; CHECK-NEXT: [[y:%\w+]] = OpUndef [[type]]
; CHECK-NEXT: [[z:%\w+]] = OpUndef [[type]]
; CHECK-NEXT: [[temp:%\w+]] = OpExtInst [[type]] [[ext]] FMax [[x]] [[y]]
; CHECK-NEXT: [[result:%\w+]] = OpExtInst [[type]] [[ext]] FMax [[temp]] [[z]]
OpCapability Shader
OpExtension "SPV_AMD_shader_trinary_minmax"
%ext = OpExtInstImport "SPV_AMD_shader_trinary_minmax"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "func"
OpExecutionMode %1 OriginUpperLeft
%void = OpTypeVoid
%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%float = OpTypeFloat 32
%uint_3 = OpConstant %uint 3
%1 = OpFunction %void None %3
%6 = OpLabel
%7 = OpUndef %float
%8 = OpUndef %float
%9 = OpUndef %float
%10 = OpExtInst %float %ext FMax3AMD %7 %8 %9
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true);
}
TEST_F(AmdExtToKhrTest, ReplaceSMax3AMD) {
const std::string text = R"(
; CHECK: OpCapability Shader
; CHECK-NOT: OpExtension "SPV_AMD_shader_trinary_minmax"
; CHECK-NOT: OpExtInstImport "SPV_AMD_shader_trinary_minmax"
; CHECK: [[ext:%\w+]] = OpExtInstImport "GLSL.std.450"
; CHECK: [[type:%\w+]] = OpTypeInt 32 1
; CHECK: OpFunction
; CHECK-NEXT: OpLabel
; CHECK-NEXT: [[x:%\w+]] = OpUndef [[type]]
; CHECK-NEXT: [[y:%\w+]] = OpUndef [[type]]
; CHECK-NEXT: [[z:%\w+]] = OpUndef [[type]]
; CHECK-NEXT: [[temp:%\w+]] = OpExtInst [[type]] [[ext]] SMax [[x]] [[y]]
; CHECK-NEXT: [[result:%\w+]] = OpExtInst [[type]] [[ext]] SMax [[temp]] [[z]]
OpCapability Shader
OpExtension "SPV_AMD_shader_trinary_minmax"
%ext = OpExtInstImport "SPV_AMD_shader_trinary_minmax"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "func"
OpExecutionMode %1 OriginUpperLeft
%void = OpTypeVoid
%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%int = OpTypeInt 32 1
%float = OpTypeFloat 32
%uint_3 = OpConstant %uint 3
%1 = OpFunction %void None %3
%6 = OpLabel
%7 = OpUndef %int
%8 = OpUndef %int
%9 = OpUndef %int
%10 = OpExtInst %int %ext SMax3AMD %7 %8 %9
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true);
}
TEST_F(AmdExtToKhrTest, ReplaceUMax3AMD) {
const std::string text = R"(
; CHECK: OpCapability Shader
; CHECK-NOT: OpExtension "SPV_AMD_shader_trinary_minmax"
; CHECK-NOT: OpExtInstImport "SPV_AMD_shader_trinary_minmax"
; CHECK: [[ext:%\w+]] = OpExtInstImport "GLSL.std.450"
; CHECK: [[type:%\w+]] = OpTypeInt 32 0
; CHECK: OpFunction
; CHECK-NEXT: OpLabel
; CHECK-NEXT: [[x:%\w+]] = OpUndef [[type]]
; CHECK-NEXT: [[y:%\w+]] = OpUndef [[type]]
; CHECK-NEXT: [[z:%\w+]] = OpUndef [[type]]
; CHECK-NEXT: [[temp:%\w+]] = OpExtInst [[type]] [[ext]] UMax [[x]] [[y]]
; CHECK-NEXT: [[result:%\w+]] = OpExtInst [[type]] [[ext]] UMax [[temp]] [[z]]
OpCapability Shader
OpExtension "SPV_AMD_shader_trinary_minmax"
%ext = OpExtInstImport "SPV_AMD_shader_trinary_minmax"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "func"
OpExecutionMode %1 OriginUpperLeft
%void = OpTypeVoid
%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%int = OpTypeInt 32 1
%float = OpTypeFloat 32
%uint_3 = OpConstant %uint 3
%1 = OpFunction %void None %3
%6 = OpLabel
%7 = OpUndef %uint
%8 = OpUndef %uint
%9 = OpUndef %uint
%10 = OpExtInst %uint %ext UMax3AMD %7 %8 %9
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true);
}
TEST_F(AmdExtToKhrTest, ReplaceVecUMax3AMD) {
const std::string text = R"(
; CHECK: OpCapability Shader
; CHECK-NOT: OpExtension "SPV_AMD_shader_trinary_minmax"
; CHECK-NOT: OpExtInstImport "SPV_AMD_shader_trinary_minmax"
; CHECK: [[ext:%\w+]] = OpExtInstImport "GLSL.std.450"
; CHECK: [[type:%\w+]] = OpTypeVector
; CHECK: OpFunction
; CHECK-NEXT: OpLabel
; CHECK-NEXT: [[x:%\w+]] = OpUndef [[type]]
; CHECK-NEXT: [[y:%\w+]] = OpUndef [[type]]
; CHECK-NEXT: [[z:%\w+]] = OpUndef [[type]]
; CHECK-NEXT: [[temp:%\w+]] = OpExtInst [[type]] [[ext]] UMax [[x]] [[y]]
; CHECK-NEXT: [[result:%\w+]] = OpExtInst [[type]] [[ext]] UMax [[temp]] [[z]]
OpCapability Shader
OpExtension "SPV_AMD_shader_trinary_minmax"
%ext = OpExtInstImport "SPV_AMD_shader_trinary_minmax"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "func"
OpExecutionMode %1 OriginUpperLeft
%void = OpTypeVoid
%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%vec = OpTypeVector %uint 4
%int = OpTypeInt 32 1
%float = OpTypeFloat 32
%uint_3 = OpConstant %uint 3
%1 = OpFunction %void None %3
%6 = OpLabel
%7 = OpUndef %vec
%8 = OpUndef %vec
%9 = OpUndef %vec
%10 = OpExtInst %vec %ext UMax3AMD %7 %8 %9
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true);
}
TEST_F(AmdExtToKhrTest, ReplaceFMid3AMD) {
const std::string text = R"(
; CHECK: OpCapability Shader
; CHECK-NOT: OpExtension "SPV_AMD_shader_trinary_minmax"
; CHECK-NOT: OpExtInstImport "SPV_AMD_shader_trinary_minmax"
; CHECK: [[ext:%\w+]] = OpExtInstImport "GLSL.std.450"
; CHECK: [[type:%\w+]] = OpTypeFloat 32
; CHECK: OpFunction
; CHECK-NEXT: OpLabel
; CHECK-NEXT: [[x:%\w+]] = OpUndef [[type]]
; CHECK-NEXT: [[y:%\w+]] = OpUndef [[type]]
; CHECK-NEXT: [[z:%\w+]] = OpUndef [[type]]
; CHECK-NEXT: [[min:%\w+]] = OpExtInst [[type]] [[ext]] FMin [[y]] [[z]]
; CHECK-NEXT: [[max:%\w+]] = OpExtInst [[type]] [[ext]] FMax [[y]] [[z]]
; CHECK-NEXT: [[result:%\w+]] = OpExtInst [[type]] [[ext]] FClamp [[x]] [[min]] [[max]]
OpCapability Shader
OpExtension "SPV_AMD_shader_trinary_minmax"
%ext = OpExtInstImport "SPV_AMD_shader_trinary_minmax"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "func"
OpExecutionMode %1 OriginUpperLeft
%void = OpTypeVoid
%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%float = OpTypeFloat 32
%uint_3 = OpConstant %uint 3
%1 = OpFunction %void None %3
%6 = OpLabel
%7 = OpUndef %float
%8 = OpUndef %float
%9 = OpUndef %float
%10 = OpExtInst %float %ext FMid3AMD %7 %8 %9
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true);
}
TEST_F(AmdExtToKhrTest, ReplaceSMid3AMD) {
const std::string text = R"(
; CHECK: OpCapability Shader
; CHECK-NOT: OpExtension "SPV_AMD_shader_trinary_minmax"
; CHECK-NOT: OpExtInstImport "SPV_AMD_shader_trinary_minmax"
; CHECK: [[ext:%\w+]] = OpExtInstImport "GLSL.std.450"
; CHECK: [[type:%\w+]] = OpTypeInt 32 1
; CHECK: OpFunction
; CHECK-NEXT: OpLabel
; CHECK-NEXT: [[x:%\w+]] = OpUndef [[type]]
; CHECK-NEXT: [[y:%\w+]] = OpUndef [[type]]
; CHECK-NEXT: [[z:%\w+]] = OpUndef [[type]]
; CHECK-NEXT: [[min:%\w+]] = OpExtInst [[type]] [[ext]] SMin [[y]] [[z]]
; CHECK-NEXT: [[max:%\w+]] = OpExtInst [[type]] [[ext]] SMax [[y]] [[z]]
; CHECK-NEXT: [[result:%\w+]] = OpExtInst [[type]] [[ext]] SClamp [[x]] [[min]] [[max]]
OpCapability Shader
OpExtension "SPV_AMD_shader_trinary_minmax"
%ext = OpExtInstImport "SPV_AMD_shader_trinary_minmax"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "func"
OpExecutionMode %1 OriginUpperLeft
%void = OpTypeVoid
%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%int = OpTypeInt 32 1
%float = OpTypeFloat 32
%uint_3 = OpConstant %uint 3
%1 = OpFunction %void None %3
%6 = OpLabel
%7 = OpUndef %int
%8 = OpUndef %int
%9 = OpUndef %int
%10 = OpExtInst %int %ext SMid3AMD %7 %8 %9
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true);
}
TEST_F(AmdExtToKhrTest, ReplaceUMid3AMD) {
const std::string text = R"(
; CHECK: OpCapability Shader
; CHECK-NOT: OpExtension "SPV_AMD_shader_trinary_minmax"
; CHECK-NOT: OpExtInstImport "SPV_AMD_shader_trinary_minmax"
; CHECK: [[ext:%\w+]] = OpExtInstImport "GLSL.std.450"
; CHECK: [[type:%\w+]] = OpTypeInt 32 0
; CHECK: OpFunction
; CHECK-NEXT: OpLabel
; CHECK-NEXT: [[x:%\w+]] = OpUndef [[type]]
; CHECK-NEXT: [[y:%\w+]] = OpUndef [[type]]
; CHECK-NEXT: [[z:%\w+]] = OpUndef [[type]]
; CHECK-NEXT: [[min:%\w+]] = OpExtInst [[type]] [[ext]] UMin [[y]] [[z]]
; CHECK-NEXT: [[max:%\w+]] = OpExtInst [[type]] [[ext]] UMax [[y]] [[z]]
; CHECK-NEXT: [[result:%\w+]] = OpExtInst [[type]] [[ext]] UClamp [[x]] [[min]] [[max]]
OpCapability Shader
OpExtension "SPV_AMD_shader_trinary_minmax"
%ext = OpExtInstImport "SPV_AMD_shader_trinary_minmax"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "func"
OpExecutionMode %1 OriginUpperLeft
%void = OpTypeVoid
%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%int = OpTypeInt 32 1
%float = OpTypeFloat 32
%uint_3 = OpConstant %uint 3
%1 = OpFunction %void None %3
%6 = OpLabel
%7 = OpUndef %uint
%8 = OpUndef %uint
%9 = OpUndef %uint
%10 = OpExtInst %uint %ext UMid3AMD %7 %8 %9
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true);
}
TEST_F(AmdExtToKhrTest, ReplaceVecUMid3AMD) {
const std::string text = R"(
; CHECK: OpCapability Shader
; CHECK-NOT: OpExtension "SPV_AMD_shader_trinary_minmax"
; CHECK-NOT: OpExtInstImport "SPV_AMD_shader_trinary_minmax"
; CHECK: [[ext:%\w+]] = OpExtInstImport "GLSL.std.450"
; CHECK: [[type:%\w+]] = OpTypeVector
; CHECK: OpFunction
; CHECK-NEXT: OpLabel
; CHECK-NEXT: [[x:%\w+]] = OpUndef [[type]]
; CHECK-NEXT: [[y:%\w+]] = OpUndef [[type]]
; CHECK-NEXT: [[z:%\w+]] = OpUndef [[type]]
; CHECK-NEXT: [[min:%\w+]] = OpExtInst [[type]] [[ext]] UMin [[y]] [[z]]
; CHECK-NEXT: [[max:%\w+]] = OpExtInst [[type]] [[ext]] UMax [[y]] [[z]]
; CHECK-NEXT: [[result:%\w+]] = OpExtInst [[type]] [[ext]] UClamp [[x]] [[min]] [[max]]
OpCapability Shader
OpExtension "SPV_AMD_shader_trinary_minmax"
%ext = OpExtInstImport "SPV_AMD_shader_trinary_minmax"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "func"
OpExecutionMode %1 OriginUpperLeft
%void = OpTypeVoid
%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%vec = OpTypeVector %uint 3
%int = OpTypeInt 32 1
%float = OpTypeFloat 32
%uint_3 = OpConstant %uint 3
%1 = OpFunction %void None %3
%6 = OpLabel
%7 = OpUndef %vec
%8 = OpUndef %vec
%9 = OpUndef %vec
%10 = OpExtInst %vec %ext UMid3AMD %7 %8 %9
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<AmdExtensionToKhrPass>(text, true);
}
TEST_F(AmdExtToKhrTest, ReplaceCubeFaceCoordAMD) {
// Sorry for the Check test. The code sequence is so long, I do not think
// that a match test would be anymore legible. This tests the replacement of
// the CubeFaceCoordAMD instruction.
const std::string before = R"(
OpCapability Shader
OpExtension "SPV_KHR_storage_buffer_storage_class"
OpExtension "SPV_AMD_gcn_shader"
%1 = OpExtInstImport "SPV_AMD_gcn_shader"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %2 "main"
OpExecutionMode %2 LocalSize 1 1 1
%void = OpTypeVoid
%4 = OpTypeFunction %void
%float = OpTypeFloat 32
%v2float = OpTypeVector %float 2
%v3float = OpTypeVector %float 3
%2 = OpFunction %void None %4
%8 = OpLabel
%9 = OpUndef %v3float
%10 = OpExtInst %v2float %1 CubeFaceCoordAMD %9
OpReturn
OpFunctionEnd
)";
const std::string after = R"(OpCapability Shader
OpExtension "SPV_KHR_storage_buffer_storage_class"
%12 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %2 "main"
OpExecutionMode %2 LocalSize 1 1 1
%void = OpTypeVoid
%4 = OpTypeFunction %void
%float = OpTypeFloat 32
%v2float = OpTypeVector %float 2
%v3float = OpTypeVector %float 3
%bool = OpTypeBool
%float_0 = OpConstant %float 0
%float_2 = OpConstant %float 2
%float_0_5 = OpConstant %float 0.5
%16 = OpConstantComposite %v2float %float_0_5 %float_0_5
%2 = OpFunction %void None %4
%8 = OpLabel
%9 = OpUndef %v3float
%17 = OpCompositeExtract %float %9 0
%18 = OpCompositeExtract %float %9 1
%19 = OpCompositeExtract %float %9 2
%20 = OpFNegate %float %17
%21 = OpFNegate %float %18
%22 = OpFNegate %float %19
%23 = OpExtInst %float %12 FAbs %17
%24 = OpExtInst %float %12 FAbs %18
%25 = OpExtInst %float %12 FAbs %19
%26 = OpFOrdLessThan %bool %19 %float_0
%27 = OpFOrdLessThan %bool %18 %float_0
%28 = OpFOrdLessThan %bool %17 %float_0
%29 = OpExtInst %float %12 FMax %23 %24
%30 = OpExtInst %float %12 FMax %25 %29
%31 = OpFMul %float %float_2 %30
%32 = OpFOrdGreaterThanEqual %bool %25 %29
%33 = OpLogicalNot %bool %32
%34 = OpFOrdGreaterThanEqual %bool %24 %23
%35 = OpLogicalAnd %bool %33 %34
%36 = OpSelect %float %26 %20 %17
%37 = OpSelect %float %28 %19 %22
%38 = OpSelect %float %35 %17 %37
%39 = OpSelect %float %32 %36 %38
%40 = OpSelect %float %27 %22 %19
%41 = OpSelect %float %35 %40 %21
%42 = OpCompositeConstruct %v2float %39 %41
%43 = OpCompositeConstruct %v2float %31 %31
%44 = OpFDiv %v2float %42 %43
%10 = OpFAdd %v2float %44 %16
OpReturn
OpFunctionEnd
)";
SinglePassRunAndCheck<AmdExtensionToKhrPass>(before, after, true);
}
TEST_F(AmdExtToKhrTest, ReplaceCubeFaceIndexAMD) {
// Sorry for the Check test. The code sequence is so long, I do not think
// that a match test would be anymore legible. This tests the replacement of
// the CubeFaceIndexAMD instruction.
const std::string before = R"(OpCapability Shader
OpExtension "SPV_KHR_storage_buffer_storage_class"
OpExtension "SPV_AMD_gcn_shader"
%1 = OpExtInstImport "SPV_AMD_gcn_shader"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %2 "main"
OpExecutionMode %2 LocalSize 1 1 1
%void = OpTypeVoid
%4 = OpTypeFunction %void
%float = OpTypeFloat 32
%v3float = OpTypeVector %float 3
%2 = OpFunction %void None %4
%7 = OpLabel
%8 = OpUndef %v3float
%9 = OpExtInst %float %1 CubeFaceIndexAMD %8
OpReturn
OpFunctionEnd
)";
const std::string after = R"(OpCapability Shader
OpExtension "SPV_KHR_storage_buffer_storage_class"
%11 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %2 "main"
OpExecutionMode %2 LocalSize 1 1 1
%void = OpTypeVoid
%4 = OpTypeFunction %void
%float = OpTypeFloat 32
%v3float = OpTypeVector %float 3
%bool = OpTypeBool
%float_0 = OpConstant %float 0
%float_1 = OpConstant %float 1
%float_2 = OpConstant %float 2
%float_3 = OpConstant %float 3
%float_4 = OpConstant %float 4
%float_5 = OpConstant %float 5
%2 = OpFunction %void None %4
%7 = OpLabel
%8 = OpUndef %v3float
%18 = OpCompositeExtract %float %8 0
%19 = OpCompositeExtract %float %8 1
%20 = OpCompositeExtract %float %8 2
%21 = OpExtInst %float %11 FAbs %18
%22 = OpExtInst %float %11 FAbs %19
%23 = OpExtInst %float %11 FAbs %20
%24 = OpFOrdLessThan %bool %20 %float_0
%25 = OpFOrdLessThan %bool %19 %float_0
%26 = OpFOrdLessThan %bool %18 %float_0
%27 = OpExtInst %float %11 FMax %21 %22
%28 = OpFOrdGreaterThanEqual %bool %23 %27
%29 = OpFOrdGreaterThanEqual %bool %22 %21
%30 = OpSelect %float %24 %float_5 %float_4
%31 = OpSelect %float %25 %float_3 %float_2
%32 = OpSelect %float %26 %float_1 %float_0
%33 = OpSelect %float %29 %31 %32
%9 = OpSelect %float %28 %30 %33
OpReturn
OpFunctionEnd
)";
SinglePassRunAndCheck<AmdExtensionToKhrPass>(before, after, true);
}
TEST_F(AmdExtToKhrTest, SetVersion) {
const std::string text = R"(
OpCapability Shader
OpCapability Int64
OpExtension "SPV_AMD_shader_ballot"
%1 = OpExtInstImport "SPV_AMD_shader_ballot"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "func"
OpExecutionMode %2 OriginUpperLeft
%void = OpTypeVoid
%4 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%ulong = OpTypeInt 64 0
%ulong_0 = OpConstant %ulong 0
%2 = OpFunction %void None %4
%8 = OpLabel
%9 = OpExtInst %uint %1 MbcntAMD %ulong_0
OpReturn
OpFunctionEnd
)";
// Set the version to 1.1 and make sure it is upgraded to 1.3.
SetTargetEnv(SPV_ENV_UNIVERSAL_1_1);
SetDisassembleOptions(0);
auto result = SinglePassRunAndDisassemble<AmdExtensionToKhrPass>(
text, /* skip_nop = */ true, /* skip_validation = */ false);
EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result));
const std::string& output = std::get<0>(result);
EXPECT_THAT(output, HasSubstr("Version: 1.3"));
}
TEST_F(AmdExtToKhrTest, SetVersion1) {
const std::string text = R"(
OpCapability Shader
OpCapability Int64
OpExtension "SPV_AMD_shader_ballot"
%1 = OpExtInstImport "SPV_AMD_shader_ballot"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "func"
OpExecutionMode %2 OriginUpperLeft
%void = OpTypeVoid
%4 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%ulong = OpTypeInt 64 0
%ulong_0 = OpConstant %ulong 0
%2 = OpFunction %void None %4
%8 = OpLabel
%9 = OpExtInst %uint %1 MbcntAMD %ulong_0
OpReturn
OpFunctionEnd
)";
// Set the version to 1.4 and make sure it is stays the same.
SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
SetDisassembleOptions(0);
auto result = SinglePassRunAndDisassemble<AmdExtensionToKhrPass>(
text, /* skip_nop = */ true, /* skip_validation = */ false);
EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result));
const std::string& output = std::get<0>(result);
EXPECT_THAT(output, HasSubstr("Version: 1.4"));
}
} // namespace
} // namespace opt
} // namespace spvtools

View File

@@ -1,266 +0,0 @@
// Copyright (c) 2016 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.
#ifndef TEST_OPT_ASSEMBLY_BUILDER_H_
#define TEST_OPT_ASSEMBLY_BUILDER_H_
#include <algorithm>
#include <cstdint>
#include <sstream>
#include <string>
#include <unordered_set>
#include <vector>
namespace spvtools {
namespace opt {
// A simple SPIR-V assembly code builder for test uses. It builds an SPIR-V
// assembly module from vectors of assembly strings. It allows users to add
// instructions to the main function and the type-constants-globals section
// directly. It relies on OpName instructions and friendly-name disassembling
// to keep the ID names unchanged after assembling.
//
// An assembly module is divided into several sections, matching with the
// SPIR-V Logical Layout:
// Global Preamble:
// OpCapability instructions;
// OpExtension instructions and OpExtInstImport instructions;
// OpMemoryModel instruction;
// OpEntryPoint and OpExecutionMode instruction;
// OpString, OpSourceExtension, OpSource and OpSourceContinued instructions.
// Names:
// OpName instructions.
// Annotations:
// OpDecorate, OpMemberDecorate, OpGroupDecorate, OpGroupMemberDecorate and
// OpDecorationGroup.
// Types, Constants and Global variables:
// Types, constants and global variables declaration instructions.
// Main Function:
// Main function instructions.
// Main Function Postamble:
// The return and function end instructions.
//
// The assembly code is built by concatenating all the strings in the above
// sections.
//
// Users define the contents in section <Type, Constants and Global Variables>
// and <Main Function>. The <Names> section is to hold the names for IDs to
// keep them unchanged before and after assembling. All defined IDs to be added
// to this code builder will be assigned with a global name through OpName
// instruction. The name is extracted from the definition instruction.
// E.g. adding instruction: %var_a = OpConstant %int 2, will also add an
// instruction: OpName %var_a, "var_a".
//
// Note that the name must not be used on more than one defined IDs and
// friendly-name disassembling must be enabled so that OpName instructions will
// be respected.
class AssemblyBuilder {
// The base ID value for spec constants.
static const uint32_t SPEC_ID_BASE = 200;
public:
// Initalize a minimal SPIR-V assembly code as the template. The minimal
// module contains an empty main function and some predefined names for the
// main function.
AssemblyBuilder()
: spec_id_counter_(SPEC_ID_BASE),
global_preamble_({
// clang-format off
"OpCapability Shader",
"OpCapability Float64",
"%1 = OpExtInstImport \"GLSL.std.450\"",
"OpMemoryModel Logical GLSL450",
"OpEntryPoint Vertex %main \"main\"",
// clang-format on
}),
names_(),
annotations_(),
types_consts_globals_(),
main_func_(),
main_func_postamble_({
"OpReturn",
"OpFunctionEnd",
}) {
AppendTypesConstantsGlobals({
"%void = OpTypeVoid",
"%main_func_type = OpTypeFunction %void",
});
AppendInMain({
"%main = OpFunction %void None %main_func_type",
"%main_func_entry_block = OpLabel",
});
}
// Appends OpName instructions to this builder. Instrcution strings that do
// not start with 'OpName ' will be skipped. Returns the references of this
// assembly builder.
AssemblyBuilder& AppendNames(const std::vector<std::string>& vec_asm_code) {
for (auto& inst_str : vec_asm_code) {
if (inst_str.find("OpName ") == 0) {
names_.push_back(inst_str);
}
}
return *this;
}
// Appends instructions to the types-constants-globals section and returns
// the reference of this assembly builder. IDs defined in the given code will
// be added to the Names section and then be registered with OpName
// instruction. Corresponding decoration instruction will be added for spec
// constants defined with opcode: 'OpSpecConstant'.
AssemblyBuilder& AppendTypesConstantsGlobals(
const std::vector<std::string>& vec_asm_code) {
AddNamesForResultIDsIn(vec_asm_code);
// Check spec constants defined with OpSpecConstant.
for (auto& inst_str : vec_asm_code) {
if (inst_str.find("= OpSpecConstant ") != std::string::npos ||
inst_str.find("= OpSpecConstantTrue ") != std::string::npos ||
inst_str.find("= OpSpecConstantFalse ") != std::string::npos) {
AddSpecIDFor(GetResultIDName(inst_str));
}
}
types_consts_globals_.insert(types_consts_globals_.end(),
vec_asm_code.begin(), vec_asm_code.end());
return *this;
}
// Appends instructions to the main function block, which is already labelled
// with "main_func_entry_block". Returns the reference of this assembly
// builder. IDs defined in the given code will be added to the Names section
// and then be registered with OpName instruction.
AssemblyBuilder& AppendInMain(const std::vector<std::string>& vec_asm_code) {
AddNamesForResultIDsIn(vec_asm_code);
main_func_.insert(main_func_.end(), vec_asm_code.begin(),
vec_asm_code.end());
return *this;
}
// Appends annotation instructions to the annotation section, and returns the
// reference of this assembly builder.
AssemblyBuilder& AppendAnnotations(
const std::vector<std::string>& vec_annotations) {
annotations_.insert(annotations_.end(), vec_annotations.begin(),
vec_annotations.end());
return *this;
}
// Pre-pends string to the preamble of the module. Useful for EFFCEE checks.
AssemblyBuilder& PrependPreamble(const std::vector<std::string>& preamble) {
preamble_.insert(preamble_.end(), preamble.begin(), preamble.end());
return *this;
}
// Get the SPIR-V assembly code as string.
std::string GetCode() const {
std::ostringstream ss;
for (const auto& line : preamble_) {
ss << line << std::endl;
}
for (const auto& line : global_preamble_) {
ss << line << std::endl;
}
for (const auto& line : names_) {
ss << line << std::endl;
}
for (const auto& line : annotations_) {
ss << line << std::endl;
}
for (const auto& line : types_consts_globals_) {
ss << line << std::endl;
}
for (const auto& line : main_func_) {
ss << line << std::endl;
}
for (const auto& line : main_func_postamble_) {
ss << line << std::endl;
}
return ss.str();
}
private:
// Adds a given name to the Name section with OpName. If the given name has
// been added before, does nothing.
void AddOpNameIfNotExist(const std::string& id_name) {
if (!used_names_.count(id_name)) {
std::stringstream opname_inst;
opname_inst << "OpName "
<< "%" << id_name << " \"" << id_name << "\"";
names_.emplace_back(opname_inst.str());
used_names_.insert(id_name);
}
}
// Adds the names in a vector of assembly code strings to the Names section.
// If a '=' sign is found in an instruction, this instruction will be treated
// as an ID defining instruction. The ID name used in the instruction will be
// extracted and added to the Names section.
void AddNamesForResultIDsIn(const std::vector<std::string>& vec_asm_code) {
for (const auto& line : vec_asm_code) {
std::string name = GetResultIDName(line);
if (!name.empty()) {
AddOpNameIfNotExist(name);
}
}
}
// Adds an OpDecorate SpecId instruction for the given ID name.
void AddSpecIDFor(const std::string& id_name) {
std::stringstream decorate_inst;
decorate_inst << "OpDecorate "
<< "%" << id_name << " SpecId " << spec_id_counter_;
spec_id_counter_ += 1;
annotations_.emplace_back(decorate_inst.str());
}
// Extracts the ID name from a SPIR-V assembly instruction string. If the
// instruction is an ID-defining instruction (has result ID), returns the
// name of the result ID in string. If the instruction does not have result
// ID, returns an empty string.
std::string GetResultIDName(const std::string inst_str) {
std::string name;
if (inst_str.find('=') != std::string::npos) {
size_t assign_sign = inst_str.find('=');
name = inst_str.substr(0, assign_sign);
name.erase(remove_if(name.begin(), name.end(),
[](char c) { return c == ' ' || c == '%'; }),
name.end());
}
return name;
}
uint32_t spec_id_counter_;
// User-defined preamble.
std::vector<std::string> preamble_;
// The vector that contains common preambles shared across all test SPIR-V
// code.
std::vector<std::string> global_preamble_;
// The vector that contains OpName instructions.
std::vector<std::string> names_;
// The vector that contains annotation instructions.
std::vector<std::string> annotations_;
// The vector that contains the code to declare types, constants and global
// variables (aka. the Types-Constants-Globals section).
std::vector<std::string> types_consts_globals_;
// The vector that contains the code in main function's entry block.
std::vector<std::string> main_func_;
// The vector that contains the postamble of main function body.
std::vector<std::string> main_func_postamble_;
// All of the defined variable names.
std::unordered_set<std::string> used_names_;
};
} // namespace opt
} // namespace spvtools
#endif // TEST_OPT_ASSEMBLY_BUILDER_H_

View File

@@ -1,283 +0,0 @@
// Copyright (c) 2016 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 "test/opt/assembly_builder.h"
#include "test/opt/pass_fixture.h"
#include "test/opt/pass_utils.h"
namespace spvtools {
namespace opt {
namespace {
using AssemblyBuilderTest = PassTest<::testing::Test>;
TEST_F(AssemblyBuilderTest, MinimalShader) {
AssemblyBuilder builder;
std::vector<const char*> expected = {
// clang-format off
"OpCapability Shader",
"OpCapability Float64",
"%1 = OpExtInstImport \"GLSL.std.450\"",
"OpMemoryModel Logical GLSL450",
"OpEntryPoint Vertex %main \"main\"",
"OpName %void \"void\"",
"OpName %main_func_type \"main_func_type\"",
"OpName %main \"main\"",
"OpName %main_func_entry_block \"main_func_entry_block\"",
"%void = OpTypeVoid",
"%main_func_type = OpTypeFunction %void",
"%main = OpFunction %void None %main_func_type",
"%main_func_entry_block = OpLabel",
"OpReturn",
"OpFunctionEnd",
// clang-format on
};
SinglePassRunAndCheck<NullPass>(builder.GetCode(), JoinAllInsts(expected),
/* skip_nop = */ false);
}
TEST_F(AssemblyBuilderTest, ShaderWithConstants) {
AssemblyBuilder builder;
builder
.AppendTypesConstantsGlobals({
// clang-format off
"%bool = OpTypeBool",
"%_PF_bool = OpTypePointer Function %bool",
"%bt = OpConstantTrue %bool",
"%bf = OpConstantFalse %bool",
"%int = OpTypeInt 32 1",
"%_PF_int = OpTypePointer Function %int",
"%si = OpConstant %int 1",
"%uint = OpTypeInt 32 0",
"%_PF_uint = OpTypePointer Function %uint",
"%ui = OpConstant %uint 2",
"%float = OpTypeFloat 32",
"%_PF_float = OpTypePointer Function %float",
"%f = OpConstant %float 3.1415",
"%double = OpTypeFloat 64",
"%_PF_double = OpTypePointer Function %double",
"%d = OpConstant %double 3.14159265358979",
// clang-format on
})
.AppendInMain({
// clang-format off
"%btv = OpVariable %_PF_bool Function",
"%bfv = OpVariable %_PF_bool Function",
"%iv = OpVariable %_PF_int Function",
"%uv = OpVariable %_PF_uint Function",
"%fv = OpVariable %_PF_float Function",
"%dv = OpVariable %_PF_double Function",
"OpStore %btv %bt",
"OpStore %bfv %bf",
"OpStore %iv %si",
"OpStore %uv %ui",
"OpStore %fv %f",
"OpStore %dv %d",
// clang-format on
});
std::vector<const char*> expected = {
// clang-format off
"OpCapability Shader",
"OpCapability Float64",
"%1 = OpExtInstImport \"GLSL.std.450\"",
"OpMemoryModel Logical GLSL450",
"OpEntryPoint Vertex %main \"main\"",
"OpName %void \"void\"",
"OpName %main_func_type \"main_func_type\"",
"OpName %main \"main\"",
"OpName %main_func_entry_block \"main_func_entry_block\"",
"OpName %bool \"bool\"",
"OpName %_PF_bool \"_PF_bool\"",
"OpName %bt \"bt\"",
"OpName %bf \"bf\"",
"OpName %int \"int\"",
"OpName %_PF_int \"_PF_int\"",
"OpName %si \"si\"",
"OpName %uint \"uint\"",
"OpName %_PF_uint \"_PF_uint\"",
"OpName %ui \"ui\"",
"OpName %float \"float\"",
"OpName %_PF_float \"_PF_float\"",
"OpName %f \"f\"",
"OpName %double \"double\"",
"OpName %_PF_double \"_PF_double\"",
"OpName %d \"d\"",
"OpName %btv \"btv\"",
"OpName %bfv \"bfv\"",
"OpName %iv \"iv\"",
"OpName %uv \"uv\"",
"OpName %fv \"fv\"",
"OpName %dv \"dv\"",
"%void = OpTypeVoid",
"%main_func_type = OpTypeFunction %void",
"%bool = OpTypeBool",
"%_PF_bool = OpTypePointer Function %bool",
"%bt = OpConstantTrue %bool",
"%bf = OpConstantFalse %bool",
"%int = OpTypeInt 32 1",
"%_PF_int = OpTypePointer Function %int",
"%si = OpConstant %int 1",
"%uint = OpTypeInt 32 0",
"%_PF_uint = OpTypePointer Function %uint",
"%ui = OpConstant %uint 2",
"%float = OpTypeFloat 32",
"%_PF_float = OpTypePointer Function %float",
"%f = OpConstant %float 3.1415",
"%double = OpTypeFloat 64",
"%_PF_double = OpTypePointer Function %double",
"%d = OpConstant %double 3.14159265358979",
"%main = OpFunction %void None %main_func_type",
"%main_func_entry_block = OpLabel",
"%btv = OpVariable %_PF_bool Function",
"%bfv = OpVariable %_PF_bool Function",
"%iv = OpVariable %_PF_int Function",
"%uv = OpVariable %_PF_uint Function",
"%fv = OpVariable %_PF_float Function",
"%dv = OpVariable %_PF_double Function",
"OpStore %btv %bt",
"OpStore %bfv %bf",
"OpStore %iv %si",
"OpStore %uv %ui",
"OpStore %fv %f",
"OpStore %dv %d",
"OpReturn",
"OpFunctionEnd",
// clang-format on
};
SinglePassRunAndCheck<NullPass>(builder.GetCode(), JoinAllInsts(expected),
/* skip_nop = */ false);
}
TEST_F(AssemblyBuilderTest, SpecConstants) {
AssemblyBuilder builder;
builder.AppendTypesConstantsGlobals({
"%bool = OpTypeBool",
"%uint = OpTypeInt 32 0",
"%int = OpTypeInt 32 1",
"%float = OpTypeFloat 32",
"%double = OpTypeFloat 64",
"%v2int = OpTypeVector %int 2",
"%spec_true = OpSpecConstantTrue %bool",
"%spec_false = OpSpecConstantFalse %bool",
"%spec_uint = OpSpecConstant %uint 1",
"%spec_int = OpSpecConstant %int 1",
"%spec_float = OpSpecConstant %float 1.25",
"%spec_double = OpSpecConstant %double 1.2345678",
// Spec constants defined below should not have SpecID.
"%spec_add_op = OpSpecConstantOp %int IAdd %spec_int %spec_int",
"%spec_vec = OpSpecConstantComposite %v2int %spec_int %spec_int",
"%spec_vec_x = OpSpecConstantOp %int CompositeExtract %spec_vec 0",
});
std::vector<const char*> expected = {
// clang-format off
"OpCapability Shader",
"OpCapability Float64",
"%1 = OpExtInstImport \"GLSL.std.450\"",
"OpMemoryModel Logical GLSL450",
"OpEntryPoint Vertex %main \"main\"",
"OpName %void \"void\"",
"OpName %main_func_type \"main_func_type\"",
"OpName %main \"main\"",
"OpName %main_func_entry_block \"main_func_entry_block\"",
"OpName %bool \"bool\"",
"OpName %uint \"uint\"",
"OpName %int \"int\"",
"OpName %float \"float\"",
"OpName %double \"double\"",
"OpName %v2int \"v2int\"",
"OpName %spec_true \"spec_true\"",
"OpName %spec_false \"spec_false\"",
"OpName %spec_uint \"spec_uint\"",
"OpName %spec_int \"spec_int\"",
"OpName %spec_float \"spec_float\"",
"OpName %spec_double \"spec_double\"",
"OpName %spec_add_op \"spec_add_op\"",
"OpName %spec_vec \"spec_vec\"",
"OpName %spec_vec_x \"spec_vec_x\"",
"OpDecorate %spec_true SpecId 200",
"OpDecorate %spec_false SpecId 201",
"OpDecorate %spec_uint SpecId 202",
"OpDecorate %spec_int SpecId 203",
"OpDecorate %spec_float SpecId 204",
"OpDecorate %spec_double SpecId 205",
"%void = OpTypeVoid",
"%main_func_type = OpTypeFunction %void",
"%bool = OpTypeBool",
"%uint = OpTypeInt 32 0",
"%int = OpTypeInt 32 1",
"%float = OpTypeFloat 32",
"%double = OpTypeFloat 64",
"%v2int = OpTypeVector %int 2",
"%spec_true = OpSpecConstantTrue %bool",
"%spec_false = OpSpecConstantFalse %bool",
"%spec_uint = OpSpecConstant %uint 1",
"%spec_int = OpSpecConstant %int 1",
"%spec_float = OpSpecConstant %float 1.25",
"%spec_double = OpSpecConstant %double 1.2345678",
"%spec_add_op = OpSpecConstantOp %int IAdd %spec_int %spec_int",
"%spec_vec = OpSpecConstantComposite %v2int %spec_int %spec_int",
"%spec_vec_x = OpSpecConstantOp %int CompositeExtract %spec_vec 0",
"%main = OpFunction %void None %main_func_type",
"%main_func_entry_block = OpLabel",
"OpReturn",
"OpFunctionEnd",
// clang-format on
};
SinglePassRunAndCheck<NullPass>(builder.GetCode(), JoinAllInsts(expected),
/* skip_nop = */ false);
}
TEST_F(AssemblyBuilderTest, AppendNames) {
AssemblyBuilder builder;
builder.AppendNames({
"OpName %void \"another_name_for_void\"",
"I am an invalid OpName instruction and should not be added",
"OpName %main \"another name for main\"",
});
std::vector<const char*> expected = {
// clang-format off
"OpCapability Shader",
"OpCapability Float64",
"%1 = OpExtInstImport \"GLSL.std.450\"",
"OpMemoryModel Logical GLSL450",
"OpEntryPoint Vertex %main \"main\"",
"OpName %void \"void\"",
"OpName %main_func_type \"main_func_type\"",
"OpName %main \"main\"",
"OpName %main_func_entry_block \"main_func_entry_block\"",
"OpName %void \"another_name_for_void\"",
"OpName %main \"another name for main\"",
"%void = OpTypeVoid",
"%main_func_type = OpTypeFunction %void",
"%main = OpFunction %void None %main_func_type",
"%main_func_entry_block = OpLabel",
"OpReturn",
"OpFunctionEnd",
// clang-format on
};
SinglePassRunAndCheck<NullPass>(builder.GetCode(), JoinAllInsts(expected),
/* skip_nop = */ false);
}
} // namespace
} // namespace opt
} // namespace spvtools

View File

@@ -1,976 +0,0 @@
// Copyright (c) 2017 Valve Corporation
// Copyright (c) 2017 LunarG 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 "test/opt/pass_fixture.h"
#include "test/opt/pass_utils.h"
namespace spvtools {
namespace opt {
namespace {
using BlockMergeTest = PassTest<::testing::Test>;
TEST_F(BlockMergeTest, Simple) {
// Note: SPIR-V hand edited to insert block boundary
// between two statements in main.
//
// #version 140
//
// in vec4 BaseColor;
//
// void main()
// {
// vec4 v = BaseColor;
// gl_FragColor = v;
// }
const std::string predefs =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 140
OpName %main "main"
OpName %v "v"
OpName %BaseColor "BaseColor"
OpName %gl_FragColor "gl_FragColor"
%void = OpTypeVoid
%7 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Function_v4float = OpTypePointer Function %v4float
%_ptr_Input_v4float = OpTypePointer Input %v4float
%BaseColor = OpVariable %_ptr_Input_v4float Input
%_ptr_Output_v4float = OpTypePointer Output %v4float
%gl_FragColor = OpVariable %_ptr_Output_v4float Output
)";
const std::string before =
R"(%main = OpFunction %void None %7
%13 = OpLabel
%v = OpVariable %_ptr_Function_v4float Function
%14 = OpLoad %v4float %BaseColor
OpStore %v %14
OpBranch %15
%15 = OpLabel
%16 = OpLoad %v4float %v
OpStore %gl_FragColor %16
OpReturn
OpFunctionEnd
)";
const std::string after =
R"(%main = OpFunction %void None %7
%13 = OpLabel
%v = OpVariable %_ptr_Function_v4float Function
%14 = OpLoad %v4float %BaseColor
OpStore %v %14
%16 = OpLoad %v4float %v
OpStore %gl_FragColor %16
OpReturn
OpFunctionEnd
)";
SinglePassRunAndCheck<BlockMergePass>(predefs + before, predefs + after, true,
true);
}
TEST_F(BlockMergeTest, EmptyBlock) {
// Note: SPIR-V hand edited to insert empty block
// after two statements in main.
//
// #version 140
//
// in vec4 BaseColor;
//
// void main()
// {
// vec4 v = BaseColor;
// gl_FragColor = v;
// }
const std::string predefs =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 140
OpName %main "main"
OpName %v "v"
OpName %BaseColor "BaseColor"
OpName %gl_FragColor "gl_FragColor"
%void = OpTypeVoid
%7 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Function_v4float = OpTypePointer Function %v4float
%_ptr_Input_v4float = OpTypePointer Input %v4float
%BaseColor = OpVariable %_ptr_Input_v4float Input
%_ptr_Output_v4float = OpTypePointer Output %v4float
%gl_FragColor = OpVariable %_ptr_Output_v4float Output
)";
const std::string before =
R"(%main = OpFunction %void None %7
%13 = OpLabel
%v = OpVariable %_ptr_Function_v4float Function
%14 = OpLoad %v4float %BaseColor
OpStore %v %14
OpBranch %15
%15 = OpLabel
%16 = OpLoad %v4float %v
OpStore %gl_FragColor %16
OpBranch %17
%17 = OpLabel
OpBranch %18
%18 = OpLabel
OpReturn
OpFunctionEnd
)";
const std::string after =
R"(%main = OpFunction %void None %7
%13 = OpLabel
%v = OpVariable %_ptr_Function_v4float Function
%14 = OpLoad %v4float %BaseColor
OpStore %v %14
%16 = OpLoad %v4float %v
OpStore %gl_FragColor %16
OpReturn
OpFunctionEnd
)";
SinglePassRunAndCheck<BlockMergePass>(predefs + before, predefs + after, true,
true);
}
TEST_F(BlockMergeTest, NestedInControlFlow) {
// Note: SPIR-V hand edited to insert block boundary
// between OpFMul and OpStore in then-part.
//
// #version 140
// in vec4 BaseColor;
//
// layout(std140) uniform U_t
// {
// bool g_B ;
// } ;
//
// void main()
// {
// vec4 v = BaseColor;
// if (g_B)
// vec4 v = v * 0.25;
// gl_FragColor = v;
// }
const std::string predefs =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 140
OpName %main "main"
OpName %v "v"
OpName %BaseColor "BaseColor"
OpName %U_t "U_t"
OpMemberName %U_t 0 "g_B"
OpName %_ ""
OpName %v_0 "v"
OpName %gl_FragColor "gl_FragColor"
OpMemberDecorate %U_t 0 Offset 0
OpDecorate %U_t Block
OpDecorate %_ DescriptorSet 0
%void = OpTypeVoid
%10 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Function_v4float = OpTypePointer Function %v4float
%_ptr_Input_v4float = OpTypePointer Input %v4float
%BaseColor = OpVariable %_ptr_Input_v4float Input
%uint = OpTypeInt 32 0
%U_t = OpTypeStruct %uint
%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t
%_ = OpVariable %_ptr_Uniform_U_t Uniform
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%_ptr_Uniform_uint = OpTypePointer Uniform %uint
%bool = OpTypeBool
%uint_0 = OpConstant %uint 0
%float_0_25 = OpConstant %float 0.25
%_ptr_Output_v4float = OpTypePointer Output %v4float
%gl_FragColor = OpVariable %_ptr_Output_v4float Output
)";
const std::string before =
R"(%main = OpFunction %void None %10
%24 = OpLabel
%v = OpVariable %_ptr_Function_v4float Function
%v_0 = OpVariable %_ptr_Function_v4float Function
%25 = OpLoad %v4float %BaseColor
OpStore %v %25
%26 = OpAccessChain %_ptr_Uniform_uint %_ %int_0
%27 = OpLoad %uint %26
%28 = OpINotEqual %bool %27 %uint_0
OpSelectionMerge %29 None
OpBranchConditional %28 %30 %29
%30 = OpLabel
%31 = OpLoad %v4float %v
%32 = OpVectorTimesScalar %v4float %31 %float_0_25
OpBranch %33
%33 = OpLabel
OpStore %v_0 %32
OpBranch %29
%29 = OpLabel
%34 = OpLoad %v4float %v
OpStore %gl_FragColor %34
OpReturn
OpFunctionEnd
)";
const std::string after =
R"(%main = OpFunction %void None %10
%24 = OpLabel
%v = OpVariable %_ptr_Function_v4float Function
%v_0 = OpVariable %_ptr_Function_v4float Function
%25 = OpLoad %v4float %BaseColor
OpStore %v %25
%26 = OpAccessChain %_ptr_Uniform_uint %_ %int_0
%27 = OpLoad %uint %26
%28 = OpINotEqual %bool %27 %uint_0
OpSelectionMerge %29 None
OpBranchConditional %28 %30 %29
%30 = OpLabel
%31 = OpLoad %v4float %v
%32 = OpVectorTimesScalar %v4float %31 %float_0_25
OpStore %v_0 %32
OpBranch %29
%29 = OpLabel
%34 = OpLoad %v4float %v
OpStore %gl_FragColor %34
OpReturn
OpFunctionEnd
)";
SinglePassRunAndCheck<BlockMergePass>(predefs + before, predefs + after, true,
true);
}
TEST_F(BlockMergeTest, PhiInSuccessorOfMergedBlock) {
const std::string text = R"(
; CHECK: OpSelectionMerge [[merge:%\w+]] None
; CHECK-NEXT: OpBranchConditional {{%\w+}} [[then:%\w+]] [[else:%\w+]]
; CHECK: [[then]] = OpLabel
; CHECK-NEXT: OpBranch [[merge]]
; CHECK: [[else]] = OpLabel
; CHECK-NEXT: OpBranch [[merge]]
; CHECK: [[merge]] = OpLabel
; CHECK-NEXT: OpPhi {{%\w+}} %true [[then]] %false [[else]]
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
%void = OpTypeVoid
%bool = OpTypeBool
%true = OpConstantTrue %bool
%false = OpConstantFalse %bool
%functy = OpTypeFunction %void
%func = OpFunction %void None %functy
%entry = OpLabel
OpSelectionMerge %merge None
OpBranchConditional %true %then %else
%then = OpLabel
OpBranch %then_next
%then_next = OpLabel
OpBranch %merge
%else = OpLabel
OpBranch %merge
%merge = OpLabel
%phi = OpPhi %bool %true %then_next %false %else
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<BlockMergePass>(text, true);
}
TEST_F(BlockMergeTest, UpdateMergeInstruction) {
const std::string text = R"(
; CHECK: OpSelectionMerge [[merge:%\w+]] None
; CHECK-NEXT: OpBranchConditional {{%\w+}} [[then:%\w+]] [[else:%\w+]]
; CHECK: [[then]] = OpLabel
; CHECK-NEXT: OpBranch [[merge]]
; CHECK: [[else]] = OpLabel
; CHECK-NEXT: OpBranch [[merge]]
; CHECK: [[merge]] = OpLabel
; CHECK-NEXT: OpReturn
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
%void = OpTypeVoid
%bool = OpTypeBool
%true = OpConstantTrue %bool
%false = OpConstantFalse %bool
%functy = OpTypeFunction %void
%func = OpFunction %void None %functy
%entry = OpLabel
OpSelectionMerge %real_merge None
OpBranchConditional %true %then %else
%then = OpLabel
OpBranch %merge
%else = OpLabel
OpBranch %merge
%merge = OpLabel
OpBranch %real_merge
%real_merge = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<BlockMergePass>(text, true);
}
TEST_F(BlockMergeTest, TwoMergeBlocksCannotBeMerged) {
const std::string text = R"(
; CHECK: OpSelectionMerge [[outer_merge:%\w+]] None
; CHECK: OpSelectionMerge [[inner_merge:%\w+]] None
; CHECK: [[inner_merge]] = OpLabel
; CHECK-NEXT: OpBranch [[outer_merge]]
; CHECK: [[outer_merge]] = OpLabel
; CHECK-NEXT: OpReturn
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
%void = OpTypeVoid
%bool = OpTypeBool
%true = OpConstantTrue %bool
%false = OpConstantFalse %bool
%functy = OpTypeFunction %void
%func = OpFunction %void None %functy
%entry = OpLabel
OpSelectionMerge %outer_merge None
OpBranchConditional %true %then %else
%then = OpLabel
OpBranch %inner_header
%else = OpLabel
OpBranch %inner_header
%inner_header = OpLabel
OpSelectionMerge %inner_merge None
OpBranchConditional %true %inner_then %inner_else
%inner_then = OpLabel
OpBranch %inner_merge
%inner_else = OpLabel
OpBranch %inner_merge
%inner_merge = OpLabel
OpBranch %outer_merge
%outer_merge = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<BlockMergePass>(text, true);
}
TEST_F(BlockMergeTest, MergeContinue) {
const std::string text = R"(
; CHECK: OpBranch [[header:%\w+]]
; CHECK: [[header]] = OpLabel
; CHECK-NEXT: OpLogicalAnd
; CHECK-NEXT: OpLoopMerge {{%\w+}} [[header]] None
; CHECK-NEXT: OpBranch [[header]]
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
%void = OpTypeVoid
%bool = OpTypeBool
%true = OpConstantTrue %bool
%false = OpConstantFalse %bool
%functy = OpTypeFunction %void
%func = OpFunction %void None %functy
%entry = OpLabel
OpBranch %header
%header = OpLabel
OpLoopMerge %merge %continue None
OpBranch %continue
%continue = OpLabel
%op = OpLogicalAnd %bool %true %false
OpBranch %header
%merge = OpLabel
OpUnreachable
OpFunctionEnd
)";
SinglePassRunAndMatch<BlockMergePass>(text, true);
}
TEST_F(BlockMergeTest, TwoHeadersCannotBeMerged) {
const std::string text = R"(
; CHECK: OpBranch [[loop_header:%\w+]]
; CHECK: [[loop_header]] = OpLabel
; CHECK-NEXT: OpLoopMerge
; CHECK-NEXT: OpBranch [[if_header:%\w+]]
; CHECK: [[if_header]] = OpLabel
; CHECK-NEXT: OpSelectionMerge
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
%void = OpTypeVoid
%bool = OpTypeBool
%true = OpConstantTrue %bool
%false = OpConstantFalse %bool
%functy = OpTypeFunction %void
%func = OpFunction %void None %functy
%entry = OpLabel
OpBranch %header
%header = OpLabel
OpLoopMerge %merge %continue None
OpBranch %inner_header
%inner_header = OpLabel
OpSelectionMerge %if_merge None
OpBranchConditional %true %then %if_merge
%then = OpLabel
OpBranch %continue
%if_merge = OpLabel
OpBranch %continue
%continue = OpLabel
OpBranchConditional %false %merge %header
%merge = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<BlockMergePass>(text, true);
}
TEST_F(BlockMergeTest, CannotMergeContinue) {
const std::string text = R"(
; CHECK: OpBranch [[loop_header:%\w+]]
; CHECK: [[loop_header]] = OpLabel
; CHECK-NEXT: OpLoopMerge {{%\w+}} [[continue:%\w+]]
; CHECK-NEXT: OpBranchConditional {{%\w+}} [[if_header:%\w+]]
; CHECK: [[if_header]] = OpLabel
; CHECK-NEXT: OpSelectionMerge
; CHECK: [[continue]] = OpLabel
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
%void = OpTypeVoid
%bool = OpTypeBool
%true = OpConstantTrue %bool
%false = OpConstantFalse %bool
%functy = OpTypeFunction %void
%func = OpFunction %void None %functy
%entry = OpLabel
OpBranch %header
%header = OpLabel
OpLoopMerge %merge %continue None
OpBranchConditional %true %inner_header %merge
%inner_header = OpLabel
OpSelectionMerge %if_merge None
OpBranchConditional %true %then %if_merge
%then = OpLabel
OpBranch %continue
%if_merge = OpLabel
OpBranch %continue
%continue = OpLabel
OpBranchConditional %false %merge %header
%merge = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<BlockMergePass>(text, true);
}
TEST_F(BlockMergeTest, RemoveStructuredDeclaration) {
// Note: SPIR-V hand edited remove dead branch and add block
// before continue block
//
// #version 140
// in vec4 BaseColor;
//
// void main()
// {
// while (true) {
// break;
// }
// gl_FragColor = BaseColor;
// }
const std::string assembly =
R"(
; CHECK: OpLabel
; CHECK: [[header:%\w+]] = OpLabel
; CHECK-NOT: OpLoopMerge
; CHECK: OpReturn
; CHECK: [[continue:%\w+]] = OpLabel
; CHECK-NEXT: OpBranch [[block:%\w+]]
; CHECK: [[block]] = OpLabel
; CHECK-NEXT: OpBranch [[header]]
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %gl_FragColor %BaseColor
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 140
OpName %main "main"
OpName %gl_FragColor "gl_FragColor"
OpName %BaseColor "BaseColor"
%void = OpTypeVoid
%6 = OpTypeFunction %void
%bool = OpTypeBool
%true = OpConstantTrue %bool
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Output_v4float = OpTypePointer Output %v4float
%gl_FragColor = OpVariable %_ptr_Output_v4float Output
%_ptr_Input_v4float = OpTypePointer Input %v4float
%BaseColor = OpVariable %_ptr_Input_v4float Input
%main = OpFunction %void None %6
%13 = OpLabel
OpBranch %14
%14 = OpLabel
OpLoopMerge %15 %16 None
OpBranch %17
%17 = OpLabel
OpBranch %15
%18 = OpLabel
OpBranch %16
%16 = OpLabel
OpBranch %14
%15 = OpLabel
%19 = OpLoad %v4float %BaseColor
OpStore %gl_FragColor %19
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<BlockMergePass>(assembly, true);
}
TEST_F(BlockMergeTest, DontMergeKill) {
const std::string text = R"(
; CHECK: OpLoopMerge [[merge:%\w+]] [[cont:%\w+]] None
; CHECK-NEXT: OpBranch [[ret:%\w+]]
; CHECK: [[ret:%\w+]] = OpLabel
; CHECK-NEXT: OpKill
; CHECK-DAG: [[cont]] = OpLabel
; CHECK-DAG: [[merge]] = OpLabel
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
%void = OpTypeVoid
%bool = OpTypeBool
%functy = OpTypeFunction %void
%func = OpFunction %void None %functy
%1 = OpLabel
OpBranch %2
%2 = OpLabel
OpLoopMerge %3 %4 None
OpBranch %5
%5 = OpLabel
OpKill
%4 = OpLabel
OpBranch %2
%3 = OpLabel
OpUnreachable
OpFunctionEnd
)";
SinglePassRunAndMatch<BlockMergePass>(text, true);
}
TEST_F(BlockMergeTest, DontMergeUnreachable) {
const std::string text = R"(
; CHECK: OpLoopMerge [[merge:%\w+]] [[cont:%\w+]] None
; CHECK-NEXT: OpBranch [[ret:%\w+]]
; CHECK: [[ret:%\w+]] = OpLabel
; CHECK-NEXT: OpUnreachable
; CHECK-DAG: [[cont]] = OpLabel
; CHECK-DAG: [[merge]] = OpLabel
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
%void = OpTypeVoid
%bool = OpTypeBool
%functy = OpTypeFunction %void
%func = OpFunction %void None %functy
%1 = OpLabel
OpBranch %2
%2 = OpLabel
OpLoopMerge %3 %4 None
OpBranch %5
%5 = OpLabel
OpUnreachable
%4 = OpLabel
OpBranch %2
%3 = OpLabel
OpUnreachable
OpFunctionEnd
)";
SinglePassRunAndMatch<BlockMergePass>(text, false);
}
TEST_F(BlockMergeTest, DontMergeReturn) {
const std::string text = R"(
; CHECK: OpLoopMerge [[merge:%\w+]] [[cont:%\w+]] None
; CHECK-NEXT: OpBranch [[ret:%\w+]]
; CHECK: [[ret:%\w+]] = OpLabel
; CHECK-NEXT: OpReturn
; CHECK-DAG: [[cont]] = OpLabel
; CHECK-DAG: [[merge]] = OpLabel
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
%void = OpTypeVoid
%bool = OpTypeBool
%functy = OpTypeFunction %void
%func = OpFunction %void None %functy
%1 = OpLabel
OpBranch %2
%2 = OpLabel
OpLoopMerge %3 %4 None
OpBranch %5
%5 = OpLabel
OpReturn
%4 = OpLabel
OpBranch %2
%3 = OpLabel
OpUnreachable
OpFunctionEnd
)";
SinglePassRunAndMatch<BlockMergePass>(text, true);
}
TEST_F(BlockMergeTest, DontMergeSwitch) {
const std::string text = R"(
; CHECK: OpLoopMerge [[merge:%\w+]] [[cont:%\w+]] None
; CHECK-NEXT: OpBranch [[ret:%\w+]]
; CHECK: [[ret:%\w+]] = OpLabel
; CHECK-NEXT: OpSwitch
; CHECK-DAG: [[cont]] = OpLabel
; CHECK-DAG: [[merge]] = OpLabel
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
%void = OpTypeVoid
%bool = OpTypeBool
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%functy = OpTypeFunction %void
%func = OpFunction %void None %functy
%1 = OpLabel
OpBranch %2
%2 = OpLabel
OpLoopMerge %3 %4 None
OpBranch %5
%5 = OpLabel
OpSwitch %int_0 %6
%6 = OpLabel
OpReturn
%4 = OpLabel
OpBranch %2
%3 = OpLabel
OpUnreachable
OpFunctionEnd
)";
SinglePassRunAndMatch<BlockMergePass>(text, true);
}
TEST_F(BlockMergeTest, DontMergeReturnValue) {
const std::string text = R"(
; CHECK: OpLoopMerge [[merge:%\w+]] [[cont:%\w+]] None
; CHECK-NEXT: OpBranch [[ret:%\w+]]
; CHECK: [[ret:%\w+]] = OpLabel
; CHECK-NEXT: OpReturn
; CHECK-DAG: [[cont]] = OpLabel
; CHECK-DAG: [[merge]] = OpLabel
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
%void = OpTypeVoid
%bool = OpTypeBool
%functy = OpTypeFunction %void
%otherfuncty = OpTypeFunction %bool
%true = OpConstantTrue %bool
%func = OpFunction %void None %functy
%1 = OpLabel
%2 = OpFunctionCall %bool %3
OpReturn
OpFunctionEnd
%3 = OpFunction %bool None %otherfuncty
%4 = OpLabel
OpBranch %5
%5 = OpLabel
OpLoopMerge %6 %7 None
OpBranch %8
%8 = OpLabel
OpReturnValue %true
%7 = OpLabel
OpBranch %5
%6 = OpLabel
OpUnreachable
OpFunctionEnd
)";
SinglePassRunAndMatch<BlockMergePass>(text, true);
}
TEST_F(BlockMergeTest, MergeHeaders) {
// Merge two headers when the second is the merge block of the first.
const std::string text = R"(
; CHECK: OpFunction
; CHECK-NEXT: OpLabel
; CHECK-NEXT: OpBranch [[header:%\w+]]
; CHECK-NEXT: [[header]] = OpLabel
; CHECK-NEXT: OpSelectionMerge [[merge:%\w+]]
; CHECK: [[merge]] = OpLabel
; CHEKC: OpReturn
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
%void = OpTypeVoid
%bool = OpTypeBool
%functy = OpTypeFunction %void
%otherfuncty = OpTypeFunction %bool
%true = OpConstantTrue %bool
%func = OpFunction %void None %functy
%1 = OpLabel
OpBranch %5
%5 = OpLabel
OpLoopMerge %8 %7 None
OpBranch %8
%7 = OpLabel
OpBranch %5
%8 = OpLabel
OpSelectionMerge %m None
OpBranchConditional %true %a %m
%a = OpLabel
OpBranch %m
%m = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<BlockMergePass>(text, true);
}
TEST_F(BlockMergeTest, OpPhiInSuccessor) {
// Checks that when merging blocks A and B, the OpPhi at the start of B is
// removed and uses of its definition are replaced appropriately.
const std::string prefix =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpSource ESSL 310
OpName %main "main"
OpName %x "x"
OpName %y "y"
%void = OpTypeVoid
%6 = OpTypeFunction %void
%int = OpTypeInt 32 1
%_ptr_Function_int = OpTypePointer Function %int
%int_1 = OpConstant %int 1
%main = OpFunction %void None %6
%10 = OpLabel
%x = OpVariable %_ptr_Function_int Function
%y = OpVariable %_ptr_Function_int Function
OpStore %x %int_1
%11 = OpLoad %int %x
)";
const std::string suffix_before =
R"(OpBranch %12
%12 = OpLabel
%13 = OpPhi %int %11 %10
OpStore %y %13
OpReturn
OpFunctionEnd
)";
const std::string suffix_after =
R"(OpStore %y %11
OpReturn
OpFunctionEnd
)";
SinglePassRunAndCheck<BlockMergePass>(prefix + suffix_before,
prefix + suffix_after, true, true);
}
TEST_F(BlockMergeTest, MultipleOpPhisInSuccessor) {
// Checks that when merging blocks A and B, the OpPhis at the start of B are
// removed and uses of their definitions are replaced appropriately.
const std::string prefix =
R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpSource ESSL 310
OpName %main "main"
OpName %S "S"
OpMemberName %S 0 "x"
OpMemberName %S 1 "f"
OpName %s "s"
OpName %g "g"
OpName %y "y"
OpName %t "t"
OpName %z "z"
%void = OpTypeVoid
%10 = OpTypeFunction %void
%int = OpTypeInt 32 1
%float = OpTypeFloat 32
%S = OpTypeStruct %int %float
%_ptr_Function_S = OpTypePointer Function %S
%int_1 = OpConstant %int 1
%float_2 = OpConstant %float 2
%16 = OpConstantComposite %S %int_1 %float_2
%_ptr_Function_float = OpTypePointer Function %float
%_ptr_Function_int = OpTypePointer Function %int
%int_3 = OpConstant %int 3
%int_0 = OpConstant %int 0
%main = OpFunction %void None %10
%21 = OpLabel
%s = OpVariable %_ptr_Function_S Function
%g = OpVariable %_ptr_Function_float Function
%y = OpVariable %_ptr_Function_int Function
%t = OpVariable %_ptr_Function_S Function
%z = OpVariable %_ptr_Function_float Function
OpStore %s %16
OpStore %g %float_2
OpStore %y %int_3
%22 = OpLoad %S %s
OpStore %t %22
%23 = OpAccessChain %_ptr_Function_float %s %int_1
%24 = OpLoad %float %23
%25 = OpLoad %float %g
)";
const std::string suffix_before =
R"(OpBranch %26
%26 = OpLabel
%27 = OpPhi %float %24 %21
%28 = OpPhi %float %25 %21
%29 = OpFAdd %float %27 %28
%30 = OpAccessChain %_ptr_Function_int %s %int_0
%31 = OpLoad %int %30
OpBranch %32
%32 = OpLabel
%33 = OpPhi %float %29 %26
%34 = OpPhi %int %31 %26
%35 = OpConvertSToF %float %34
OpBranch %36
%36 = OpLabel
%37 = OpPhi %float %35 %32
%38 = OpFSub %float %33 %37
%39 = OpLoad %int %y
OpBranch %40
%40 = OpLabel
%41 = OpPhi %float %38 %36
%42 = OpPhi %int %39 %36
%43 = OpConvertSToF %float %42
%44 = OpFAdd %float %41 %43
OpStore %z %44
OpReturn
OpFunctionEnd
)";
const std::string suffix_after =
R"(%29 = OpFAdd %float %24 %25
%30 = OpAccessChain %_ptr_Function_int %s %int_0
%31 = OpLoad %int %30
%35 = OpConvertSToF %float %31
%38 = OpFSub %float %29 %35
%39 = OpLoad %int %y
%43 = OpConvertSToF %float %39
%44 = OpFAdd %float %38 %43
OpStore %z %44
OpReturn
OpFunctionEnd
)";
SinglePassRunAndCheck<BlockMergePass>(prefix + suffix_before,
prefix + suffix_after, true, true);
}
TEST_F(BlockMergeTest, UnreachableLoop) {
const std::string spirv = R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpSource ESSL 310
OpName %main "main"
%void = OpTypeVoid
%4 = OpTypeFunction %void
%int = OpTypeInt 32 1
%_ptr_Function_int = OpTypePointer Function %int
%bool = OpTypeBool
%false = OpConstantFalse %bool
%main = OpFunction %void None %4
%9 = OpLabel
OpBranch %10
%11 = OpLabel
OpLoopMerge %12 %13 None
OpBranchConditional %false %13 %14
%13 = OpLabel
OpSelectionMerge %15 None
OpBranchConditional %false %16 %17
%16 = OpLabel
OpBranch %15
%17 = OpLabel
OpBranch %15
%15 = OpLabel
OpBranch %11
%14 = OpLabel
OpReturn
%12 = OpLabel
OpBranch %10
%10 = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndCheck<BlockMergePass>(spirv, spirv, true, true);
}
// TODO(greg-lunarg): Add tests to verify handling of these cases:
//
// More complex control flow
// Others?
} // namespace
} // namespace opt
} // namespace spvtools

View File

@@ -1,930 +0,0 @@
// 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 "gmock/gmock.h"
#include "gtest/gtest.h"
#include "source/opt/ccp_pass.h"
#include "test/opt/pass_fixture.h"
#include "test/opt/pass_utils.h"
namespace spvtools {
namespace opt {
namespace {
using CCPTest = PassTest<::testing::Test>;
TEST_F(CCPTest, PropagateThroughPhis) {
const std::string spv_asm = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %x %outparm
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpName %main "main"
OpName %x "x"
OpName %outparm "outparm"
OpDecorate %x Flat
OpDecorate %x Location 0
OpDecorate %outparm Location 0
%void = OpTypeVoid
%3 = OpTypeFunction %void
%int = OpTypeInt 32 1
%bool = OpTypeBool
%_ptr_Function_int = OpTypePointer Function %int
%int_4 = OpConstant %int 4
%int_3 = OpConstant %int 3
%int_1 = OpConstant %int 1
%_ptr_Input_int = OpTypePointer Input %int
%x = OpVariable %_ptr_Input_int Input
%_ptr_Output_int = OpTypePointer Output %int
%outparm = OpVariable %_ptr_Output_int Output
%main = OpFunction %void None %3
%4 = OpLabel
%5 = OpLoad %int %x
%9 = OpIAdd %int %int_1 %int_3
%6 = OpSGreaterThan %bool %5 %int_3
OpSelectionMerge %25 None
OpBranchConditional %6 %22 %23
%22 = OpLabel
; CHECK: OpCopyObject %int %int_4
%7 = OpCopyObject %int %9
OpBranch %25
%23 = OpLabel
%8 = OpCopyObject %int %int_4
OpBranch %25
%25 = OpLabel
; %int_4 should have propagated to both OpPhi operands.
; CHECK: OpPhi %int %int_4 {{%\d+}} %int_4 {{%\d+}}
%35 = OpPhi %int %7 %22 %8 %23
; This function always returns 4. DCE should get rid of everything else.
; CHECK OpStore %outparm %int_4
OpStore %outparm %35
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<CCPPass>(spv_asm, true);
}
TEST_F(CCPTest, SimplifyConditionals) {
const std::string spv_asm = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %outparm
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpName %main "main"
OpName %outparm "outparm"
OpDecorate %outparm Location 0
%void = OpTypeVoid
%3 = OpTypeFunction %void
%int = OpTypeInt 32 1
%bool = OpTypeBool
%_ptr_Function_int = OpTypePointer Function %int
%int_4 = OpConstant %int 4
%int_3 = OpConstant %int 3
%int_1 = OpConstant %int 1
%_ptr_Output_int = OpTypePointer Output %int
%outparm = OpVariable %_ptr_Output_int Output
%main = OpFunction %void None %3
%4 = OpLabel
%9 = OpIAdd %int %int_4 %int_3
%6 = OpSGreaterThan %bool %9 %int_3
OpSelectionMerge %25 None
; CHECK: OpBranchConditional %true [[bb_taken:%\d+]] [[bb_not_taken:%\d+]]
OpBranchConditional %6 %22 %23
; CHECK: [[bb_taken]] = OpLabel
%22 = OpLabel
; CHECK: OpCopyObject %int %int_7
%7 = OpCopyObject %int %9
OpBranch %25
; CHECK: [[bb_not_taken]] = OpLabel
%23 = OpLabel
; CHECK: [[id_not_evaluated:%\d+]] = OpCopyObject %int %int_4
%8 = OpCopyObject %int %int_4
OpBranch %25
%25 = OpLabel
; %int_7 should have propagated to the first OpPhi operand. But the else branch
; is not executable (conditional is always true), so no values should be
; propagated there and the value of the OpPhi should always be %int_7.
; CHECK: OpPhi %int %int_7 [[bb_taken]] [[id_not_evaluated]] [[bb_not_taken]]
%35 = OpPhi %int %7 %22 %8 %23
; Only the true path of the conditional is ever executed. The output of this
; function is always %int_7.
; CHECK: OpStore %outparm %int_7
OpStore %outparm %35
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<CCPPass>(spv_asm, true);
}
TEST_F(CCPTest, SimplifySwitches) {
const std::string spv_asm = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %outparm
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpName %main "main"
OpName %outparm "outparm"
OpDecorate %outparm Location 0
%void = OpTypeVoid
%6 = OpTypeFunction %void
%int = OpTypeInt 32 1
%_ptr_Function_int = OpTypePointer Function %int
%int_23 = OpConstant %int 23
%int_42 = OpConstant %int 42
%int_14 = OpConstant %int 14
%int_15 = OpConstant %int 15
%int_4 = OpConstant %int 4
%_ptr_Output_int = OpTypePointer Output %int
%outparm = OpVariable %_ptr_Output_int Output
%main = OpFunction %void None %6
%15 = OpLabel
OpSelectionMerge %17 None
OpSwitch %int_23 %17 10 %18 13 %19 23 %20
%18 = OpLabel
OpBranch %17
%19 = OpLabel
OpBranch %17
%20 = OpLabel
OpBranch %17
%17 = OpLabel
%24 = OpPhi %int %int_23 %15 %int_42 %18 %int_14 %19 %int_15 %20
; The switch will always jump to label %20, which carries the value %int_15.
; CHECK: OpIAdd %int %int_15 %int_4
%22 = OpIAdd %int %24 %int_4
; Consequently, the return value will always be %int_19.
; CHECK: OpStore %outparm %int_19
OpStore %outparm %22
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<CCPPass>(spv_asm, true);
}
TEST_F(CCPTest, SimplifySwitchesDefaultBranch) {
const std::string spv_asm = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %outparm
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpName %main "main"
OpName %outparm "outparm"
OpDecorate %outparm Location 0
%void = OpTypeVoid
%6 = OpTypeFunction %void
%int = OpTypeInt 32 1
%_ptr_Function_int = OpTypePointer Function %int
%int_42 = OpConstant %int 42
%int_4 = OpConstant %int 4
%int_1 = OpConstant %int 1
%_ptr_Output_int = OpTypePointer Output %int
%outparm = OpVariable %_ptr_Output_int Output
%main = OpFunction %void None %6
%13 = OpLabel
%15 = OpIAdd %int %int_42 %int_4
OpSelectionMerge %16 None
; CHECK: OpSwitch %int_46 {{%\d+}} 10 {{%\d+}}
OpSwitch %15 %17 10 %18
%18 = OpLabel
OpBranch %16
%17 = OpLabel
OpBranch %16
%16 = OpLabel
%22 = OpPhi %int %int_42 %18 %int_1 %17
; The switch will always jump to the default label %17. This carries the value
; %int_1.
; CHECK: OpIAdd %int %int_1 %int_4
%20 = OpIAdd %int %22 %int_4
; Resulting in a return value of %int_5.
; CHECK: OpStore %outparm %int_5
OpStore %outparm %20
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<CCPPass>(spv_asm, true);
}
TEST_F(CCPTest, SimplifyIntVector) {
const std::string spv_asm = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %OutColor
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpName %main "main"
OpName %v "v"
OpName %OutColor "OutColor"
OpDecorate %OutColor Location 0
%void = OpTypeVoid
%3 = OpTypeFunction %void
%int = OpTypeInt 32 1
%v4int = OpTypeVector %int 4
%_ptr_Function_v4int = OpTypePointer Function %v4int
%int_1 = OpConstant %int 1
%int_2 = OpConstant %int 2
%int_3 = OpConstant %int 3
%int_4 = OpConstant %int 4
%14 = OpConstantComposite %v4int %int_1 %int_2 %int_3 %int_4
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%_ptr_Function_int = OpTypePointer Function %int
%_ptr_Output_v4int = OpTypePointer Output %v4int
%OutColor = OpVariable %_ptr_Output_v4int Output
%main = OpFunction %void None %3
%5 = OpLabel
%v = OpVariable %_ptr_Function_v4int Function
OpStore %v %14
%18 = OpAccessChain %_ptr_Function_int %v %uint_0
%19 = OpLoad %int %18
; The constant folder does not see through access chains. To get this, the
; vector would have to be scalarized.
; CHECK: [[result_id:%\d+]] = OpIAdd %int {{%\d+}} %int_1
%20 = OpIAdd %int %19 %int_1
%21 = OpAccessChain %_ptr_Function_int %v %uint_0
; CHECK: OpStore {{%\d+}} [[result_id]]
OpStore %21 %20
%24 = OpLoad %v4int %v
OpStore %OutColor %24
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<CCPPass>(spv_asm, true);
}
TEST_F(CCPTest, BadSimplifyFloatVector) {
const std::string spv_asm = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %OutColor
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpName %main "main"
OpName %v "v"
OpName %OutColor "OutColor"
OpDecorate %OutColor Location 0
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Function_v4float = OpTypePointer Function %v4float
%float_1 = OpConstant %float 1
%float_2 = OpConstant %float 2
%float_3 = OpConstant %float 3
%float_4 = OpConstant %float 4
%14 = OpConstantComposite %v4float %float_1 %float_2 %float_3 %float_4
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%_ptr_Function_float = OpTypePointer Function %float
%_ptr_Output_v4float = OpTypePointer Output %v4float
%OutColor = OpVariable %_ptr_Output_v4float Output
%main = OpFunction %void None %3
%5 = OpLabel
%v = OpVariable %_ptr_Function_v4float Function
OpStore %v %14
%18 = OpAccessChain %_ptr_Function_float %v %uint_0
%19 = OpLoad %float %18
; NOTE: This test should start failing once floating point folding is
; implemented (https://github.com/KhronosGroup/SPIRV-Tools/issues/943).
; This should be checking that we are adding %float_1 + %float_1.
; CHECK: [[result_id:%\d+]] = OpFAdd %float {{%\d+}} %float_1
%20 = OpFAdd %float %19 %float_1
%21 = OpAccessChain %_ptr_Function_float %v %uint_0
; This should be checkint that we are storing %float_2 instead of result_it.
; CHECK: OpStore {{%\d+}} [[result_id]]
OpStore %21 %20
%24 = OpLoad %v4float %v
OpStore %OutColor %24
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<CCPPass>(spv_asm, true);
}
TEST_F(CCPTest, NoLoadStorePropagation) {
const std::string spv_asm = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %outparm
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpName %main "main"
OpName %x "x"
OpName %outparm "outparm"
OpDecorate %outparm Location 0
%void = OpTypeVoid
%3 = OpTypeFunction %void
%int = OpTypeInt 32 1
%_ptr_Function_int = OpTypePointer Function %int
%int_23 = OpConstant %int 23
%_ptr_Output_int = OpTypePointer Output %int
%outparm = OpVariable %_ptr_Output_int Output
%main = OpFunction %void None %3
%5 = OpLabel
%x = OpVariable %_ptr_Function_int Function
OpStore %x %int_23
; int_23 should not propagate into this load.
; CHECK: [[load_id:%\d+]] = OpLoad %int %x
%12 = OpLoad %int %x
; Nor into this copy operation.
; CHECK: [[copy_id:%\d+]] = OpCopyObject %int [[load_id]]
%13 = OpCopyObject %int %12
; Likewise here.
; CHECK: OpStore %outparm [[copy_id]]
OpStore %outparm %13
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<CCPPass>(spv_asm, true);
}
TEST_F(CCPTest, HandleAbortInstructions) {
const std::string spv_asm = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpSource HLSL 500
OpName %main "main"
%void = OpTypeVoid
%3 = OpTypeFunction %void
%int = OpTypeInt 32 1
%bool = OpTypeBool
; CHECK: %true = OpConstantTrue %bool
%int_3 = OpConstant %int 3
%int_1 = OpConstant %int 1
%main = OpFunction %void None %3
%4 = OpLabel
%9 = OpIAdd %int %int_3 %int_1
%6 = OpSGreaterThan %bool %9 %int_3
OpSelectionMerge %23 None
; CHECK: OpBranchConditional %true {{%\d+}} {{%\d+}}
OpBranchConditional %6 %22 %23
%22 = OpLabel
OpKill
%23 = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<CCPPass>(spv_asm, true);
}
TEST_F(CCPTest, SSAWebCycles) {
// Test reduced from https://github.com/KhronosGroup/SPIRV-Tools/issues/1159
// When there is a cycle in the SSA def-use web, the propagator was getting
// into an infinite loop. SSA edges for Phi instructions should not be
// added to the edges to simulate.
const std::string spv_asm = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpName %main "main"
%void = OpTypeVoid
%3 = OpTypeFunction %void
%int = OpTypeInt 32 1
%_ptr_Function_int = OpTypePointer Function %int
%int_0 = OpConstant %int 0
%int_4 = OpConstant %int 4
%bool = OpTypeBool
%int_1 = OpConstant %int 1
%_ptr_Output_int = OpTypePointer Output %int
%main = OpFunction %void None %3
%5 = OpLabel
OpBranch %11
%11 = OpLabel
%29 = OpPhi %int %int_0 %5 %22 %14
%30 = OpPhi %int %int_0 %5 %25 %14
OpLoopMerge %13 %14 None
OpBranch %15
%15 = OpLabel
%19 = OpSLessThan %bool %30 %int_4
; CHECK: OpBranchConditional %true {{%\d+}} {{%\d+}}
OpBranchConditional %19 %12 %13
%12 = OpLabel
; CHECK: OpIAdd %int %int_0 %int_0
%22 = OpIAdd %int %29 %30
OpBranch %14
%14 = OpLabel
; CHECK: OpPhi %int %int_0 {{%\d+}}
%25 = OpPhi %int %30 %12
OpBranch %11
%13 = OpLabel
OpReturn
OpFunctionEnd
)";
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndMatch<CCPPass>(spv_asm, true);
}
TEST_F(CCPTest, LoopInductionVariables) {
// Test reduced from https://github.com/KhronosGroup/SPIRV-Tools/issues/1143
// We are failing to properly consider the induction variable for this loop
// as Varying.
const std::string spv_asm = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 430
OpName %main "main"
%void = OpTypeVoid
%5 = OpTypeFunction %void
%int = OpTypeInt 32 1
%_ptr_Function_int = OpTypePointer Function %int
%int_0 = OpConstant %int 0
%int_10 = OpConstant %int 10
%bool = OpTypeBool
%int_1 = OpConstant %int 1
%main = OpFunction %void None %5
%12 = OpLabel
OpBranch %13
%13 = OpLabel
; This Phi should not have all constant arguments:
; CHECK: [[phi_id:%\d+]] = OpPhi %int %int_0 {{%\d+}} {{%\d+}} {{%\d+}}
%22 = OpPhi %int %int_0 %12 %21 %15
OpLoopMerge %14 %15 None
OpBranch %16
%16 = OpLabel
; The Phi should never be considered to have the value %int_0.
; CHECK: [[branch_selector:%\d+]] = OpSLessThan %bool [[phi_id]] %int_10
%18 = OpSLessThan %bool %22 %int_10
; This conditional was wrongly converted into an always-true jump due to the
; bad meet evaluation of %22.
; CHECK: OpBranchConditional [[branch_selector]] {{%\d+}} {{%\d+}}
OpBranchConditional %18 %19 %14
%19 = OpLabel
OpBranch %15
%15 = OpLabel
; CHECK: OpIAdd %int [[phi_id]] %int_1
%21 = OpIAdd %int %22 %int_1
OpBranch %13
%14 = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<CCPPass>(spv_asm, true);
}
TEST_F(CCPTest, HandleCompositeWithUndef) {
// Check to make sure that CCP does not crash when given a "constant" struct
// with an undef. If at a later time CCP is enhanced to optimize this case,
// it is not wrong.
const std::string spv_asm = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpSource HLSL 500
OpName %main "main"
%void = OpTypeVoid
%4 = OpTypeFunction %void
%int = OpTypeInt 32 1
%bool = OpTypeBool
%_struct_7 = OpTypeStruct %int %int
%int_1 = OpConstant %int 1
%9 = OpUndef %int
%10 = OpConstantComposite %_struct_7 %int_1 %9
%main = OpFunction %void None %4
%11 = OpLabel
%12 = OpCompositeExtract %int %10 0
%13 = OpCopyObject %int %12
OpReturn
OpFunctionEnd
)";
auto res = SinglePassRunToBinary<CCPPass>(spv_asm, true);
EXPECT_EQ(std::get<1>(res), Pass::Status::SuccessWithoutChange);
}
TEST_F(CCPTest, SkipSpecConstantInstrucitons) {
const std::string spv_asm = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpSource HLSL 500
OpName %main "main"
%void = OpTypeVoid
%4 = OpTypeFunction %void
%bool = OpTypeBool
%10 = OpSpecConstantFalse %bool
%main = OpFunction %void None %4
%11 = OpLabel
OpBranchConditional %10 %L1 %L2
%L1 = OpLabel
OpReturn
%L2 = OpLabel
OpReturn
OpFunctionEnd
)";
auto res = SinglePassRunToBinary<CCPPass>(spv_asm, true);
EXPECT_EQ(std::get<1>(res), Pass::Status::SuccessWithoutChange);
}
TEST_F(CCPTest, UpdateSubsequentPhisToVarying) {
const std::string text = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func" %in
OpExecutionMode %func OriginUpperLeft
%void = OpTypeVoid
%bool = OpTypeBool
%int = OpTypeInt 32 1
%false = OpConstantFalse %bool
%int0 = OpConstant %int 0
%int1 = OpConstant %int 1
%int6 = OpConstant %int 6
%int_ptr_Input = OpTypePointer Input %int
%in = OpVariable %int_ptr_Input Input
%undef = OpUndef %int
%functy = OpTypeFunction %void
%func = OpFunction %void None %functy
%1 = OpLabel
OpBranch %2
%2 = OpLabel
%outer_phi = OpPhi %int %int0 %1 %outer_add %15
%cond1 = OpSLessThanEqual %bool %outer_phi %int6
OpLoopMerge %3 %15 None
OpBranchConditional %cond1 %4 %3
%4 = OpLabel
%ld = OpLoad %int %in
%cond2 = OpSGreaterThanEqual %bool %int1 %ld
OpSelectionMerge %10 None
OpBranchConditional %cond2 %8 %9
%8 = OpLabel
OpBranch %10
%9 = OpLabel
OpBranch %10
%10 = OpLabel
%extra_phi = OpPhi %int %outer_phi %8 %outer_phi %9
OpBranch %11
%11 = OpLabel
%inner_phi = OpPhi %int %int0 %10 %inner_add %13
%cond3 = OpSLessThanEqual %bool %inner_phi %int6
OpLoopMerge %14 %13 None
OpBranchConditional %cond3 %12 %14
%12 = OpLabel
OpBranch %13
%13 = OpLabel
%inner_add = OpIAdd %int %inner_phi %int1
OpBranch %11
%14 = OpLabel
OpBranch %15
%15 = OpLabel
%outer_add = OpIAdd %int %extra_phi %int1
OpBranch %2
%3 = OpLabel
OpReturn
OpFunctionEnd
)";
auto res = SinglePassRunToBinary<CCPPass>(text, true);
EXPECT_EQ(std::get<1>(res), Pass::Status::SuccessWithoutChange);
}
TEST_F(CCPTest, UndefInPhi) {
const std::string text = R"(
; CHECK: [[uint1:%\w+]] = OpConstant {{%\w+}} 1
; CHECK: [[phi:%\w+]] = OpPhi
; CHECK: OpIAdd {{%\w+}} [[phi]] [[uint1]]
OpCapability Kernel
OpCapability Linkage
OpMemoryModel Logical OpenCL
OpDecorate %1 LinkageAttributes "func" Export
%void = OpTypeVoid
%bool = OpTypeBool
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%uint_1 = OpConstant %uint 1
%7 = OpUndef %uint
%8 = OpTypeFunction %void %bool
%1 = OpFunction %void None %8
%9 = OpFunctionParameter %bool
%10 = OpLabel
OpBranchConditional %9 %11 %12
%11 = OpLabel
OpBranch %13
%12 = OpLabel
OpBranch %14
%14 = OpLabel
OpBranchConditional %9 %13 %15
%15 = OpLabel
OpBranch %13
%13 = OpLabel
%16 = OpPhi %uint %uint_0 %11 %7 %14 %uint_1 %15
%17 = OpIAdd %uint %16 %uint_1
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<CCPPass>(text, true);
}
// Just test to make sure the constant fold rules are being used. Will rely on
// the folding test for specific testing of specific rules.
TEST_F(CCPTest, UseConstantFoldingRules) {
const std::string text = R"(
; CHECK: [[float1:%\w+]] = OpConstant {{%\w+}} 1
; CHECK: OpReturnValue [[float1]]
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
OpDecorate %1 LinkageAttributes "func" Export
%void = OpTypeVoid
%bool = OpTypeBool
%float = OpTypeFloat 32
%float_0 = OpConstant %float 0
%float_1 = OpConstant %float 1
%8 = OpTypeFunction %float
%1 = OpFunction %float None %8
%10 = OpLabel
%17 = OpFAdd %float %float_0 %float_1
OpReturnValue %17
OpFunctionEnd
)";
SinglePassRunAndMatch<CCPPass>(text, true);
}
// Test for #1300. Previously value for %5 would not settle during simulation.
TEST_F(CCPTest, SettlePhiLatticeValue) {
const std::string text = R"(
OpCapability Kernel
OpCapability Linkage
OpMemoryModel Logical OpenCL
OpDecorate %func LinkageAttributes "func" Export
%void = OpTypeVoid
%bool = OpTypeBool
%true = OpConstantTrue %bool
%false = OpConstantFalse %bool
%functy = OpTypeFunction %void
%func = OpFunction %void None %functy
%1 = OpLabel
OpBranchConditional %true %2 %3
%3 = OpLabel
OpBranch %2
%2 = OpLabel
%5 = OpPhi %bool %true %1 %false %3
OpReturn
OpFunctionEnd
)";
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunToBinary<CCPPass>(text, true);
}
TEST_F(CCPTest, NullBranchCondition) {
const std::string text = R"(
; CHECK: [[int1:%\w+]] = OpConstant {{%\w+}} 1
; CHECK: [[int2:%\w+]] = OpConstant {{%\w+}} 2
; CHECK: OpIAdd {{%\w+}} [[int1]] [[int2]]
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
%void = OpTypeVoid
%bool = OpTypeBool
%int = OpTypeInt 32 1
%null = OpConstantNull %bool
%int_1 = OpConstant %int 1
%int_2 = OpConstant %int 2
%functy = OpTypeFunction %void
%func = OpFunction %void None %functy
%1 = OpLabel
OpSelectionMerge %2 None
OpBranchConditional %null %2 %3
%3 = OpLabel
OpBranch %2
%2 = OpLabel
%phi = OpPhi %int %int_1 %1 %int_2 %3
%add = OpIAdd %int %int_1 %phi
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<CCPPass>(text, true);
}
TEST_F(CCPTest, UndefBranchCondition) {
const std::string text = R"(
; CHECK: [[int1:%\w+]] = OpConstant {{%\w+}} 1
; CHECK: [[phi:%\w+]] = OpPhi
; CHECK: OpIAdd {{%\w+}} [[int1]] [[phi]]
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
%void = OpTypeVoid
%bool = OpTypeBool
%int = OpTypeInt 32 1
%undef = OpUndef %bool
%int_1 = OpConstant %int 1
%int_2 = OpConstant %int 2
%functy = OpTypeFunction %void
%func = OpFunction %void None %functy
%1 = OpLabel
OpSelectionMerge %2 None
OpBranchConditional %undef %2 %3
%3 = OpLabel
OpBranch %2
%2 = OpLabel
%phi = OpPhi %int %int_1 %1 %int_2 %3
%add = OpIAdd %int %int_1 %phi
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<CCPPass>(text, true);
}
TEST_F(CCPTest, NullSwitchCondition) {
const std::string text = R"(
; CHECK: [[int1:%\w+]] = OpConstant {{%\w+}} 1
; CHECK: [[int2:%\w+]] = OpConstant {{%\w+}} 2
; CHECK: OpIAdd {{%\w+}} [[int1]] [[int2]]
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
%void = OpTypeVoid
%int = OpTypeInt 32 1
%null = OpConstantNull %int
%int_1 = OpConstant %int 1
%int_2 = OpConstant %int 2
%functy = OpTypeFunction %void
%func = OpFunction %void None %functy
%1 = OpLabel
OpSelectionMerge %2 None
OpSwitch %null %2 0 %3
%3 = OpLabel
OpBranch %2
%2 = OpLabel
%phi = OpPhi %int %int_1 %1 %int_2 %3
%add = OpIAdd %int %int_1 %phi
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<CCPPass>(text, true);
}
TEST_F(CCPTest, UndefSwitchCondition) {
const std::string text = R"(
; CHECK: [[int1:%\w+]] = OpConstant {{%\w+}} 1
; CHECK: [[phi:%\w+]] = OpPhi
; CHECK: OpIAdd {{%\w+}} [[int1]] [[phi]]
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
%void = OpTypeVoid
%int = OpTypeInt 32 1
%undef = OpUndef %int
%int_1 = OpConstant %int 1
%int_2 = OpConstant %int 2
%functy = OpTypeFunction %void
%func = OpFunction %void None %functy
%1 = OpLabel
OpSelectionMerge %2 None
OpSwitch %undef %2 0 %3
%3 = OpLabel
OpBranch %2
%2 = OpLabel
%phi = OpPhi %int %int_1 %1 %int_2 %3
%add = OpIAdd %int %int_1 %phi
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<CCPPass>(text, true);
}
// Test for #1361.
TEST_F(CCPTest, CompositeConstructOfGlobalValue) {
const std::string text = R"(
; CHECK: [[phi:%\w+]] = OpPhi
; CHECK-NEXT: OpCompositeExtract {{%\w+}} [[phi]] 0
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func" %in
OpExecutionMode %func OriginUpperLeft
%void = OpTypeVoid
%int = OpTypeInt 32 1
%bool = OpTypeBool
%functy = OpTypeFunction %void
%ptr_int_Input = OpTypePointer Input %int
%in = OpVariable %ptr_int_Input Input
%struct = OpTypeStruct %ptr_int_Input %ptr_int_Input
%struct_null = OpConstantNull %struct
%func = OpFunction %void None %functy
%1 = OpLabel
OpBranch %2
%2 = OpLabel
%phi = OpPhi %struct %struct_null %1 %5 %4
%extract = OpCompositeExtract %ptr_int_Input %phi 0
OpLoopMerge %3 %4 None
OpBranch %4
%4 = OpLabel
%5 = OpCompositeConstruct %struct %in %in
OpBranch %2
%3 = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<CCPPass>(text, true);
}
TEST_F(CCPTest, FoldWithDecoration) {
const std::string text = R"(
; CHECK: OpCapability
; CHECK-NOT: OpDecorate
; CHECK: OpFunctionEnd
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource ESSL 310
OpDecorate %3 RelaxedPrecision
%void = OpTypeVoid
%5 = OpTypeFunction %void
%float = OpTypeFloat 32
%v3float = OpTypeVector %float 3
%float_0 = OpConstant %float 0
%v4float = OpTypeVector %float 4
%10 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
%2 = OpFunction %void None %5
%11 = OpLabel
%3 = OpVectorShuffle %v3float %10 %10 0 1 2
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<CCPPass>(text, true);
}
} // namespace
} // namespace opt
} // namespace spvtools

View File

@@ -1,456 +0,0 @@
// 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 "test/opt/pass_fixture.h"
#include "test/opt/pass_utils.h"
namespace spvtools {
namespace opt {
namespace {
using CFGCleanupTest = PassTest<::testing::Test>;
TEST_F(CFGCleanupTest, RemoveUnreachableBlocks) {
const std::string declarations = R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %inf %outf4
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpName %main "main"
OpName %inf "inf"
OpName %outf4 "outf4"
OpDecorate %inf Location 0
OpDecorate %outf4 Location 0
%void = OpTypeVoid
%6 = OpTypeFunction %void
%float = OpTypeFloat 32
%_ptr_Input_float = OpTypePointer Input %float
%inf = OpVariable %_ptr_Input_float Input
%float_2 = OpConstant %float 2
%bool = OpTypeBool
%v4float = OpTypeVector %float 4
%_ptr_Output_v4float = OpTypePointer Output %v4float
%outf4 = OpVariable %_ptr_Output_v4float Output
%float_n0_5 = OpConstant %float -0.5
)";
const std::string body_before = R"(%main = OpFunction %void None %6
%14 = OpLabel
OpBranch %18
%19 = OpLabel
%20 = OpLoad %float %inf
%21 = OpCompositeConstruct %v4float %20 %20 %20 %20
OpStore %outf4 %21
OpBranch %17
%18 = OpLabel
%22 = OpLoad %float %inf
%23 = OpFAdd %float %22 %float_n0_5
%24 = OpCompositeConstruct %v4float %23 %23 %23 %23
OpStore %outf4 %24
OpBranch %17
%17 = OpLabel
OpReturn
OpFunctionEnd
)";
const std::string body_after = R"(%main = OpFunction %void None %6
%14 = OpLabel
OpBranch %15
%15 = OpLabel
%20 = OpLoad %float %inf
%21 = OpFAdd %float %20 %float_n0_5
%22 = OpCompositeConstruct %v4float %21 %21 %21 %21
OpStore %outf4 %22
OpBranch %19
%19 = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndCheck<CFGCleanupPass>(declarations + body_before,
declarations + body_after, true, true);
}
TEST_F(CFGCleanupTest, RemoveDecorations) {
const std::string before = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpName %main "main"
OpName %x "x"
OpName %dead "dead"
OpDecorate %x RelaxedPrecision
OpDecorate %dead RelaxedPrecision
%void = OpTypeVoid
%6 = OpTypeFunction %void
%float = OpTypeFloat 32
%_ptr_Function_float = OpTypePointer Function %float
%float_2 = OpConstant %float 2
%float_4 = OpConstant %float 4
%main = OpFunction %void None %6
%14 = OpLabel
%x = OpVariable %_ptr_Function_float Function
OpBranch %18
%19 = OpLabel
%dead = OpVariable %_ptr_Function_float Function
OpStore %dead %float_2
OpBranch %17
%18 = OpLabel
OpStore %x %float_4
OpBranch %17
%17 = OpLabel
OpReturn
OpFunctionEnd
)";
const std::string after = R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpName %main "main"
OpName %x "x"
OpDecorate %x RelaxedPrecision
%void = OpTypeVoid
%6 = OpTypeFunction %void
%float = OpTypeFloat 32
%_ptr_Function_float = OpTypePointer Function %float
%float_2 = OpConstant %float 2
%float_4 = OpConstant %float 4
%main = OpFunction %void None %6
%11 = OpLabel
%x = OpVariable %_ptr_Function_float Function
OpBranch %12
%12 = OpLabel
OpStore %x %float_4
OpBranch %14
%14 = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndCheck<CFGCleanupPass>(before, after, true, true);
}
TEST_F(CFGCleanupTest, UpdatePhis) {
const std::string before = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %y %outparm
OpExecutionMode %main OriginUpperLeft
OpName %main "main"
OpName %y "y"
OpName %outparm "outparm"
OpDecorate %y Flat
OpDecorate %y Location 0
OpDecorate %outparm Location 0
%void = OpTypeVoid
%3 = OpTypeFunction %void
%int = OpTypeInt 32 1
%_ptr_Function_int = OpTypePointer Function %int
%_ptr_Input_int = OpTypePointer Input %int
%y = OpVariable %_ptr_Input_int Input
%int_10 = OpConstant %int 10
%bool = OpTypeBool
%int_42 = OpConstant %int 42
%int_23 = OpConstant %int 23
%int_5 = OpConstant %int 5
%_ptr_Output_int = OpTypePointer Output %int
%outparm = OpVariable %_ptr_Output_int Output
%main = OpFunction %void None %3
%5 = OpLabel
%11 = OpLoad %int %y
OpBranch %21
%16 = OpLabel
%20 = OpIAdd %int %11 %int_42
OpBranch %17
%21 = OpLabel
%24 = OpISub %int %11 %int_23
OpBranch %17
%17 = OpLabel
%31 = OpPhi %int %20 %16 %24 %21
%27 = OpIAdd %int %31 %int_5
OpStore %outparm %27
OpReturn
OpFunctionEnd
)";
const std::string after = R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %y %outparm
OpExecutionMode %main OriginUpperLeft
OpName %main "main"
OpName %y "y"
OpName %outparm "outparm"
OpDecorate %y Flat
OpDecorate %y Location 0
OpDecorate %outparm Location 0
%void = OpTypeVoid
%6 = OpTypeFunction %void
%int = OpTypeInt 32 1
%_ptr_Function_int = OpTypePointer Function %int
%_ptr_Input_int = OpTypePointer Input %int
%y = OpVariable %_ptr_Input_int Input
%int_10 = OpConstant %int 10
%bool = OpTypeBool
%int_42 = OpConstant %int 42
%int_23 = OpConstant %int 23
%int_5 = OpConstant %int 5
%_ptr_Output_int = OpTypePointer Output %int
%outparm = OpVariable %_ptr_Output_int Output
%main = OpFunction %void None %6
%16 = OpLabel
%17 = OpLoad %int %y
OpBranch %18
%18 = OpLabel
%22 = OpISub %int %17 %int_23
OpBranch %21
%21 = OpLabel
%23 = OpPhi %int %22 %18
%24 = OpIAdd %int %23 %int_5
OpStore %outparm %24
OpReturn
OpFunctionEnd
)";
SinglePassRunAndCheck<CFGCleanupPass>(before, after, true, true);
}
TEST_F(CFGCleanupTest, RemoveNamedLabels) {
const std::string before = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main"
OpSource GLSL 430
OpName %main "main"
OpName %dead "dead"
%void = OpTypeVoid
%5 = OpTypeFunction %void
%main = OpFunction %void None %5
%6 = OpLabel
OpReturn
%dead = OpLabel
OpReturn
OpFunctionEnd)";
const std::string after = R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main"
OpSource GLSL 430
OpName %main "main"
%void = OpTypeVoid
%5 = OpTypeFunction %void
%main = OpFunction %void None %5
%6 = OpLabel
OpReturn
OpFunctionEnd
)";
SinglePassRunAndCheck<CFGCleanupPass>(before, after, true, true);
}
TEST_F(CFGCleanupTest, RemovePhiArgsFromFarBlocks) {
const std::string before = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %y %outparm
OpExecutionMode %main OriginUpperLeft
OpName %main "main"
OpName %y "y"
OpName %outparm "outparm"
OpDecorate %y Flat
OpDecorate %y Location 0
OpDecorate %outparm Location 0
%void = OpTypeVoid
%3 = OpTypeFunction %void
%int = OpTypeInt 32 1
%_ptr_Function_int = OpTypePointer Function %int
%_ptr_Input_int = OpTypePointer Input %int
%y = OpVariable %_ptr_Input_int Input
%int_42 = OpConstant %int 42
%_ptr_Output_int = OpTypePointer Output %int
%outparm = OpVariable %_ptr_Output_int Output
%int_14 = OpConstant %int 14
%int_15 = OpConstant %int 15
%int_5 = OpConstant %int 5
%main = OpFunction %void None %3
%5 = OpLabel
OpBranch %40
%41 = OpLabel
%11 = OpLoad %int %y
OpBranch %40
%40 = OpLabel
%12 = OpLoad %int %y
OpSelectionMerge %16 None
OpSwitch %12 %16 10 %13 13 %14 18 %15
%13 = OpLabel
OpBranch %16
%14 = OpLabel
OpStore %outparm %int_14
OpBranch %16
%15 = OpLabel
OpStore %outparm %int_15
OpBranch %16
%16 = OpLabel
%30 = OpPhi %int %11 %40 %int_42 %13 %11 %14 %11 %15
%28 = OpIAdd %int %30 %int_5
OpStore %outparm %28
OpReturn
OpFunctionEnd)";
const std::string after = R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %y %outparm
OpExecutionMode %main OriginUpperLeft
OpName %main "main"
OpName %y "y"
OpName %outparm "outparm"
OpDecorate %y Flat
OpDecorate %y Location 0
OpDecorate %outparm Location 0
%void = OpTypeVoid
%6 = OpTypeFunction %void
%int = OpTypeInt 32 1
%_ptr_Function_int = OpTypePointer Function %int
%_ptr_Input_int = OpTypePointer Input %int
%y = OpVariable %_ptr_Input_int Input
%int_42 = OpConstant %int 42
%_ptr_Output_int = OpTypePointer Output %int
%outparm = OpVariable %_ptr_Output_int Output
%int_14 = OpConstant %int 14
%int_15 = OpConstant %int 15
%int_5 = OpConstant %int 5
%26 = OpUndef %int
%main = OpFunction %void None %6
%15 = OpLabel
OpBranch %16
%16 = OpLabel
%19 = OpLoad %int %y
OpSelectionMerge %20 None
OpSwitch %19 %20 10 %21 13 %22 18 %23
%21 = OpLabel
OpBranch %20
%22 = OpLabel
OpStore %outparm %int_14
OpBranch %20
%23 = OpLabel
OpStore %outparm %int_15
OpBranch %20
%20 = OpLabel
%24 = OpPhi %int %26 %16 %int_42 %21 %26 %22 %26 %23
%25 = OpIAdd %int %24 %int_5
OpStore %outparm %25
OpReturn
OpFunctionEnd
)";
SinglePassRunAndCheck<CFGCleanupPass>(before, after, true, true);
}
TEST_F(CFGCleanupTest, RemovePhiConstantArgs) {
const std::string before = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %y %outparm
OpExecutionMode %main OriginUpperLeft
OpName %main "main"
OpName %y "y"
OpName %outparm "outparm"
OpDecorate %y Flat
OpDecorate %y Location 0
OpDecorate %outparm Location 0
%void = OpTypeVoid
%3 = OpTypeFunction %void
%int = OpTypeInt 32 1
%_ptr_Input_int = OpTypePointer Input %int
%y = OpVariable %_ptr_Input_int Input
%int_10 = OpConstant %int 10
%bool = OpTypeBool
%_ptr_Function_int = OpTypePointer Function %int
%int_23 = OpConstant %int 23
%int_5 = OpConstant %int 5
%_ptr_Output_int = OpTypePointer Output %int
%outparm = OpVariable %_ptr_Output_int Output
%24 = OpUndef %int
%main = OpFunction %void None %3
%5 = OpLabel
OpBranch %14
%40 = OpLabel
%9 = OpLoad %int %y
%12 = OpSGreaterThan %bool %9 %int_10
OpSelectionMerge %14 None
OpBranchConditional %12 %13 %14
%13 = OpLabel
OpBranch %14
%14 = OpLabel
%25 = OpPhi %int %24 %5 %int_23 %13
%20 = OpIAdd %int %25 %int_5
OpStore %outparm %20
OpReturn
OpFunctionEnd)";
const std::string after = R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %y %outparm
OpExecutionMode %main OriginUpperLeft
OpName %main "main"
OpName %y "y"
OpName %outparm "outparm"
OpDecorate %y Flat
OpDecorate %y Location 0
OpDecorate %outparm Location 0
%void = OpTypeVoid
%6 = OpTypeFunction %void
%int = OpTypeInt 32 1
%_ptr_Input_int = OpTypePointer Input %int
%y = OpVariable %_ptr_Input_int Input
%int_10 = OpConstant %int 10
%bool = OpTypeBool
%_ptr_Function_int = OpTypePointer Function %int
%int_23 = OpConstant %int 23
%int_5 = OpConstant %int 5
%_ptr_Output_int = OpTypePointer Output %int
%outparm = OpVariable %_ptr_Output_int Output
%15 = OpUndef %int
%main = OpFunction %void None %6
%16 = OpLabel
OpBranch %17
%17 = OpLabel
%22 = OpPhi %int %15 %16
%23 = OpIAdd %int %22 %int_5
OpStore %outparm %23
OpReturn
OpFunctionEnd
)";
SinglePassRunAndCheck<CFGCleanupPass>(before, after, true, true);
}
} // namespace
} // namespace opt
} // namespace spvtools

View File

@@ -1,205 +0,0 @@
// Copyright (c) 2019 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 <string>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "source/opt/ir_context.h"
#include "test/opt/pass_fixture.h"
#include "test/opt/pass_utils.h"
namespace spvtools {
namespace opt {
namespace {
using ::testing::ContainerEq;
using CFGTest = PassTest<::testing::Test>;
TEST_F(CFGTest, ForEachBlockInPostOrderIf) {
const std::string test = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main"
OpName %main "main"
%bool = OpTypeBool
%true = OpConstantTrue %bool
%void = OpTypeVoid
%4 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%5 = OpConstant %uint 5
%main = OpFunction %void None %4
%8 = OpLabel
OpSelectionMerge %10 None
OpBranchConditional %true %9 %10
%9 = OpLabel
OpBranch %10
%10 = OpLabel
OpReturn
OpFunctionEnd
)";
std::unique_ptr<IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, test,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
ASSERT_NE(nullptr, context);
CFG* cfg = context->cfg();
Module* module = context->module();
Function* function = &*module->begin();
std::vector<uint32_t> order;
cfg->ForEachBlockInPostOrder(&*function->begin(), [&order](BasicBlock* bb) {
order.push_back(bb->id());
});
std::vector<uint32_t> expected_result = {10, 9, 8};
EXPECT_THAT(order, ContainerEq(expected_result));
}
TEST_F(CFGTest, ForEachBlockInPostOrderLoop) {
const std::string test = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main"
OpName %main "main"
%bool = OpTypeBool
%true = OpConstantTrue %bool
%void = OpTypeVoid
%4 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%5 = OpConstant %uint 5
%main = OpFunction %void None %4
%8 = OpLabel
OpBranch %9
%9 = OpLabel
OpLoopMerge %11 %10 None
OpBranchConditional %true %11 %10
%10 = OpLabel
OpBranch %9
%11 = OpLabel
OpReturn
OpFunctionEnd
)";
std::unique_ptr<IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, test,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
ASSERT_NE(nullptr, context);
CFG* cfg = context->cfg();
Module* module = context->module();
Function* function = &*module->begin();
std::vector<uint32_t> order;
cfg->ForEachBlockInPostOrder(&*function->begin(), [&order](BasicBlock* bb) {
order.push_back(bb->id());
});
std::vector<uint32_t> expected_result1 = {10, 11, 9, 8};
std::vector<uint32_t> expected_result2 = {11, 10, 9, 8};
EXPECT_THAT(order, AnyOf(ContainerEq(expected_result1),
ContainerEq(expected_result2)));
}
TEST_F(CFGTest, ForEachBlockInReversePostOrderIf) {
const std::string test = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main"
OpName %main "main"
%bool = OpTypeBool
%true = OpConstantTrue %bool
%void = OpTypeVoid
%4 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%5 = OpConstant %uint 5
%main = OpFunction %void None %4
%8 = OpLabel
OpSelectionMerge %10 None
OpBranchConditional %true %9 %10
%9 = OpLabel
OpBranch %10
%10 = OpLabel
OpReturn
OpFunctionEnd
)";
std::unique_ptr<IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, test,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
ASSERT_NE(nullptr, context);
CFG* cfg = context->cfg();
Module* module = context->module();
Function* function = &*module->begin();
std::vector<uint32_t> order;
cfg->ForEachBlockInReversePostOrder(
&*function->begin(),
[&order](BasicBlock* bb) { order.push_back(bb->id()); });
std::vector<uint32_t> expected_result = {8, 9, 10};
EXPECT_THAT(order, ContainerEq(expected_result));
}
TEST_F(CFGTest, ForEachBlockInReversePostOrderLoop) {
const std::string test = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main"
OpName %main "main"
%bool = OpTypeBool
%true = OpConstantTrue %bool
%void = OpTypeVoid
%4 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%5 = OpConstant %uint 5
%main = OpFunction %void None %4
%8 = OpLabel
OpBranch %9
%9 = OpLabel
OpLoopMerge %11 %10 None
OpBranchConditional %true %11 %10
%10 = OpLabel
OpBranch %9
%11 = OpLabel
OpReturn
OpFunctionEnd
)";
std::unique_ptr<IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, test,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
ASSERT_NE(nullptr, context);
CFG* cfg = context->cfg();
Module* module = context->module();
Function* function = &*module->begin();
std::vector<uint32_t> order;
cfg->ForEachBlockInReversePostOrder(
&*function->begin(),
[&order](BasicBlock* bb) { order.push_back(bb->id()); });
std::vector<uint32_t> expected_result1 = {8, 9, 10, 11};
std::vector<uint32_t> expected_result2 = {8, 9, 11, 10};
EXPECT_THAT(order, AnyOf(ContainerEq(expected_result1),
ContainerEq(expected_result2)));
}
} // namespace
} // namespace opt
} // namespace spvtools

Some files were not shown because too many files have changed in this diff Show More