Updated spirv-tools.
This commit is contained in:
parent
7cf5eb0eba
commit
ecd5959987
6
3rdparty/spirv-tools/CHANGES
vendored
6
3rdparty/spirv-tools/CHANGES
vendored
@ -39,7 +39,7 @@ v2020.4 2020-07-22
|
||||
|
||||
v2020.3 2020-05-27
|
||||
- General
|
||||
- Prevent Effcee install his things when build spirv-tools with testing enabled (#3256)
|
||||
- Prevent Effcee from installing things when building spirv-tools with testing enabled (#3256)
|
||||
- Update acorn version (#3294)
|
||||
- If SPIRV-Headers is in our tree, include it as subproject (#3299)
|
||||
- allow cross compiling for Windows Store, UWP, etc. (#3330)
|
||||
@ -111,7 +111,7 @@ v2020.1 2020-02-03
|
||||
- Optimizer
|
||||
- Change default version for CreatInstBindlessCheckPass to 2 (#3096, #3119)
|
||||
- Better handling of OpLine on merge blocks (#3130)
|
||||
- Use dummy switch instead of dummy loop in MergeReturn pass. (#3151)
|
||||
- Use placeholder switch instead of placeholder loop in MergeReturn pass. (#3151)
|
||||
- Handle TimeAMD in AmdExtensionToKhrPass. (#3168)
|
||||
- Validator
|
||||
- Fix structured exit validation (#3141)
|
||||
@ -438,7 +438,7 @@ v2018.6 2018-11-07
|
||||
- Optimizer
|
||||
- Unrolling loops marked for unrolling in the legalization passes.
|
||||
- Improved the compile time of loop unrolling.
|
||||
- Changee merge-return to create a dummy loop around the function.
|
||||
- Changee merge-return to create a placeholder loop around the function.
|
||||
- Small improvement to merge-blocks to allow it to merge more often.
|
||||
- Enforce an upper bound for the ids, and add option to set it.
|
||||
- #1966: Report error if there are unreachable block before running merge return
|
||||
|
@ -1 +1 @@
|
||||
"v2020.5", "SPIRV-Tools v2020.5 aebdae4b9c5b3384041b88803e83fb244f5c8067"
|
||||
"v2020.5", "SPIRV-Tools v2020.5 276598ad50d33f1d1a56311520b17390a6bed635"
|
||||
|
28
3rdparty/spirv-tools/include/generated/nonsemantic.clspvreflection.insts.inc
vendored
Normal file
28
3rdparty/spirv-tools/include/generated/nonsemantic.clspvreflection.insts.inc
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
|
||||
|
||||
static const spv_ext_inst_desc_t nonsemantic_clspvreflection_entries[] = {
|
||||
{"Kernel", 1, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
|
||||
{"ArgumentInfo", 2, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}},
|
||||
{"ArgumentStorageBuffer", 3, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}},
|
||||
{"ArgumentUniform", 4, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}},
|
||||
{"ArgumentPodStorageBuffer", 5, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}},
|
||||
{"ArgumentPodUniform", 6, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}},
|
||||
{"ArgumentPodPushConstant", 7, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}},
|
||||
{"ArgumentSampledImage", 8, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}},
|
||||
{"ArgumentStorageImage", 9, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}},
|
||||
{"ArgumentSampler", 10, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}},
|
||||
{"ArgumentWorkgroup", 11, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}},
|
||||
{"SpecConstantWorkgroupSize", 12, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
|
||||
{"SpecConstantGlobalOffset", 13, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
|
||||
{"SpecConstantWorkDim", 14, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
|
||||
{"PushConstantGlobalOffset", 15, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
|
||||
{"PushConstantEnqueuedLocalSize", 16, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
|
||||
{"PushConstantGlobalSize", 17, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
|
||||
{"PushConstantRegionOffset", 18, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
|
||||
{"PushConstantNumWorkgroups", 19, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
|
||||
{"PushConstantRegionGroupOffset", 20, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
|
||||
{"ConstantDataStorageBuffer", 21, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
|
||||
{"ConstantDataUniform", 22, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
|
||||
{"LiteralSampler", 23, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
|
||||
{"PropertyRequiredWorkgroupSize", 24, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}}
|
||||
};
|
@ -48,8 +48,8 @@ extern "C" {
|
||||
|
||||
#define SPV_BIT(shift) (1 << (shift))
|
||||
|
||||
#define SPV_FORCE_16_BIT_ENUM(name) _##name = 0x7fff
|
||||
#define SPV_FORCE_32_BIT_ENUM(name) _##name = 0x7fffffff
|
||||
#define SPV_FORCE_16_BIT_ENUM(name) SPV_FORCE_16BIT_##name = 0x7fff
|
||||
#define SPV_FORCE_32_BIT_ENUM(name) SPV_FORCE_32BIT_##name = 0x7fffffff
|
||||
|
||||
// Enumerations
|
||||
|
||||
@ -189,8 +189,17 @@ typedef enum spv_operand_type_t {
|
||||
// Variable : expands to 0, 1 or many operands or pairs of operands.
|
||||
// This is similar to * in regular expressions.
|
||||
|
||||
// NOTE: These FIRST_* and LAST_* enum values are DEPRECATED.
|
||||
// The concept of "optional" and "variable" operand types are only intended
|
||||
// for use as an implementation detail of parsing SPIR-V, either in text or
|
||||
// binary form. Instead of using enum ranges, use characteristic function
|
||||
// spvOperandIsConcrete.
|
||||
// The use of enum value ranges in a public API makes it difficult to insert
|
||||
// new values into a range without also breaking binary compatibility.
|
||||
//
|
||||
// Macros for defining bounds on optional and variable operand types.
|
||||
// Any variable operand type is also optional.
|
||||
// TODO(dneto): Remove SPV_OPERAND_TYPE_FIRST_* and SPV_OPERAND_TYPE_LAST_*
|
||||
#define FIRST_OPTIONAL(ENUM) ENUM, SPV_OPERAND_TYPE_FIRST_OPTIONAL_TYPE = ENUM
|
||||
#define FIRST_VARIABLE(ENUM) ENUM, SPV_OPERAND_TYPE_FIRST_VARIABLE_TYPE = ENUM
|
||||
#define LAST_VARIABLE(ENUM) \
|
||||
@ -258,6 +267,12 @@ typedef enum spv_operand_type_t {
|
||||
SPV_FORCE_32_BIT_ENUM(spv_operand_type_t)
|
||||
} spv_operand_type_t;
|
||||
|
||||
// Returns true if the given type is concrete.
|
||||
bool spvOperandIsConcrete(spv_operand_type_t type);
|
||||
|
||||
// Returns true if the given type is concrete and also a mask.
|
||||
bool spvOperandIsConcreteMask(spv_operand_type_t type);
|
||||
|
||||
typedef enum spv_ext_inst_type_t {
|
||||
SPV_EXT_INST_TYPE_NONE = 0,
|
||||
SPV_EXT_INST_TYPE_GLSL_STD_450,
|
||||
@ -268,6 +283,7 @@ typedef enum spv_ext_inst_type_t {
|
||||
SPV_EXT_INST_TYPE_SPV_AMD_SHADER_BALLOT,
|
||||
SPV_EXT_INST_TYPE_DEBUGINFO,
|
||||
SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100,
|
||||
SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION,
|
||||
|
||||
// Multiple distinct extended instruction set types could return this
|
||||
// value, if they are prefixed with NonSemantic. and are otherwise
|
||||
|
34
3rdparty/spirv-tools/source/CMakeLists.txt
vendored
34
3rdparty/spirv-tools/source/CMakeLists.txt
vendored
@ -19,8 +19,8 @@ set(LANG_HEADER_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_lang
|
||||
|
||||
# For now, assume the DebugInfo grammar file is in the current directory.
|
||||
# It might migrate to SPIRV-Headers.
|
||||
set(DEBUGINFO_GRAMMAR_JSON_FILE "${CMAKE_CURRENT_SOURCE_DIR}/extinst.debuginfo.grammar.json")
|
||||
set(CLDEBUGINFO100_GRAMMAR_JSON_FILE "${CMAKE_CURRENT_SOURCE_DIR}/extinst.opencl.debuginfo.100.grammar.json")
|
||||
set(DEBUGINFO_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1/extinst.debuginfo.grammar.json")
|
||||
set(CLDEBUGINFO100_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json")
|
||||
|
||||
# macro() definitions are used in the following because we need to append .inc
|
||||
# file paths into some global lists (*_CPP_DEPENDS). And those global lists are
|
||||
@ -112,7 +112,7 @@ endmacro(spvtools_opencl_tables)
|
||||
|
||||
macro(spvtools_vendor_tables VENDOR_TABLE SHORT_NAME OPERAND_KIND_PREFIX)
|
||||
set(INSTS_FILE "${spirv-tools_BINARY_DIR}/${VENDOR_TABLE}.insts.inc")
|
||||
set(GRAMMAR_FILE "${spirv-tools_SOURCE_DIR}/source/extinst.${VENDOR_TABLE}.grammar.json")
|
||||
set(GRAMMAR_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1/extinst.${VENDOR_TABLE}.grammar.json")
|
||||
add_custom_command(OUTPUT ${INSTS_FILE}
|
||||
COMMAND ${PYTHON_EXECUTABLE} ${GRAMMAR_PROCESSING_SCRIPT}
|
||||
--extinst-vendor-grammar=${GRAMMAR_FILE}
|
||||
@ -148,6 +148,7 @@ spvtools_vendor_tables("spv-amd-gcn-shader" "spv-amd-gs" "")
|
||||
spvtools_vendor_tables("spv-amd-shader-ballot" "spv-amd-sb" "")
|
||||
spvtools_vendor_tables("debuginfo" "debuginfo" "")
|
||||
spvtools_vendor_tables("opencl.debuginfo.100" "cldi100" "CLDEBUG100_")
|
||||
spvtools_vendor_tables("nonsemantic.clspvreflection" "clspvreflection" "")
|
||||
spvtools_extinst_lang_headers("DebugInfo" ${DEBUGINFO_GRAMMAR_JSON_FILE})
|
||||
spvtools_extinst_lang_headers("OpenCLDebugInfo100" ${CLDEBUGINFO100_GRAMMAR_JSON_FILE})
|
||||
|
||||
@ -345,18 +346,21 @@ set_source_files_properties(
|
||||
|
||||
spvtools_pch(SPIRV_SOURCES pch_source)
|
||||
|
||||
add_library(${SPIRV_TOOLS} ${SPIRV_SOURCES})
|
||||
spvtools_default_compile_options(${SPIRV_TOOLS})
|
||||
target_include_directories(${SPIRV_TOOLS}
|
||||
add_library(${SPIRV_TOOLS}-static STATIC ${SPIRV_SOURCES})
|
||||
spvtools_default_compile_options(${SPIRV_TOOLS}-static)
|
||||
target_include_directories(${SPIRV_TOOLS}-static
|
||||
PUBLIC
|
||||
$<BUILD_INTERFACE:${spirv-tools_SOURCE_DIR}/include>
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
||||
PRIVATE ${spirv-tools_BINARY_DIR}
|
||||
PRIVATE ${SPIRV_HEADER_INCLUDE_DIR}
|
||||
)
|
||||
set_property(TARGET ${SPIRV_TOOLS} PROPERTY FOLDER "SPIRV-Tools libraries")
|
||||
spvtools_check_symbol_exports(${SPIRV_TOOLS})
|
||||
add_dependencies( ${SPIRV_TOOLS} core_tables enum_string_mapping extinst_tables )
|
||||
set_property(TARGET ${SPIRV_TOOLS}-static PROPERTY FOLDER "SPIRV-Tools libraries")
|
||||
spvtools_check_symbol_exports(${SPIRV_TOOLS}-static)
|
||||
add_dependencies(${SPIRV_TOOLS}-static core_tables enum_string_mapping extinst_tables)
|
||||
|
||||
# The static target does not have the '-static' suffix.
|
||||
set_target_properties(${SPIRV_TOOLS}-static PROPERTIES OUTPUT_NAME "${SPIRV_TOOLS}")
|
||||
|
||||
add_library(${SPIRV_TOOLS}-shared SHARED ${SPIRV_SOURCES})
|
||||
spvtools_default_compile_options(${SPIRV_TOOLS}-shared)
|
||||
@ -376,16 +380,24 @@ target_compile_definitions(${SPIRV_TOOLS}-shared
|
||||
)
|
||||
add_dependencies(${SPIRV_TOOLS}-shared core_tables enum_string_mapping extinst_tables)
|
||||
|
||||
# Create the "${SPIRV_TOOLS}" target as an alias to either "${SPIRV_TOOLS}-static"
|
||||
# or "${SPIRV_TOOLS}-shared" depending on the value of BUILD_SHARED_LIBS.
|
||||
if(BUILD_SHARED_LIBS)
|
||||
add_library(${SPIRV_TOOLS} ALIAS ${SPIRV_TOOLS}-shared)
|
||||
else()
|
||||
add_library(${SPIRV_TOOLS} ALIAS ${SPIRV_TOOLS}-static)
|
||||
endif()
|
||||
|
||||
if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
|
||||
find_library(LIBRT rt)
|
||||
if(LIBRT)
|
||||
target_link_libraries(${SPIRV_TOOLS} ${LIBRT})
|
||||
target_link_libraries(${SPIRV_TOOLS}-static ${LIBRT})
|
||||
target_link_libraries(${SPIRV_TOOLS}-shared ${LIBRT})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_SPIRV_TOOLS_INSTALL)
|
||||
install(TARGETS ${SPIRV_TOOLS} ${SPIRV_TOOLS}-shared EXPORT ${SPIRV_TOOLS}Targets
|
||||
install(TARGETS ${SPIRV_TOOLS}-static ${SPIRV_TOOLS}-shared EXPORT ${SPIRV_TOOLS}Targets
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
|
10
3rdparty/spirv-tools/source/ext_inst.cpp
vendored
10
3rdparty/spirv-tools/source/ext_inst.cpp
vendored
@ -28,6 +28,7 @@
|
||||
|
||||
#include "debuginfo.insts.inc"
|
||||
#include "glsl.std.450.insts.inc"
|
||||
#include "nonsemantic.clspvreflection.insts.inc"
|
||||
#include "opencl.debuginfo.100.insts.inc"
|
||||
#include "opencl.std.insts.inc"
|
||||
|
||||
@ -54,6 +55,9 @@ static const spv_ext_inst_group_t kGroups_1_0[] = {
|
||||
debuginfo_entries},
|
||||
{SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100,
|
||||
ARRAY_SIZE(opencl_debuginfo_100_entries), opencl_debuginfo_100_entries},
|
||||
{SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION,
|
||||
ARRAY_SIZE(nonsemantic_clspvreflection_entries),
|
||||
nonsemantic_clspvreflection_entries},
|
||||
};
|
||||
|
||||
static const spv_ext_inst_table_t kTable_1_0 = {ARRAY_SIZE(kGroups_1_0),
|
||||
@ -123,6 +127,9 @@ spv_ext_inst_type_t spvExtInstImportTypeGet(const char* name) {
|
||||
if (!strcmp("OpenCL.DebugInfo.100", name)) {
|
||||
return SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100;
|
||||
}
|
||||
if (!strncmp("NonSemantic.ClspvReflection.", name, 28)) {
|
||||
return SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION;
|
||||
}
|
||||
// ensure to add any known non-semantic extended instruction sets
|
||||
// above this point, and update spvExtInstIsNonSemantic()
|
||||
if (!strncmp("NonSemantic.", name, 12)) {
|
||||
@ -132,7 +139,8 @@ spv_ext_inst_type_t spvExtInstImportTypeGet(const char* name) {
|
||||
}
|
||||
|
||||
bool spvExtInstIsNonSemantic(const spv_ext_inst_type_t type) {
|
||||
if (type == SPV_EXT_INST_TYPE_NONSEMANTIC_UNKNOWN) {
|
||||
if (type == SPV_EXT_INST_TYPE_NONSEMANTIC_UNKNOWN ||
|
||||
type == SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -1,568 +0,0 @@
|
||||
{
|
||||
"copyright" : [
|
||||
"Copyright (c) 2017 The Khronos Group Inc.",
|
||||
"",
|
||||
"Permission is hereby granted, free of charge, to any person obtaining a copy",
|
||||
"of this software and/or associated documentation files (the \"Materials\"),",
|
||||
"to deal in the Materials without restriction, including without limitation",
|
||||
"the rights to use, copy, modify, merge, publish, distribute, sublicense,",
|
||||
"and/or sell copies of the Materials, and to permit persons to whom the",
|
||||
"Materials are furnished to do so, subject to the following conditions:",
|
||||
"",
|
||||
"The above copyright notice and this permission notice shall be included in",
|
||||
"all copies or substantial portions of the Materials.",
|
||||
"",
|
||||
"MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS",
|
||||
"STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND",
|
||||
"HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ",
|
||||
"",
|
||||
"THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS",
|
||||
"OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,",
|
||||
"FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL",
|
||||
"THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER",
|
||||
"LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING",
|
||||
"FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS",
|
||||
"IN THE MATERIALS."
|
||||
],
|
||||
"version" : 100,
|
||||
"revision" : 1,
|
||||
"instructions" : [
|
||||
{
|
||||
"opname" : "DebugInfoNone",
|
||||
"opcode" : 0
|
||||
},
|
||||
{
|
||||
"opname" : "DebugCompilationUnit",
|
||||
"opcode" : 1,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Source'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Version'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'DWARF Version'" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugTypeBasic",
|
||||
"opcode" : 2,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Name'" },
|
||||
{ "kind" : "IdRef", "name" : "'Size'" },
|
||||
{ "kind" : "DebugBaseTypeAttributeEncoding", "name" : "'Encoding'" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugTypePointer",
|
||||
"opcode" : 3,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Base Type'" },
|
||||
{ "kind" : "StorageClass", "name" : "'Storage Class'" },
|
||||
{ "kind" : "DebugInfoFlags", "name" : "'Literal Flags'" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugTypeQualifier",
|
||||
"opcode" : 4,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Base Type'" },
|
||||
{ "kind" : "DebugTypeQualifier", "name" : "'Type Qualifier'" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugTypeArray",
|
||||
"opcode" : 5,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Base Type'" },
|
||||
{ "kind" : "IdRef", "name" : "'Component Counts'", "quantifier" : "*" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugTypeVector",
|
||||
"opcode" : 6,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Base Type'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Component Count'" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugTypedef",
|
||||
"opcode" : 7,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Name'" },
|
||||
{ "kind" : "IdRef", "name" : "'Base Type'" },
|
||||
{ "kind" : "IdRef", "name" : "'Source'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Line'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Column'" },
|
||||
{ "kind" : "IdRef", "name" : "'Parent'" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugTypeFunction",
|
||||
"opcode" : 8,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Return Type'" },
|
||||
{ "kind" : "IdRef", "name" : "'Paramter Types'", "quantifier" : "*" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugTypeEnum",
|
||||
"opcode" : 9,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Name'" },
|
||||
{ "kind" : "IdRef", "name" : "'Underlying Type'" },
|
||||
{ "kind" : "IdRef", "name" : "'Source'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Line'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Column'" },
|
||||
{ "kind" : "IdRef", "name" : "'Parent'" },
|
||||
{ "kind" : "IdRef", "name" : "'Size'" },
|
||||
{ "kind" : "DebugInfoFlags", "name" : "'Flags'" },
|
||||
{ "kind" : "PairIdRefIdRef", "name" : "'Value, Name, Value, Name, ...'", "quantifier" : "*" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugTypeComposite",
|
||||
"opcode" : 10,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Name'" },
|
||||
{ "kind" : "DebugCompositeType", "name" : "'Tag'" },
|
||||
{ "kind" : "IdRef", "name" : "'Source'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Line'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Column'" },
|
||||
{ "kind" : "IdRef", "name" : "'Parent'" },
|
||||
{ "kind" : "IdRef", "name" : "'Size'" },
|
||||
{ "kind" : "DebugInfoFlags", "name" : "'Flags'" },
|
||||
{ "kind" : "IdRef", "name" : "'Members'", "quantifier" : "*" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugTypeMember",
|
||||
"opcode" : 11,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Name'" },
|
||||
{ "kind" : "IdRef", "name" : "'Type'" },
|
||||
{ "kind" : "IdRef", "name" : "'Source'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Line'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Column'" },
|
||||
{ "kind" : "IdRef", "name" : "'Parent'" },
|
||||
{ "kind" : "IdRef", "name" : "'Offset'" },
|
||||
{ "kind" : "IdRef", "name" : "'Size'" },
|
||||
{ "kind" : "DebugInfoFlags", "name" : "'Flags'" },
|
||||
{ "kind" : "IdRef", "name" : "'Value'", "quantifier" : "?" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugTypeInheritance",
|
||||
"opcode" : 12,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Child'" },
|
||||
{ "kind" : "IdRef", "name" : "'Parent'" },
|
||||
{ "kind" : "IdRef", "name" : "'Offset'" },
|
||||
{ "kind" : "IdRef", "name" : "'Size'" },
|
||||
{ "kind" : "DebugInfoFlags", "name" : "'Flags'" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugTypePtrToMember",
|
||||
"opcode" : 13,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Member Type'" },
|
||||
{ "kind" : "IdRef", "name" : "'Parent'" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugTypeTemplate",
|
||||
"opcode" : 14,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Target'" },
|
||||
{ "kind" : "IdRef", "name" : "'Parameters'", "quantifier" : "*" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugTypeTemplateParameter",
|
||||
"opcode" : 15,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Name'" },
|
||||
{ "kind" : "IdRef", "name" : "'Actual Type'" },
|
||||
{ "kind" : "IdRef", "name" : "'Value'" },
|
||||
{ "kind" : "IdRef", "name" : "'Source'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Line'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Column'" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugTypeTemplateTemplateParameter",
|
||||
"opcode" : 16,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Name'" },
|
||||
{ "kind" : "IdRef", "name" : "'Template Name'" },
|
||||
{ "kind" : "IdRef", "name" : "'Source'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Line'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Column'" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugTypeTemplateParameterPack",
|
||||
"opcode" : 17,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Name'" },
|
||||
{ "kind" : "IdRef", "name" : "'Source'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Line'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Column'" },
|
||||
{ "kind" : "IdRef", "name" : "'Template Parameters'", "quantifier" : "*" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugGlobalVariable",
|
||||
"opcode" : 18,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Name'" },
|
||||
{ "kind" : "IdRef", "name" : "'Type'" },
|
||||
{ "kind" : "IdRef", "name" : "'Source'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Line'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Column'" },
|
||||
{ "kind" : "IdRef", "name" : "'Parent'" },
|
||||
{ "kind" : "IdRef", "name" : "'Linkage Name'" },
|
||||
{ "kind" : "IdRef", "name" : "'Variable'" },
|
||||
{ "kind" : "DebugInfoFlags", "name" : "'Flags'" },
|
||||
{ "kind" : "IdRef", "name" : "'Static Member Declaration'", "quantifier" : "?" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugFunctionDeclaration",
|
||||
"opcode" : 19,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Name'" },
|
||||
{ "kind" : "IdRef", "name" : "'Type'" },
|
||||
{ "kind" : "IdRef", "name" : "'Source'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Line'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Column'" },
|
||||
{ "kind" : "IdRef", "name" : "'Parent'" },
|
||||
{ "kind" : "IdRef", "name" : "'Linkage Name'" },
|
||||
{ "kind" : "DebugInfoFlags", "name" : "'Flags'" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugFunction",
|
||||
"opcode" : 20,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Name'" },
|
||||
{ "kind" : "IdRef", "name" : "'Type'" },
|
||||
{ "kind" : "IdRef", "name" : "'Source'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Line'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Column'" },
|
||||
{ "kind" : "IdRef", "name" : "'Parent'" },
|
||||
{ "kind" : "IdRef", "name" : "'Linkage Name'" },
|
||||
{ "kind" : "DebugInfoFlags", "name" : "'Flags'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Scope Line'" },
|
||||
{ "kind" : "IdRef", "name" : "'Function'" },
|
||||
{ "kind" : "IdRef", "name" : "'Declaration'", "quantifier" : "?" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugLexicalBlock",
|
||||
"opcode" : 21,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Source'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Line'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Column'" },
|
||||
{ "kind" : "IdRef", "name" : "'Parent'" },
|
||||
{ "kind" : "IdRef", "name" : "'Name'", "quantifier" : "?" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugLexicalBlockDiscriminator",
|
||||
"opcode" : 22,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Scope'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Discriminator'" },
|
||||
{ "kind" : "IdRef", "name" : "'Parent'" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugScope",
|
||||
"opcode" : 23,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Scope'" },
|
||||
{ "kind" : "IdRef", "name" : "'Inlined At'", "quantifier" : "?" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugNoScope",
|
||||
"opcode" : 24
|
||||
},
|
||||
{
|
||||
"opname" : "DebugInlinedAt",
|
||||
"opcode" : 25,
|
||||
"operands" : [
|
||||
{ "kind" : "LiteralInteger", "name" : "'Line'" },
|
||||
{ "kind" : "IdRef", "name" : "'Scope'" },
|
||||
{ "kind" : "IdRef", "name" : "'Inlined'", "quantifier" : "?" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugLocalVariable",
|
||||
"opcode" : 26,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Name'" },
|
||||
{ "kind" : "IdRef", "name" : "'Type'" },
|
||||
{ "kind" : "IdRef", "name" : "'Source'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Line'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Column'" },
|
||||
{ "kind" : "IdRef", "name" : "'Parent'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Arg Number'", "quantifier" : "?" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugInlinedVariable",
|
||||
"opcode" : 27,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Variable'" },
|
||||
{ "kind" : "IdRef", "name" : "'Inlined'" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugDeclare",
|
||||
"opcode" : 28,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Local Variable'" },
|
||||
{ "kind" : "IdRef", "name" : "'Variable'" },
|
||||
{ "kind" : "IdRef", "name" : "'Expression'" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugValue",
|
||||
"opcode" : 29,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Value'" },
|
||||
{ "kind" : "IdRef", "name" : "'Expression'" },
|
||||
{ "kind" : "IdRef", "name" : "'Indexes'", "quantifier" : "*" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugOperation",
|
||||
"opcode" : 30,
|
||||
"operands" : [
|
||||
{ "kind" : "DebugOperation", "name" : "'OpCode'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Operands ...'", "quantifier" : "*" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugExpression",
|
||||
"opcode" : 31,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Operands ...'", "quantifier" : "*" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugMacroDef",
|
||||
"opcode" : 32,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Source'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Line'" },
|
||||
{ "kind" : "IdRef", "name" : "'Name'" },
|
||||
{ "kind" : "IdRef", "name" : "'Value'", "quantifier" : "?" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugMacroUndef",
|
||||
"opcode" : 33,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Source'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Line'" },
|
||||
{ "kind" : "IdRef", "name" : "'Macro'" }
|
||||
]
|
||||
}
|
||||
],
|
||||
"operand_kinds" : [
|
||||
{
|
||||
"category" : "BitEnum",
|
||||
"kind" : "DebugInfoFlags",
|
||||
"enumerants" : [
|
||||
{
|
||||
"enumerant" : "FlagIsProtected",
|
||||
"value" : "0x01"
|
||||
},
|
||||
{
|
||||
"enumerant" : "FlagIsPrivate",
|
||||
"value" : "0x02"
|
||||
},
|
||||
{
|
||||
"enumerant" : "FlagIsPublic",
|
||||
"value" : "0x03"
|
||||
},
|
||||
{
|
||||
"enumerant" : "FlagIsLocal",
|
||||
"value" : "0x04"
|
||||
},
|
||||
{
|
||||
"enumerant" : "FlagIsDefinition",
|
||||
"value" : "0x08"
|
||||
},
|
||||
{
|
||||
"enumerant" : "FlagFwdDecl",
|
||||
"value" : "0x10"
|
||||
},
|
||||
{
|
||||
"enumerant" : "FlagArtificial",
|
||||
"value" : "0x20"
|
||||
},
|
||||
{
|
||||
"enumerant" : "FlagExplicit",
|
||||
"value" : "0x40"
|
||||
},
|
||||
{
|
||||
"enumerant" : "FlagPrototyped",
|
||||
"value" : "0x80"
|
||||
},
|
||||
{
|
||||
"enumerant" : "FlagObjectPointer",
|
||||
"value" : "0x100"
|
||||
},
|
||||
{
|
||||
"enumerant" : "FlagStaticMember",
|
||||
"value" : "0x200"
|
||||
},
|
||||
{
|
||||
"enumerant" : "FlagIndirectVariable",
|
||||
"value" : "0x400"
|
||||
},
|
||||
{
|
||||
"enumerant" : "FlagLValueReference",
|
||||
"value" : "0x800"
|
||||
},
|
||||
{
|
||||
"enumerant" : "FlagRValueReference",
|
||||
"value" : "0x1000"
|
||||
},
|
||||
{
|
||||
"enumerant" : "FlagIsOptimized",
|
||||
"value" : "0x2000"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"category" : "ValueEnum",
|
||||
"kind" : "DebugBaseTypeAttributeEncoding",
|
||||
"enumerants" : [
|
||||
{
|
||||
"enumerant" : "Unspecified",
|
||||
"value" : "0"
|
||||
},
|
||||
{
|
||||
"enumerant" : "Address",
|
||||
"value" : "1"
|
||||
},
|
||||
{
|
||||
"enumerant" : "Boolean",
|
||||
"value" : "2"
|
||||
},
|
||||
{
|
||||
"enumerant" : "Float",
|
||||
"value" : "4"
|
||||
},
|
||||
{
|
||||
"enumerant" : "Signed",
|
||||
"value" : "5"
|
||||
},
|
||||
{
|
||||
"enumerant" : "SignedChar",
|
||||
"value" : "6"
|
||||
},
|
||||
{
|
||||
"enumerant" : "Unsigned",
|
||||
"value" : "7"
|
||||
},
|
||||
{
|
||||
"enumerant" : "UnsignedChar",
|
||||
"value" : "8"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"category" : "ValueEnum",
|
||||
"kind" : "DebugCompositeType",
|
||||
"enumerants" : [
|
||||
{
|
||||
"enumerant" : "Class",
|
||||
"value" : "0"
|
||||
},
|
||||
{
|
||||
"enumerant" : "Structure",
|
||||
"value" : "1"
|
||||
},
|
||||
{
|
||||
"enumerant" : "Union",
|
||||
"value" : "2"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"category" : "ValueEnum",
|
||||
"kind" : "DebugTypeQualifier",
|
||||
"enumerants" : [
|
||||
{
|
||||
"enumerant" : "ConstType",
|
||||
"value" : "0"
|
||||
},
|
||||
{
|
||||
"enumerant" : "VolatileType",
|
||||
"value" : "1"
|
||||
},
|
||||
{
|
||||
"enumerant" : "RestrictType",
|
||||
"value" : "2"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"category" : "ValueEnum",
|
||||
"kind" : "DebugOperation",
|
||||
"enumerants" : [
|
||||
{
|
||||
"enumerant" : "Deref",
|
||||
"value" : "0"
|
||||
},
|
||||
{
|
||||
"enumerant" : "Plus",
|
||||
"value" : "1"
|
||||
},
|
||||
{
|
||||
"enumerant" : "Minus",
|
||||
"value" : "2"
|
||||
},
|
||||
{
|
||||
"enumerant" : "PlusUconst",
|
||||
"value" : "3",
|
||||
"parameters" : [
|
||||
{ "kind" : "LiteralInteger" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"enumerant" : "BitPiece",
|
||||
"value" : "4",
|
||||
"parameters" : [
|
||||
{ "kind" : "LiteralInteger" },
|
||||
{ "kind" : "LiteralInteger" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"enumerant" : "Swap",
|
||||
"value" : "5"
|
||||
},
|
||||
{
|
||||
"enumerant" : "Xderef",
|
||||
"value" : "6"
|
||||
},
|
||||
{
|
||||
"enumerant" : "StackValue",
|
||||
"value" : "7"
|
||||
},
|
||||
{
|
||||
"enumerant" : "Constu",
|
||||
"value" : "8",
|
||||
"parameters" : [
|
||||
{ "kind" : "LiteralInteger" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@ -1,632 +0,0 @@
|
||||
{
|
||||
"copyright" : [
|
||||
"Copyright (c) 2018 The Khronos Group Inc.",
|
||||
"",
|
||||
"Permission is hereby granted, free of charge, to any person obtaining a copy",
|
||||
"of this software and/or associated documentation files (the \"Materials\"),",
|
||||
"to deal in the Materials without restriction, including without limitation",
|
||||
"the rights to use, copy, modify, merge, publish, distribute, sublicense,",
|
||||
"and/or sell copies of the Materials, and to permit persons to whom the",
|
||||
"Materials are furnished to do so, subject to the following conditions:",
|
||||
"",
|
||||
"The above copyright notice and this permission notice shall be included in",
|
||||
"all copies or substantial portions of the Materials.",
|
||||
"",
|
||||
"MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS",
|
||||
"STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND",
|
||||
"HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ",
|
||||
"",
|
||||
"THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS",
|
||||
"OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,",
|
||||
"FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL",
|
||||
"THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER",
|
||||
"LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING",
|
||||
"FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS",
|
||||
"IN THE MATERIALS."
|
||||
],
|
||||
"version" : 200,
|
||||
"revision" : 2,
|
||||
"instructions" : [
|
||||
{
|
||||
"opname" : "DebugInfoNone",
|
||||
"opcode" : 0
|
||||
},
|
||||
{
|
||||
"opname" : "DebugCompilationUnit",
|
||||
"opcode" : 1,
|
||||
"operands" : [
|
||||
{ "kind" : "LiteralInteger", "name" : "'Version'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'DWARF Version'" },
|
||||
{ "kind" : "IdRef", "name" : "'Source'" },
|
||||
{ "kind" : "SourceLanguage", "name" : "'Language'" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugTypeBasic",
|
||||
"opcode" : 2,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Name'" },
|
||||
{ "kind" : "IdRef", "name" : "'Size'" },
|
||||
{ "kind" : "DebugBaseTypeAttributeEncoding", "name" : "'Encoding'" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugTypePointer",
|
||||
"opcode" : 3,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Base Type'" },
|
||||
{ "kind" : "StorageClass", "name" : "'Storage Class'" },
|
||||
{ "kind" : "DebugInfoFlags", "name" : "'Flags'" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugTypeQualifier",
|
||||
"opcode" : 4,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Base Type'" },
|
||||
{ "kind" : "DebugTypeQualifier", "name" : "'Type Qualifier'" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugTypeArray",
|
||||
"opcode" : 5,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Base Type'" },
|
||||
{ "kind" : "IdRef", "name" : "'Component Counts'", "quantifier" : "*" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugTypeVector",
|
||||
"opcode" : 6,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Base Type'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Component Count'" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugTypedef",
|
||||
"opcode" : 7,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Name'" },
|
||||
{ "kind" : "IdRef", "name" : "'Base Type'" },
|
||||
{ "kind" : "IdRef", "name" : "'Source'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Line'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Column'" },
|
||||
{ "kind" : "IdRef", "name" : "'Parent'" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugTypeFunction",
|
||||
"opcode" : 8,
|
||||
"operands" : [
|
||||
{ "kind" : "DebugInfoFlags", "name" : "'Flags'" },
|
||||
{ "kind" : "IdRef", "name" : "'Return Type'" },
|
||||
{ "kind" : "IdRef", "name" : "'Parameter Types'", "quantifier" : "*" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugTypeEnum",
|
||||
"opcode" : 9,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Name'" },
|
||||
{ "kind" : "IdRef", "name" : "'Underlying Type'" },
|
||||
{ "kind" : "IdRef", "name" : "'Source'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Line'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Column'" },
|
||||
{ "kind" : "IdRef", "name" : "'Parent'" },
|
||||
{ "kind" : "IdRef", "name" : "'Size'" },
|
||||
{ "kind" : "DebugInfoFlags", "name" : "'Flags'" },
|
||||
{ "kind" : "PairIdRefIdRef", "name" : "'Value, Name, Value, Name, ...'", "quantifier" : "*" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugTypeComposite",
|
||||
"opcode" : 10,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Name'" },
|
||||
{ "kind" : "DebugCompositeType", "name" : "'Tag'" },
|
||||
{ "kind" : "IdRef", "name" : "'Source'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Line'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Column'" },
|
||||
{ "kind" : "IdRef", "name" : "'Parent'" },
|
||||
{ "kind" : "IdRef", "name" : "'Linkage Name'" },
|
||||
{ "kind" : "IdRef", "name" : "'Size'" },
|
||||
{ "kind" : "DebugInfoFlags", "name" : "'Flags'" },
|
||||
{ "kind" : "IdRef", "name" : "'Members'", "quantifier" : "*" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugTypeMember",
|
||||
"opcode" : 11,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Name'" },
|
||||
{ "kind" : "IdRef", "name" : "'Type'" },
|
||||
{ "kind" : "IdRef", "name" : "'Source'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Line'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Column'" },
|
||||
{ "kind" : "IdRef", "name" : "'Parent'" },
|
||||
{ "kind" : "IdRef", "name" : "'Offset'" },
|
||||
{ "kind" : "IdRef", "name" : "'Size'" },
|
||||
{ "kind" : "DebugInfoFlags", "name" : "'Flags'" },
|
||||
{ "kind" : "IdRef", "name" : "'Value'", "quantifier" : "?" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugTypeInheritance",
|
||||
"opcode" : 12,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Child'" },
|
||||
{ "kind" : "IdRef", "name" : "'Parent'" },
|
||||
{ "kind" : "IdRef", "name" : "'Offset'" },
|
||||
{ "kind" : "IdRef", "name" : "'Size'" },
|
||||
{ "kind" : "DebugInfoFlags", "name" : "'Flags'" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugTypePtrToMember",
|
||||
"opcode" : 13,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Member Type'" },
|
||||
{ "kind" : "IdRef", "name" : "'Parent'" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugTypeTemplate",
|
||||
"opcode" : 14,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Target'" },
|
||||
{ "kind" : "IdRef", "name" : "'Parameters'", "quantifier" : "*" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugTypeTemplateParameter",
|
||||
"opcode" : 15,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Name'" },
|
||||
{ "kind" : "IdRef", "name" : "'Actual Type'" },
|
||||
{ "kind" : "IdRef", "name" : "'Value'" },
|
||||
{ "kind" : "IdRef", "name" : "'Source'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Line'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Column'" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugTypeTemplateTemplateParameter",
|
||||
"opcode" : 16,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Name'" },
|
||||
{ "kind" : "IdRef", "name" : "'Template Name'" },
|
||||
{ "kind" : "IdRef", "name" : "'Source'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Line'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Column'" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugTypeTemplateParameterPack",
|
||||
"opcode" : 17,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Name'" },
|
||||
{ "kind" : "IdRef", "name" : "'Source'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Line'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Column'" },
|
||||
{ "kind" : "IdRef", "name" : "'Template Parameters'", "quantifier" : "*" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugGlobalVariable",
|
||||
"opcode" : 18,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Name'" },
|
||||
{ "kind" : "IdRef", "name" : "'Type'" },
|
||||
{ "kind" : "IdRef", "name" : "'Source'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Line'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Column'" },
|
||||
{ "kind" : "IdRef", "name" : "'Parent'" },
|
||||
{ "kind" : "IdRef", "name" : "'Linkage Name'" },
|
||||
{ "kind" : "IdRef", "name" : "'Variable'" },
|
||||
{ "kind" : "DebugInfoFlags", "name" : "'Flags'" },
|
||||
{ "kind" : "IdRef", "name" : "'Static Member Declaration'", "quantifier" : "?" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugFunctionDeclaration",
|
||||
"opcode" : 19,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Name'" },
|
||||
{ "kind" : "IdRef", "name" : "'Type'" },
|
||||
{ "kind" : "IdRef", "name" : "'Source'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Line'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Column'" },
|
||||
{ "kind" : "IdRef", "name" : "'Parent'" },
|
||||
{ "kind" : "IdRef", "name" : "'Linkage Name'" },
|
||||
{ "kind" : "DebugInfoFlags", "name" : "'Flags'" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugFunction",
|
||||
"opcode" : 20,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Name'" },
|
||||
{ "kind" : "IdRef", "name" : "'Type'" },
|
||||
{ "kind" : "IdRef", "name" : "'Source'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Line'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Column'" },
|
||||
{ "kind" : "IdRef", "name" : "'Parent'" },
|
||||
{ "kind" : "IdRef", "name" : "'Linkage Name'" },
|
||||
{ "kind" : "DebugInfoFlags", "name" : "'Flags'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Scope Line'" },
|
||||
{ "kind" : "IdRef", "name" : "'Function'" },
|
||||
{ "kind" : "IdRef", "name" : "'Declaration'", "quantifier" : "?" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugLexicalBlock",
|
||||
"opcode" : 21,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Source'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Line'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Column'" },
|
||||
{ "kind" : "IdRef", "name" : "'Parent'" },
|
||||
{ "kind" : "IdRef", "name" : "'Name'", "quantifier" : "?" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugLexicalBlockDiscriminator",
|
||||
"opcode" : 22,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Source'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Discriminator'" },
|
||||
{ "kind" : "IdRef", "name" : "'Parent'" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugScope",
|
||||
"opcode" : 23,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Scope'" },
|
||||
{ "kind" : "IdRef", "name" : "'Inlined At'", "quantifier" : "?" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugNoScope",
|
||||
"opcode" : 24
|
||||
},
|
||||
{
|
||||
"opname" : "DebugInlinedAt",
|
||||
"opcode" : 25,
|
||||
"operands" : [
|
||||
{ "kind" : "LiteralInteger", "name" : "'Line'" },
|
||||
{ "kind" : "IdRef", "name" : "'Scope'" },
|
||||
{ "kind" : "IdRef", "name" : "'Inlined'", "quantifier" : "?" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugLocalVariable",
|
||||
"opcode" : 26,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Name'" },
|
||||
{ "kind" : "IdRef", "name" : "'Type'" },
|
||||
{ "kind" : "IdRef", "name" : "'Source'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Line'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Column'" },
|
||||
{ "kind" : "IdRef", "name" : "'Parent'" },
|
||||
{ "kind" : "DebugInfoFlags", "name" : "'Flags'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Arg Number'", "quantifier" : "?" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugInlinedVariable",
|
||||
"opcode" : 27,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Variable'" },
|
||||
{ "kind" : "IdRef", "name" : "'Inlined'" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugDeclare",
|
||||
"opcode" : 28,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Local Variable'" },
|
||||
{ "kind" : "IdRef", "name" : "'Variable'" },
|
||||
{ "kind" : "IdRef", "name" : "'Expression'" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugValue",
|
||||
"opcode" : 29,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Local Variable'" },
|
||||
{ "kind" : "IdRef", "name" : "'Value'" },
|
||||
{ "kind" : "IdRef", "name" : "'Expression'" },
|
||||
{ "kind" : "IdRef", "name" : "'Indexes'", "quantifier" : "*" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugOperation",
|
||||
"opcode" : 30,
|
||||
"operands" : [
|
||||
{ "kind" : "DebugOperation", "name" : "'OpCode'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Operands ...'", "quantifier" : "*" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugExpression",
|
||||
"opcode" : 31,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Operands ...'", "quantifier" : "*" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugMacroDef",
|
||||
"opcode" : 32,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Source'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Line'" },
|
||||
{ "kind" : "IdRef", "name" : "'Name'" },
|
||||
{ "kind" : "IdRef", "name" : "'Value'", "quantifier" : "?" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugMacroUndef",
|
||||
"opcode" : 33,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Source'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Line'" },
|
||||
{ "kind" : "IdRef", "name" : "'Macro'" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugImportedEntity",
|
||||
"opcode" : 34,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'Name'" },
|
||||
{ "kind" : "DebugImportedEntity", "name" : "'Tag'" },
|
||||
{ "kind" : "IdRef", "name" : "'Source'" },
|
||||
{ "kind" : "IdRef", "name" : "'Entity'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Line'" },
|
||||
{ "kind" : "LiteralInteger", "name" : "'Column'" },
|
||||
{ "kind" : "IdRef", "name" : "'Parent'" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"opname" : "DebugSource",
|
||||
"opcode" : 35,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'File'" },
|
||||
{ "kind" : "IdRef", "name" : "'Text'", "quantifier" : "?" }
|
||||
]
|
||||
}
|
||||
],
|
||||
"operand_kinds" : [
|
||||
{
|
||||
"category" : "BitEnum",
|
||||
"kind" : "DebugInfoFlags",
|
||||
"enumerants" : [
|
||||
{
|
||||
"enumerant" : "FlagIsProtected",
|
||||
"value" : "0x01"
|
||||
},
|
||||
{
|
||||
"enumerant" : "FlagIsPrivate",
|
||||
"value" : "0x02"
|
||||
},
|
||||
{
|
||||
"enumerant" : "FlagIsPublic",
|
||||
"value" : "0x03"
|
||||
},
|
||||
{
|
||||
"enumerant" : "FlagIsLocal",
|
||||
"value" : "0x04"
|
||||
},
|
||||
{
|
||||
"enumerant" : "FlagIsDefinition",
|
||||
"value" : "0x08"
|
||||
},
|
||||
{
|
||||
"enumerant" : "FlagFwdDecl",
|
||||
"value" : "0x10"
|
||||
},
|
||||
{
|
||||
"enumerant" : "FlagArtificial",
|
||||
"value" : "0x20"
|
||||
},
|
||||
{
|
||||
"enumerant" : "FlagExplicit",
|
||||
"value" : "0x40"
|
||||
},
|
||||
{
|
||||
"enumerant" : "FlagPrototyped",
|
||||
"value" : "0x80"
|
||||
},
|
||||
{
|
||||
"enumerant" : "FlagObjectPointer",
|
||||
"value" : "0x100"
|
||||
},
|
||||
{
|
||||
"enumerant" : "FlagStaticMember",
|
||||
"value" : "0x200"
|
||||
},
|
||||
{
|
||||
"enumerant" : "FlagIndirectVariable",
|
||||
"value" : "0x400"
|
||||
},
|
||||
{
|
||||
"enumerant" : "FlagLValueReference",
|
||||
"value" : "0x800"
|
||||
},
|
||||
{
|
||||
"enumerant" : "FlagRValueReference",
|
||||
"value" : "0x1000"
|
||||
},
|
||||
{
|
||||
"enumerant" : "FlagIsOptimized",
|
||||
"value" : "0x2000"
|
||||
},
|
||||
{
|
||||
"enumerant" : "FlagIsEnumClass",
|
||||
"value" : "0x4000"
|
||||
},
|
||||
{
|
||||
"enumerant" : "FlagTypePassByValue",
|
||||
"value" : "0x8000"
|
||||
},
|
||||
{
|
||||
"enumerant" : "FlagTypePassByReference",
|
||||
"value" : "0x10000"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"category" : "ValueEnum",
|
||||
"kind" : "DebugBaseTypeAttributeEncoding",
|
||||
"enumerants" : [
|
||||
{
|
||||
"enumerant" : "Unspecified",
|
||||
"value" : "0"
|
||||
},
|
||||
{
|
||||
"enumerant" : "Address",
|
||||
"value" : "1"
|
||||
},
|
||||
{
|
||||
"enumerant" : "Boolean",
|
||||
"value" : "2"
|
||||
},
|
||||
{
|
||||
"enumerant" : "Float",
|
||||
"value" : "3"
|
||||
},
|
||||
{
|
||||
"enumerant" : "Signed",
|
||||
"value" : "4"
|
||||
},
|
||||
{
|
||||
"enumerant" : "SignedChar",
|
||||
"value" : "5"
|
||||
},
|
||||
{
|
||||
"enumerant" : "Unsigned",
|
||||
"value" : "6"
|
||||
},
|
||||
{
|
||||
"enumerant" : "UnsignedChar",
|
||||
"value" : "7"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"category" : "ValueEnum",
|
||||
"kind" : "DebugCompositeType",
|
||||
"enumerants" : [
|
||||
{
|
||||
"enumerant" : "Class",
|
||||
"value" : "0"
|
||||
},
|
||||
{
|
||||
"enumerant" : "Structure",
|
||||
"value" : "1"
|
||||
},
|
||||
{
|
||||
"enumerant" : "Union",
|
||||
"value" : "2"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"category" : "ValueEnum",
|
||||
"kind" : "DebugTypeQualifier",
|
||||
"enumerants" : [
|
||||
{
|
||||
"enumerant" : "ConstType",
|
||||
"value" : "0"
|
||||
},
|
||||
{
|
||||
"enumerant" : "VolatileType",
|
||||
"value" : "1"
|
||||
},
|
||||
{
|
||||
"enumerant" : "RestrictType",
|
||||
"value" : "2"
|
||||
},
|
||||
{
|
||||
"enumerant" : "AtomicType",
|
||||
"value" : "3"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"category" : "ValueEnum",
|
||||
"kind" : "DebugOperation",
|
||||
"enumerants" : [
|
||||
{
|
||||
"enumerant" : "Deref",
|
||||
"value" : "0"
|
||||
},
|
||||
{
|
||||
"enumerant" : "Plus",
|
||||
"value" : "1"
|
||||
},
|
||||
{
|
||||
"enumerant" : "Minus",
|
||||
"value" : "2"
|
||||
},
|
||||
{
|
||||
"enumerant" : "PlusUconst",
|
||||
"value" : "3",
|
||||
"parameters" : [
|
||||
{ "kind" : "LiteralInteger" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"enumerant" : "BitPiece",
|
||||
"value" : "4",
|
||||
"parameters" : [
|
||||
{ "kind" : "LiteralInteger" },
|
||||
{ "kind" : "LiteralInteger" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"enumerant" : "Swap",
|
||||
"value" : "5"
|
||||
},
|
||||
{
|
||||
"enumerant" : "Xderef",
|
||||
"value" : "6"
|
||||
},
|
||||
{
|
||||
"enumerant" : "StackValue",
|
||||
"value" : "7"
|
||||
},
|
||||
{
|
||||
"enumerant" : "Constu",
|
||||
"value" : "8",
|
||||
"parameters" : [
|
||||
{ "kind" : "LiteralInteger" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"enumerant" : "Fragment",
|
||||
"value" : "9",
|
||||
"parameters" : [
|
||||
{ "kind" : "LiteralInteger" },
|
||||
{ "kind" : "LiteralInteger" }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"category" : "ValueEnum",
|
||||
"kind" : "DebugImportedEntity",
|
||||
"enumerants" : [
|
||||
{
|
||||
"enumerant" : "ImportedModule",
|
||||
"value" : "0"
|
||||
},
|
||||
{
|
||||
"enumerant" : "ImportedDeclaration",
|
||||
"value" : "1"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
{
|
||||
"revision" : 2,
|
||||
"instructions" : [
|
||||
{
|
||||
"opname" : "CubeFaceIndexAMD",
|
||||
"opcode" : 1,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'P'" }
|
||||
],
|
||||
"extensions" : [ "SPV_AMD_gcn_shader" ]
|
||||
},
|
||||
{
|
||||
"opname" : "CubeFaceCoordAMD",
|
||||
"opcode" : 2,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'P'" }
|
||||
],
|
||||
"extensions" : [ "SPV_AMD_gcn_shader" ]
|
||||
},
|
||||
{
|
||||
"opname" : "TimeAMD",
|
||||
"opcode" : 3,
|
||||
"extensions" : [ "SPV_AMD_gcn_shader" ]
|
||||
}
|
||||
]
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
{
|
||||
"revision" : 5,
|
||||
"instructions" : [
|
||||
{
|
||||
"opname" : "SwizzleInvocationsAMD",
|
||||
"opcode" : 1,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'data'" },
|
||||
{ "kind" : "IdRef", "name" : "'offset'" }
|
||||
],
|
||||
"extensions" : [ "SPV_AMD_shader_ballot" ]
|
||||
},
|
||||
{
|
||||
"opname" : "SwizzleInvocationsMaskedAMD",
|
||||
"opcode" : 2,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'data'" },
|
||||
{ "kind" : "IdRef", "name" : "'mask'" }
|
||||
],
|
||||
"extensions" : [ "SPV_AMD_shader_ballot" ]
|
||||
},
|
||||
{
|
||||
"opname" : "WriteInvocationAMD",
|
||||
"opcode" : 3,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'inputValue'" },
|
||||
{ "kind" : "IdRef", "name" : "'writeValue'" },
|
||||
{ "kind" : "IdRef", "name" : "'invocationIndex'" }
|
||||
],
|
||||
"extensions" : [ "SPV_AMD_shader_ballot" ]
|
||||
},
|
||||
{
|
||||
"opname" : "MbcntAMD",
|
||||
"opcode" : 4,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'mask'" }
|
||||
],
|
||||
"extensions" : [ "SPV_AMD_shader_ballot" ]
|
||||
}
|
||||
]
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
{
|
||||
"revision" : 4,
|
||||
"instructions" : [
|
||||
{
|
||||
"opname" : "InterpolateAtVertexAMD",
|
||||
"opcode" : 1,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'interpolant'" },
|
||||
{ "kind" : "IdRef", "name" : "'vertexIdx'" }
|
||||
],
|
||||
"extensions" : [ "SPV_AMD_shader_explicit_vertex_parameter" ]
|
||||
}
|
||||
]
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
{
|
||||
"revision" : 4,
|
||||
"instructions" : [
|
||||
{
|
||||
"opname" : "FMin3AMD",
|
||||
"opcode" : 1,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'x'" },
|
||||
{ "kind" : "IdRef", "name" : "'y'" },
|
||||
{ "kind" : "IdRef", "name" : "'z'" }
|
||||
],
|
||||
"extensions" : [ "SPV_AMD_shader_trinary_minmax" ]
|
||||
},
|
||||
{
|
||||
"opname" : "UMin3AMD",
|
||||
"opcode" : 2,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'x'" },
|
||||
{ "kind" : "IdRef", "name" : "'y'" },
|
||||
{ "kind" : "IdRef", "name" : "'z'" }
|
||||
],
|
||||
"extensions" : [ "SPV_AMD_shader_trinary_minmax" ]
|
||||
},
|
||||
{
|
||||
"opname" : "SMin3AMD",
|
||||
"opcode" : 3,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'x'" },
|
||||
{ "kind" : "IdRef", "name" : "'y'" },
|
||||
{ "kind" : "IdRef", "name" : "'z'" }
|
||||
],
|
||||
"extensions" : [ "SPV_AMD_shader_trinary_minmax" ]
|
||||
},
|
||||
{
|
||||
"opname" : "FMax3AMD",
|
||||
"opcode" : 4,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'x'" },
|
||||
{ "kind" : "IdRef", "name" : "'y'" },
|
||||
{ "kind" : "IdRef", "name" : "'z'" }
|
||||
],
|
||||
"extensions" : [ "SPV_AMD_shader_trinary_minmax" ]
|
||||
},
|
||||
{
|
||||
"opname" : "UMax3AMD",
|
||||
"opcode" : 5,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'x'" },
|
||||
{ "kind" : "IdRef", "name" : "'y'" },
|
||||
{ "kind" : "IdRef", "name" : "'z'" }
|
||||
],
|
||||
"extensions" : [ "SPV_AMD_shader_trinary_minmax" ]
|
||||
},
|
||||
{
|
||||
"opname" : "SMax3AMD",
|
||||
"opcode" : 6,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'x'" },
|
||||
{ "kind" : "IdRef", "name" : "'y'" },
|
||||
{ "kind" : "IdRef", "name" : "'z'" }
|
||||
],
|
||||
"extensions" : [ "SPV_AMD_shader_trinary_minmax" ]
|
||||
},
|
||||
{
|
||||
"opname" : "FMid3AMD",
|
||||
"opcode" : 7,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'x'" },
|
||||
{ "kind" : "IdRef", "name" : "'y'" },
|
||||
{ "kind" : "IdRef", "name" : "'z'" }
|
||||
],
|
||||
"extensions" : [ "SPV_AMD_shader_trinary_minmax" ]
|
||||
},
|
||||
{
|
||||
"opname" : "UMid3AMD",
|
||||
"opcode" : 8,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'x'" },
|
||||
{ "kind" : "IdRef", "name" : "'y'" },
|
||||
{ "kind" : "IdRef", "name" : "'z'" }
|
||||
],
|
||||
"extensions" : [ "SPV_AMD_shader_trinary_minmax" ]
|
||||
},
|
||||
{
|
||||
"opname" : "SMid3AMD",
|
||||
"opcode" : 9,
|
||||
"operands" : [
|
||||
{ "kind" : "IdRef", "name" : "'x'" },
|
||||
{ "kind" : "IdRef", "name" : "'y'" },
|
||||
{ "kind" : "IdRef", "name" : "'z'" }
|
||||
],
|
||||
"extensions" : [ "SPV_AMD_shader_trinary_minmax" ]
|
||||
}
|
||||
]
|
||||
}
|
10
3rdparty/spirv-tools/source/fuzz/CMakeLists.txt
vendored
10
3rdparty/spirv-tools/source/fuzz/CMakeLists.txt
vendored
@ -65,6 +65,7 @@ if(SPIRV_BUILD_FUZZER)
|
||||
fuzzer_pass_copy_objects.h
|
||||
fuzzer_pass_donate_modules.h
|
||||
fuzzer_pass_invert_comparison_operators.h
|
||||
fuzzer_pass_interchange_signedness_of_integer_operands.h
|
||||
fuzzer_pass_interchange_zero_like_constants.h
|
||||
fuzzer_pass_merge_blocks.h
|
||||
fuzzer_pass_obfuscate_constants.h
|
||||
@ -76,6 +77,7 @@ if(SPIRV_BUILD_FUZZER)
|
||||
fuzzer_pass_replace_copy_memories_with_loads_stores.h
|
||||
fuzzer_pass_replace_copy_objects_with_stores_loads.h
|
||||
fuzzer_pass_replace_linear_algebra_instructions.h
|
||||
fuzzer_pass_replace_loads_stores_with_copy_memories.h
|
||||
fuzzer_pass_replace_parameter_with_global.h
|
||||
fuzzer_pass_replace_params_with_struct.h
|
||||
fuzzer_pass_split_blocks.h
|
||||
@ -106,6 +108,7 @@ if(SPIRV_BUILD_FUZZER)
|
||||
transformation_add_global_variable.h
|
||||
transformation_add_image_sample_unused_components.h
|
||||
transformation_add_local_variable.h
|
||||
transformation_add_loop_preheader.h
|
||||
transformation_add_no_contraction_decoration.h
|
||||
transformation_add_parameter.h
|
||||
transformation_add_relaxed_decoration.h
|
||||
@ -142,6 +145,7 @@ if(SPIRV_BUILD_FUZZER)
|
||||
transformation_replace_copy_object_with_store_load.h
|
||||
transformation_replace_id_with_synonym.h
|
||||
transformation_replace_linear_algebra_instruction.h
|
||||
transformation_replace_load_store_with_copy_memory.h
|
||||
transformation_replace_parameter_with_global.h
|
||||
transformation_replace_params_with_struct.h
|
||||
transformation_set_function_control.h
|
||||
@ -192,6 +196,7 @@ if(SPIRV_BUILD_FUZZER)
|
||||
fuzzer_pass_copy_objects.cpp
|
||||
fuzzer_pass_donate_modules.cpp
|
||||
fuzzer_pass_invert_comparison_operators.cpp
|
||||
fuzzer_pass_interchange_signedness_of_integer_operands.cpp
|
||||
fuzzer_pass_interchange_zero_like_constants.cpp
|
||||
fuzzer_pass_merge_blocks.cpp
|
||||
fuzzer_pass_obfuscate_constants.cpp
|
||||
@ -203,6 +208,7 @@ if(SPIRV_BUILD_FUZZER)
|
||||
fuzzer_pass_replace_copy_memories_with_loads_stores.cpp
|
||||
fuzzer_pass_replace_copy_objects_with_stores_loads.cpp
|
||||
fuzzer_pass_replace_linear_algebra_instructions.cpp
|
||||
fuzzer_pass_replace_loads_stores_with_copy_memories.cpp
|
||||
fuzzer_pass_replace_parameter_with_global.cpp
|
||||
fuzzer_pass_replace_params_with_struct.cpp
|
||||
fuzzer_pass_split_blocks.cpp
|
||||
@ -232,6 +238,7 @@ if(SPIRV_BUILD_FUZZER)
|
||||
transformation_add_global_variable.cpp
|
||||
transformation_add_image_sample_unused_components.cpp
|
||||
transformation_add_local_variable.cpp
|
||||
transformation_add_loop_preheader.cpp
|
||||
transformation_add_no_contraction_decoration.cpp
|
||||
transformation_add_parameter.cpp
|
||||
transformation_add_relaxed_decoration.cpp
|
||||
@ -268,6 +275,7 @@ if(SPIRV_BUILD_FUZZER)
|
||||
transformation_replace_copy_object_with_store_load.cpp
|
||||
transformation_replace_id_with_synonym.cpp
|
||||
transformation_replace_linear_algebra_instruction.cpp
|
||||
transformation_replace_load_store_with_copy_memory.cpp
|
||||
transformation_replace_parameter_with_global.cpp
|
||||
transformation_replace_params_with_struct.cpp
|
||||
transformation_set_function_control.cpp
|
||||
@ -315,7 +323,7 @@ if(SPIRV_BUILD_FUZZER)
|
||||
|
||||
# The fuzzer reuses a lot of functionality from the SPIRV-Tools library.
|
||||
target_link_libraries(SPIRV-Tools-fuzz
|
||||
PUBLIC ${SPIRV_TOOLS}
|
||||
PUBLIC ${SPIRV_TOOLS}-static
|
||||
PUBLIC SPIRV-Tools-opt
|
||||
PUBLIC protobuf::libprotobuf)
|
||||
|
||||
|
112
3rdparty/spirv-tools/source/fuzz/fact_manager.cpp
vendored
112
3rdparty/spirv-tools/source/fuzz/fact_manager.cpp
vendored
@ -474,11 +474,19 @@ class FactManager::DataSynonymAndIdEquationFacts {
|
||||
void MakeEquivalent(const protobufs::DataDescriptor& dd1,
|
||||
const protobufs::DataDescriptor& dd2);
|
||||
|
||||
// Registers a data descriptor in the equivalence relation if it hasn't been
|
||||
// registered yet, and returns its representative.
|
||||
const protobufs::DataDescriptor* RegisterDataDescriptor(
|
||||
const protobufs::DataDescriptor& dd);
|
||||
|
||||
// Returns true if and only if |dd1| and |dd2| are valid data descriptors
|
||||
// whose associated data have the same type (modulo integer signedness).
|
||||
bool DataDescriptorsAreWellFormedAndComparable(
|
||||
// whose associated data have compatible types. Two types are compatible if:
|
||||
// - they are the same
|
||||
// - they both are numerical or vectors of numerical components with the same
|
||||
// number of components and the same bit count per component
|
||||
static bool DataDescriptorsAreWellFormedAndComparable(
|
||||
opt::IRContext* context, const protobufs::DataDescriptor& dd1,
|
||||
const protobufs::DataDescriptor& dd2) const;
|
||||
const protobufs::DataDescriptor& dd2);
|
||||
|
||||
OperationSet GetEquations(const protobufs::DataDescriptor* lhs) const;
|
||||
|
||||
@ -530,9 +538,7 @@ void FactManager::DataSynonymAndIdEquationFacts::AddFact(
|
||||
protobufs::DataDescriptor lhs_dd = MakeDataDescriptor(fact.lhs_id(), {});
|
||||
|
||||
// Register the LHS in the equivalence relation if needed.
|
||||
if (!synonymous_.Exists(lhs_dd)) {
|
||||
synonymous_.Register(lhs_dd);
|
||||
}
|
||||
RegisterDataDescriptor(lhs_dd);
|
||||
|
||||
// Get equivalence class representatives for all ids used on the RHS of the
|
||||
// equation.
|
||||
@ -540,11 +546,8 @@ void FactManager::DataSynonymAndIdEquationFacts::AddFact(
|
||||
for (auto rhs_id : fact.rhs_id()) {
|
||||
// Register a data descriptor based on this id in the equivalence relation
|
||||
// if needed, and then record the equivalence class representative.
|
||||
protobufs::DataDescriptor rhs_dd = MakeDataDescriptor(rhs_id, {});
|
||||
if (!synonymous_.Exists(rhs_dd)) {
|
||||
synonymous_.Register(rhs_dd);
|
||||
}
|
||||
rhs_dd_ptrs.push_back(synonymous_.Find(&rhs_dd));
|
||||
rhs_dd_ptrs.push_back(
|
||||
RegisterDataDescriptor(MakeDataDescriptor(rhs_id, {})));
|
||||
}
|
||||
|
||||
// Now add the fact.
|
||||
@ -604,6 +607,14 @@ void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive(
|
||||
case SpvOpConvertUToF:
|
||||
ComputeConversionDataSynonymFacts(*rhs_dds[0], context);
|
||||
break;
|
||||
case SpvOpBitcast: {
|
||||
assert(DataDescriptorsAreWellFormedAndComparable(context, lhs_dd,
|
||||
*rhs_dds[0]) &&
|
||||
"Operands of OpBitcast equation fact must have compatible types");
|
||||
if (!synonymous_.IsEquivalent(lhs_dd, *rhs_dds[0])) {
|
||||
AddDataSynonymFactRecursive(lhs_dd, *rhs_dds[0], context);
|
||||
}
|
||||
} break;
|
||||
case SpvOpIAdd: {
|
||||
// Equation form: "a = b + c"
|
||||
for (const auto& equation : GetEquations(rhs_dds[0])) {
|
||||
@ -713,9 +724,15 @@ void FactManager::DataSynonymAndIdEquationFacts::AddDataSynonymFactRecursive(
|
||||
|
||||
// Record that the data descriptors provided in the fact are equivalent.
|
||||
MakeEquivalent(dd1, dd2);
|
||||
assert(synonymous_.Find(&dd1) == synonymous_.Find(&dd2) &&
|
||||
"|dd1| and |dd2| must have a single representative");
|
||||
|
||||
// Compute various corollary facts.
|
||||
|
||||
// |dd1| and |dd2| belong to the same equivalence class so it doesn't matter
|
||||
// which one we use here.
|
||||
ComputeConversionDataSynonymFacts(dd1, context);
|
||||
|
||||
ComputeCompositeDataSynonymFacts(dd1, dd2, context);
|
||||
}
|
||||
|
||||
@ -1132,11 +1149,8 @@ void FactManager::DataSynonymAndIdEquationFacts::MakeEquivalent(
|
||||
const protobufs::DataDescriptor& dd2) {
|
||||
// Register the data descriptors if they are not already known to the
|
||||
// equivalence relation.
|
||||
for (const auto& dd : {dd1, dd2}) {
|
||||
if (!synonymous_.Exists(dd)) {
|
||||
synonymous_.Register(dd);
|
||||
}
|
||||
}
|
||||
RegisterDataDescriptor(dd1);
|
||||
RegisterDataDescriptor(dd2);
|
||||
|
||||
if (synonymous_.IsEquivalent(dd1, dd2)) {
|
||||
// The data descriptors are already known to be equivalent, so there is
|
||||
@ -1203,10 +1217,17 @@ void FactManager::DataSynonymAndIdEquationFacts::MakeEquivalent(
|
||||
id_equations_.erase(no_longer_representative);
|
||||
}
|
||||
|
||||
const protobufs::DataDescriptor*
|
||||
FactManager::DataSynonymAndIdEquationFacts::RegisterDataDescriptor(
|
||||
const protobufs::DataDescriptor& dd) {
|
||||
return synonymous_.Exists(dd) ? synonymous_.Find(&dd)
|
||||
: synonymous_.Register(dd);
|
||||
}
|
||||
|
||||
bool FactManager::DataSynonymAndIdEquationFacts::
|
||||
DataDescriptorsAreWellFormedAndComparable(
|
||||
opt::IRContext* context, const protobufs::DataDescriptor& dd1,
|
||||
const protobufs::DataDescriptor& dd2) const {
|
||||
const protobufs::DataDescriptor& dd2) {
|
||||
auto end_type_id_1 = fuzzerutil::WalkCompositeTypeIndices(
|
||||
context, context->get_def_use_mgr()->GetDef(dd1.object())->type_id(),
|
||||
dd1.index());
|
||||
@ -1225,30 +1246,49 @@ bool FactManager::DataSynonymAndIdEquationFacts::
|
||||
// vectors that differ only in signedness.
|
||||
|
||||
// Get both types.
|
||||
const opt::analysis::Type* type_1 =
|
||||
context->get_type_mgr()->GetType(end_type_id_1);
|
||||
const opt::analysis::Type* type_2 =
|
||||
context->get_type_mgr()->GetType(end_type_id_2);
|
||||
const auto* type_a = context->get_type_mgr()->GetType(end_type_id_1);
|
||||
const auto* type_b = context->get_type_mgr()->GetType(end_type_id_2);
|
||||
assert(type_a && type_b && "Data descriptors have invalid type(s)");
|
||||
|
||||
// If the first type is a vector, check that the second type is a vector of
|
||||
// the same width, and drill down to the vector element types.
|
||||
if (type_1->AsVector()) {
|
||||
if (!type_2->AsVector()) {
|
||||
// If both types are numerical or vectors of numerical components, then they
|
||||
// are compatible if they have the same number of components and the same bit
|
||||
// count per component.
|
||||
|
||||
if (type_a->AsVector() && type_b->AsVector()) {
|
||||
const auto* vector_a = type_a->AsVector();
|
||||
const auto* vector_b = type_b->AsVector();
|
||||
|
||||
if (vector_a->element_count() != vector_b->element_count() ||
|
||||
vector_a->element_type()->AsBool() ||
|
||||
vector_b->element_type()->AsBool()) {
|
||||
// The case where both vectors have boolean elements and the same number
|
||||
// of components is handled by the direct equality check earlier.
|
||||
// You can't have multiple identical boolean vector types.
|
||||
return false;
|
||||
}
|
||||
if (type_1->AsVector()->element_count() !=
|
||||
type_2->AsVector()->element_count()) {
|
||||
return false;
|
||||
|
||||
type_a = vector_a->element_type();
|
||||
type_b = vector_b->element_type();
|
||||
}
|
||||
type_1 = type_1->AsVector()->element_type();
|
||||
type_2 = type_2->AsVector()->element_type();
|
||||
|
||||
auto get_bit_count_for_numeric_type =
|
||||
[](const opt::analysis::Type& type) -> uint32_t {
|
||||
if (const auto* integer = type.AsInteger()) {
|
||||
return integer->width();
|
||||
} else if (const auto* floating = type.AsFloat()) {
|
||||
return floating->width();
|
||||
} else {
|
||||
assert(false && "|type| must be a numerical type");
|
||||
return 0;
|
||||
}
|
||||
// Check that type_1 and type_2 are both integer types of the same bit-width
|
||||
// (but with potentially different signedness).
|
||||
auto integer_type_1 = type_1->AsInteger();
|
||||
auto integer_type_2 = type_2->AsInteger();
|
||||
return integer_type_1 && integer_type_2 &&
|
||||
integer_type_1->width() == integer_type_2->width();
|
||||
};
|
||||
|
||||
// Checks that both |type_a| and |type_b| are either numerical or vectors of
|
||||
// numerical components and have the same number of bits.
|
||||
return (type_a->AsInteger() || type_a->AsFloat()) &&
|
||||
(type_b->AsInteger() || type_b->AsFloat()) &&
|
||||
(get_bit_count_for_numeric_type(*type_a) ==
|
||||
get_bit_count_for_numeric_type(*type_b));
|
||||
}
|
||||
|
||||
std::vector<const protobufs::DataDescriptor*>
|
||||
|
4
3rdparty/spirv-tools/source/fuzz/fuzzer.cpp
vendored
4
3rdparty/spirv-tools/source/fuzz/fuzzer.cpp
vendored
@ -46,6 +46,7 @@
|
||||
#include "source/fuzz/fuzzer_pass_construct_composites.h"
|
||||
#include "source/fuzz/fuzzer_pass_copy_objects.h"
|
||||
#include "source/fuzz/fuzzer_pass_donate_modules.h"
|
||||
#include "source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h"
|
||||
#include "source/fuzz/fuzzer_pass_interchange_zero_like_constants.h"
|
||||
#include "source/fuzz/fuzzer_pass_invert_comparison_operators.h"
|
||||
#include "source/fuzz/fuzzer_pass_merge_blocks.h"
|
||||
@ -335,6 +336,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
|
||||
MaybeAddPass<FuzzerPassAddNoContractionDecorations>(
|
||||
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassInterchangeSignednessOfIntegerOperands>(
|
||||
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassInterchangeZeroLikeConstants>(
|
||||
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
|
@ -67,6 +67,8 @@ const std::pair<uint32_t, uint32_t> kChanceOfGoingDeeperWhenMakingAccessChain =
|
||||
{50, 95};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfInterchangingZeroLikeConstants = {
|
||||
10, 90};
|
||||
const std::pair<uint32_t, uint32_t>
|
||||
kChanceOfInterchangingSignednessOfIntegerOperands = {10, 90};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfInvertingComparisonOperators = {
|
||||
20, 50};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfMakingDonorLivesafe = {40, 60};
|
||||
@ -84,6 +86,8 @@ const std::pair<uint32_t, uint32_t> kChanceOfReplacingCopyObjectWithStoreLoad =
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfReplacingIdWithSynonym = {10, 90};
|
||||
const std::pair<uint32_t, uint32_t>
|
||||
kChanceOfReplacingLinearAlgebraInstructions = {10, 90};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfReplacingLoadStoreWithCopyMemory =
|
||||
{20, 90};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfReplacingParametersWithGlobals = {
|
||||
30, 70};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfReplacingParametersWithStruct = {
|
||||
@ -195,6 +199,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
|
||||
ChooseBetweenMinAndMax(kChanceOfDonatingAdditionalModule);
|
||||
chance_of_going_deeper_when_making_access_chain_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfGoingDeeperWhenMakingAccessChain);
|
||||
chance_of_interchanging_signedness_of_integer_operands_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfInterchangingSignednessOfIntegerOperands);
|
||||
chance_of_interchanging_zero_like_constants_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfInterchangingZeroLikeConstants);
|
||||
chance_of_inverting_comparison_operators_ =
|
||||
@ -222,6 +228,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
|
||||
ChooseBetweenMinAndMax(kChanceOfReplacingIdWithSynonym);
|
||||
chance_of_replacing_linear_algebra_instructions_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfReplacingLinearAlgebraInstructions);
|
||||
chance_of_replacing_load_store_with_copy_memory_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfReplacingLoadStoreWithCopyMemory);
|
||||
chance_of_replacing_parameters_with_globals_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfReplacingParametersWithGlobals);
|
||||
chance_of_replacing_parameters_with_struct_ =
|
||||
|
@ -186,6 +186,9 @@ class FuzzerContext {
|
||||
uint32_t GetChanceOfGoingDeeperWhenMakingAccessChain() {
|
||||
return chance_of_going_deeper_when_making_access_chain_;
|
||||
}
|
||||
uint32_t GetChanceOfInterchangingSignednessOfIntegerOperands() {
|
||||
return chance_of_interchanging_signedness_of_integer_operands_;
|
||||
}
|
||||
uint32_t GetChanceOfInterchangingZeroLikeConstants() {
|
||||
return chance_of_interchanging_zero_like_constants_;
|
||||
}
|
||||
@ -224,6 +227,9 @@ class FuzzerContext {
|
||||
uint32_t GetChanceOfReplacingLinearAlgebraInstructions() {
|
||||
return chance_of_replacing_linear_algebra_instructions_;
|
||||
}
|
||||
uint32_t GetChanceOfReplacingLoadStoreWithCopyMemory() {
|
||||
return chance_of_replacing_load_store_with_copy_memory_;
|
||||
}
|
||||
uint32_t GetChanceOfReplacingParametersWithGlobals() {
|
||||
return chance_of_replacing_parameters_with_globals_;
|
||||
}
|
||||
@ -350,6 +356,7 @@ class FuzzerContext {
|
||||
uint32_t chance_of_copying_object_;
|
||||
uint32_t chance_of_donating_additional_module_;
|
||||
uint32_t chance_of_going_deeper_when_making_access_chain_;
|
||||
uint32_t chance_of_interchanging_signedness_of_integer_operands_;
|
||||
uint32_t chance_of_interchanging_zero_like_constants_;
|
||||
uint32_t chance_of_inverting_comparison_operators_;
|
||||
uint32_t chance_of_making_donor_livesafe_;
|
||||
@ -364,6 +371,7 @@ class FuzzerContext {
|
||||
uint32_t chance_of_replacing_copyobject_with_store_load_;
|
||||
uint32_t chance_of_replacing_id_with_synonym_;
|
||||
uint32_t chance_of_replacing_linear_algebra_instructions_;
|
||||
uint32_t chance_of_replacing_load_store_with_copy_memory_;
|
||||
uint32_t chance_of_replacing_parameters_with_globals_;
|
||||
uint32_t chance_of_replacing_parameters_with_struct_;
|
||||
uint32_t chance_of_splitting_block_;
|
||||
|
19
3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp
vendored
19
3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp
vendored
@ -17,6 +17,7 @@
|
||||
#include <set>
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/id_use_descriptor.h"
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
#include "source/fuzz/transformation_add_constant_boolean.h"
|
||||
#include "source/fuzz/transformation_add_constant_composite.h"
|
||||
@ -514,5 +515,23 @@ uint32_t FuzzerPass::FindOrCreateZeroConstant(
|
||||
}
|
||||
}
|
||||
|
||||
void FuzzerPass::MaybeAddUseToReplace(
|
||||
opt::Instruction* use_inst, uint32_t use_index, uint32_t replacement_id,
|
||||
std::vector<std::pair<protobufs::IdUseDescriptor, uint32_t>>*
|
||||
uses_to_replace) {
|
||||
// Only consider this use if it is in a block
|
||||
if (!GetIRContext()->get_instr_block(use_inst)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the index of the operand restricted to input operands.
|
||||
uint32_t in_operand_index =
|
||||
fuzzerutil::InOperandIndexFromOperandIndex(*use_inst, use_index);
|
||||
auto id_use_descriptor =
|
||||
MakeIdUseDescriptorFromUse(GetIRContext(), use_inst, in_operand_index);
|
||||
uses_to_replace->emplace_back(
|
||||
std::make_pair(id_use_descriptor, replacement_id));
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
13
3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h
vendored
13
3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h
vendored
@ -175,8 +175,7 @@ class FuzzerPass {
|
||||
// with |words| as its value. If either the required integer type or the
|
||||
// constant do not exist, transformations are applied to add them.
|
||||
// The returned id either participates in IdIsIrrelevant fact or not,
|
||||
// depending
|
||||
// on the |is_irrelevant| parameter.
|
||||
// depending on the |is_irrelevant| parameter.
|
||||
uint32_t FindOrCreateIntegerConstant(const std::vector<uint32_t>& words,
|
||||
uint32_t width, bool is_signed,
|
||||
bool is_irrelevant);
|
||||
@ -274,6 +273,16 @@ class FuzzerPass {
|
||||
uint32_t FindOrCreateZeroConstant(uint32_t scalar_or_composite_type_id,
|
||||
bool is_irrelevant);
|
||||
|
||||
// Adds a pair (id_use_descriptor, |replacement_id|) to the vector
|
||||
// |uses_to_replace|, where id_use_descriptor is the id use descriptor
|
||||
// representing the usage of an id in the |use_inst| instruction, at operand
|
||||
// index |use_index|, only if the instruction is in a basic block.
|
||||
// If the instruction is not in a basic block, it does nothing.
|
||||
void MaybeAddUseToReplace(
|
||||
opt::Instruction* use_inst, uint32_t use_index, uint32_t replacement_id,
|
||||
std::vector<std::pair<protobufs::IdUseDescriptor, uint32_t>>*
|
||||
uses_to_replace);
|
||||
|
||||
private:
|
||||
opt::IRContext* ir_context_;
|
||||
TransformationContext* transformation_context_;
|
||||
|
@ -92,6 +92,11 @@ void FuzzerPassAddAccessChains::Apply() {
|
||||
relevant_pointer_instructions[GetFuzzerContext()->RandomIndex(
|
||||
relevant_pointer_instructions)];
|
||||
std::vector<uint32_t> index_ids;
|
||||
|
||||
// Each index accessing a non-struct composite will be clamped, thus
|
||||
// needing a pair of fresh ids
|
||||
std::vector<std::pair<uint32_t, uint32_t>> fresh_ids_for_clamping;
|
||||
|
||||
auto pointer_type = GetIRContext()->get_def_use_mgr()->GetDef(
|
||||
chosen_pointer->type_id());
|
||||
uint32_t subobject_type_id = pointer_type->GetSingleWordInOperand(1);
|
||||
@ -132,20 +137,37 @@ void FuzzerPassAddAccessChains::Apply() {
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3179) We
|
||||
// could allow non-constant indices when looking up non-structs,
|
||||
// using clamping to ensure they are in-bounds.
|
||||
uint32_t index_value =
|
||||
GetFuzzerContext()->GetRandomIndexForAccessChain(bound);
|
||||
index_ids.push_back(FindOrCreateIntegerConstant(
|
||||
{index_value}, 32, GetFuzzerContext()->ChooseEven(), false));
|
||||
|
||||
switch (subobject_type->opcode()) {
|
||||
case SpvOpTypeArray:
|
||||
case SpvOpTypeMatrix:
|
||||
case SpvOpTypeVector:
|
||||
case SpvOpTypeVector: {
|
||||
// The index will be clamped
|
||||
|
||||
bool is_signed = GetFuzzerContext()->ChooseEven();
|
||||
|
||||
// Make the constant ready for clamping. We need:
|
||||
// - an OpTypeBool to be present in the module
|
||||
// - an OpConstant with the same type as the index and value
|
||||
// the maximum value for an index
|
||||
// - a new pair of fresh ids for the clamping instructions
|
||||
FindOrCreateBoolType();
|
||||
FindOrCreateIntegerConstant({bound - 1}, 32, is_signed, false);
|
||||
std::pair<uint32_t, uint32_t> fresh_pair_of_ids = {
|
||||
GetFuzzerContext()->GetFreshId(),
|
||||
GetFuzzerContext()->GetFreshId()};
|
||||
fresh_ids_for_clamping.emplace_back(fresh_pair_of_ids);
|
||||
|
||||
index_ids.push_back(FindOrCreateIntegerConstant(
|
||||
{index_value}, 32, is_signed, false));
|
||||
subobject_type_id = subobject_type->GetSingleWordInOperand(0);
|
||||
break;
|
||||
|
||||
} break;
|
||||
case SpvOpTypeStruct:
|
||||
index_ids.push_back(FindOrCreateIntegerConstant(
|
||||
{index_value}, 32, GetFuzzerContext()->ChooseEven(), false));
|
||||
subobject_type_id =
|
||||
subobject_type->GetSingleWordInOperand(index_value);
|
||||
break;
|
||||
@ -162,7 +184,7 @@ void FuzzerPassAddAccessChains::Apply() {
|
||||
// Apply the transformation to add an access chain.
|
||||
ApplyTransformation(TransformationAccessChain(
|
||||
GetFuzzerContext()->GetFreshId(), chosen_pointer->result_id(),
|
||||
index_ids, instruction_descriptor));
|
||||
index_ids, instruction_descriptor, fresh_ids_for_clamping));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -93,10 +93,15 @@ void FuzzerPassAddEquationInstructions::Apply() {
|
||||
|
||||
// Make sure a result type exists in the module.
|
||||
if (const auto* vector = type->AsVector()) {
|
||||
// We store element count in a separate variable since the
|
||||
// call FindOrCreate* functions below might invalidate
|
||||
// |vector| pointer.
|
||||
const auto element_count = vector->element_count();
|
||||
|
||||
FindOrCreateVectorType(
|
||||
FindOrCreateFloatType(
|
||||
vector->element_type()->AsInteger()->width()),
|
||||
vector->element_count());
|
||||
element_count);
|
||||
} else {
|
||||
FindOrCreateFloatType(type->AsInteger()->width());
|
||||
}
|
||||
@ -138,6 +143,11 @@ void FuzzerPassAddEquationInstructions::Apply() {
|
||||
// is that they must have the same number of bits. Consider
|
||||
// improving the code below to support this in full.
|
||||
if (const auto* vector = operand_type->AsVector()) {
|
||||
// We store element count in a separate variable since the
|
||||
// call FindOrCreate* functions below might invalidate
|
||||
// |vector| pointer.
|
||||
const auto element_count = vector->element_count();
|
||||
|
||||
uint32_t element_type_id;
|
||||
if (const auto* int_type =
|
||||
vector->element_type()->AsInteger()) {
|
||||
@ -150,8 +160,7 @@ void FuzzerPassAddEquationInstructions::Apply() {
|
||||
GetFuzzerContext()->ChooseEven());
|
||||
}
|
||||
|
||||
FindOrCreateVectorType(element_type_id,
|
||||
vector->element_count());
|
||||
FindOrCreateVectorType(element_type_id, element_count);
|
||||
} else if (const auto* int_type = operand_type->AsInteger()) {
|
||||
FindOrCreateFloatType(int_type->width());
|
||||
} else {
|
||||
|
@ -60,7 +60,7 @@ void FuzzerPassApplyIdSynonyms::Apply() {
|
||||
}
|
||||
});
|
||||
|
||||
for (auto& use : uses) {
|
||||
for (const auto& use : uses) {
|
||||
auto use_inst = use.first;
|
||||
auto use_index = use.second;
|
||||
auto block_containing_use = GetIRContext()->get_instr_block(use_inst);
|
||||
@ -82,7 +82,7 @@ void FuzzerPassApplyIdSynonyms::Apply() {
|
||||
}
|
||||
|
||||
std::vector<const protobufs::DataDescriptor*> synonyms_to_try;
|
||||
for (auto& data_descriptor :
|
||||
for (const auto* data_descriptor :
|
||||
GetTransformationContext()->GetFactManager()->GetSynonymsForId(
|
||||
id_with_known_synonyms)) {
|
||||
protobufs::DataDescriptor descriptor_for_this_id =
|
||||
@ -91,8 +91,13 @@ void FuzzerPassApplyIdSynonyms::Apply() {
|
||||
// Exclude the fact that the id is synonymous with itself.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (DataDescriptorsHaveCompatibleTypes(
|
||||
use_inst->opcode(), use_in_operand_index,
|
||||
descriptor_for_this_id, *data_descriptor)) {
|
||||
synonyms_to_try.push_back(data_descriptor);
|
||||
}
|
||||
}
|
||||
while (!synonyms_to_try.empty()) {
|
||||
auto synonym_to_try =
|
||||
GetFuzzerContext()->RemoveAtRandomIndex(&synonyms_to_try);
|
||||
@ -162,5 +167,26 @@ void FuzzerPassApplyIdSynonyms::Apply() {
|
||||
}
|
||||
}
|
||||
|
||||
bool FuzzerPassApplyIdSynonyms::DataDescriptorsHaveCompatibleTypes(
|
||||
SpvOp opcode, uint32_t use_in_operand_index,
|
||||
const protobufs::DataDescriptor& dd1,
|
||||
const protobufs::DataDescriptor& dd2) {
|
||||
auto base_object_type_id_1 =
|
||||
fuzzerutil::GetTypeId(GetIRContext(), dd1.object());
|
||||
auto base_object_type_id_2 =
|
||||
fuzzerutil::GetTypeId(GetIRContext(), dd2.object());
|
||||
assert(base_object_type_id_1 && base_object_type_id_2 &&
|
||||
"Data descriptors are invalid");
|
||||
|
||||
auto type_id_1 = fuzzerutil::WalkCompositeTypeIndices(
|
||||
GetIRContext(), base_object_type_id_1, dd1.index());
|
||||
auto type_id_2 = fuzzerutil::WalkCompositeTypeIndices(
|
||||
GetIRContext(), base_object_type_id_2, dd2.index());
|
||||
assert(type_id_1 && type_id_2 && "Data descriptors have invalid types");
|
||||
|
||||
return TransformationReplaceIdWithSynonym::TypesAreCompatible(
|
||||
GetIRContext(), opcode, use_in_operand_index, type_id_1, type_id_2);
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
@ -16,7 +16,6 @@
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_APPLY_ID_SYNONYMS_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
#include "source/opt/ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
@ -34,6 +33,16 @@ class FuzzerPassApplyIdSynonyms : public FuzzerPass {
|
||||
~FuzzerPassApplyIdSynonyms() override;
|
||||
|
||||
void Apply() override;
|
||||
|
||||
private:
|
||||
// Returns true if uses of |dd1| can be replaced with |dd2| and vice-versa
|
||||
// with respect to the type. Concretely, returns true if |dd1| and |dd2| have
|
||||
// the same type or both |dd1| and |dd2| are either a numerical or a vector
|
||||
// type of integral components with possibly different signedness.
|
||||
bool DataDescriptorsHaveCompatibleTypes(SpvOp opcode,
|
||||
uint32_t use_in_operand_index,
|
||||
const protobufs::DataDescriptor& dd1,
|
||||
const protobufs::DataDescriptor& dd2);
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
|
@ -1100,11 +1100,11 @@ void FuzzerPassDonateModules::AddLivesafeFunction(
|
||||
"A runtime array type in the donor should have been "
|
||||
"replaced by a fixed-sized array in the recipient.");
|
||||
// The size of this fixed-size array is a suitable bound.
|
||||
bound = TransformationAddFunction::GetBoundForCompositeIndex(
|
||||
GetIRContext(), *fixed_size_array_type);
|
||||
bound = fuzzerutil::GetBoundForCompositeIndex(
|
||||
*fixed_size_array_type, GetIRContext());
|
||||
} else {
|
||||
bound = TransformationAddFunction::GetBoundForCompositeIndex(
|
||||
donor_ir_context, *should_be_composite_type);
|
||||
bound = fuzzerutil::GetBoundForCompositeIndex(
|
||||
*should_be_composite_type, donor_ir_context);
|
||||
}
|
||||
const uint32_t index_id = inst.GetSingleWordInOperand(index);
|
||||
auto index_inst =
|
||||
|
149
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp
vendored
Normal file
149
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp
vendored
Normal file
@ -0,0 +1,149 @@
|
||||
// Copyright (c) 2020 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 "fuzzer_pass_interchange_signedness_of_integer_operands.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/id_use_descriptor.h"
|
||||
#include "source/fuzz/transformation_record_synonymous_constants.h"
|
||||
#include "source/fuzz/transformation_replace_id_with_synonym.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassInterchangeSignednessOfIntegerOperands::
|
||||
FuzzerPassInterchangeSignednessOfIntegerOperands(
|
||||
opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassInterchangeSignednessOfIntegerOperands::
|
||||
~FuzzerPassInterchangeSignednessOfIntegerOperands() = default;
|
||||
|
||||
void FuzzerPassInterchangeSignednessOfIntegerOperands::Apply() {
|
||||
// Make vector keeping track of all the uses we want to replace.
|
||||
// This is a vector of pairs, where the first element is an id use descriptor
|
||||
// identifying the use of a constant id and the second is the id that should
|
||||
// be used to replace it.
|
||||
std::vector<std::pair<protobufs::IdUseDescriptor, uint32_t>> uses_to_replace;
|
||||
|
||||
for (auto constant : GetIRContext()->GetConstants()) {
|
||||
uint32_t constant_id = constant->result_id();
|
||||
|
||||
// We want to record the synonymity of an integer constant with another
|
||||
// constant with opposite signedness, and this can only be done if they are
|
||||
// not irrelevant.
|
||||
if (GetTransformationContext()->GetFactManager()->IdIsIrrelevant(
|
||||
constant_id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t toggled_id =
|
||||
FindOrCreateToggledIntegerConstant(constant->result_id());
|
||||
if (!toggled_id) {
|
||||
// Not an integer constant
|
||||
continue;
|
||||
}
|
||||
|
||||
assert(!GetTransformationContext()->GetFactManager()->IdIsIrrelevant(
|
||||
toggled_id) &&
|
||||
"FindOrCreateToggledConstant can't produce an irrelevant id");
|
||||
|
||||
// Record synonymous constants
|
||||
ApplyTransformation(
|
||||
TransformationRecordSynonymousConstants(constant_id, toggled_id));
|
||||
|
||||
// Find all the uses of the constant and, for each, probabilistically
|
||||
// decide whether to replace it.
|
||||
GetIRContext()->get_def_use_mgr()->ForEachUse(
|
||||
constant_id,
|
||||
[this, toggled_id, &uses_to_replace](opt::Instruction* use_inst,
|
||||
uint32_t use_index) -> void {
|
||||
if (GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()
|
||||
->GetChanceOfInterchangingSignednessOfIntegerOperands())) {
|
||||
MaybeAddUseToReplace(use_inst, use_index, toggled_id,
|
||||
&uses_to_replace);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Replace the ids if it is allowed.
|
||||
for (auto use_to_replace : uses_to_replace) {
|
||||
MaybeApplyTransformation(TransformationReplaceIdWithSynonym(
|
||||
use_to_replace.first, use_to_replace.second));
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t FuzzerPassInterchangeSignednessOfIntegerOperands::
|
||||
FindOrCreateToggledIntegerConstant(uint32_t id) {
|
||||
auto constant = GetIRContext()->get_constant_mgr()->FindDeclaredConstant(id);
|
||||
|
||||
// This pass only toggles integer constants.
|
||||
if (!constant->AsIntConstant() &&
|
||||
(!constant->AsVectorConstant() ||
|
||||
!constant->AsVectorConstant()->component_type()->AsInteger())) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (auto integer = constant->AsIntConstant()) {
|
||||
auto type = integer->type()->AsInteger();
|
||||
|
||||
// Find or create and return the toggled constant.
|
||||
return FindOrCreateIntegerConstant(std::vector<uint32_t>(integer->words()),
|
||||
type->width(), !type->IsSigned(), false);
|
||||
}
|
||||
|
||||
// The constant is an integer vector.
|
||||
|
||||
// Find the component type.
|
||||
auto component_type =
|
||||
constant->AsVectorConstant()->component_type()->AsInteger();
|
||||
|
||||
// Find or create the toggled component type.
|
||||
uint32_t toggled_component_type = FindOrCreateIntegerType(
|
||||
component_type->width(), !component_type->IsSigned());
|
||||
|
||||
// Get the information about the toggled components. We need to extract this
|
||||
// information now because the analyses might be invalidated, which would make
|
||||
// the constant and component_type variables invalid.
|
||||
std::vector<std::vector<uint32_t>> component_words;
|
||||
|
||||
for (auto component : constant->AsVectorConstant()->GetComponents()) {
|
||||
component_words.push_back(component->AsIntConstant()->words());
|
||||
}
|
||||
uint32_t width = component_type->width();
|
||||
bool is_signed = !component_type->IsSigned();
|
||||
|
||||
std::vector<uint32_t> toggled_components;
|
||||
|
||||
// Find or create the toggled components.
|
||||
for (auto words : component_words) {
|
||||
toggled_components.push_back(
|
||||
FindOrCreateIntegerConstant(words, width, is_signed, false));
|
||||
}
|
||||
|
||||
// Find or create the required toggled vector type.
|
||||
uint32_t toggled_type = FindOrCreateVectorType(
|
||||
toggled_component_type, (uint32_t)toggled_components.size());
|
||||
|
||||
// Find or create and return the toggled vector constant.
|
||||
return FindOrCreateCompositeConstant(toggled_components, toggled_type, false);
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
51
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h
vendored
Normal file
51
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
// Copyright (c) 2020 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 SOURCE_FUZZ_FUZZER_PASS_INTERCHANGE_SIGNEDNESS_OF_INTEGER_OPERANDS_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_INTERCHANGE_SIGNEDNESS_OF_INTEGER_OPERANDS_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// A pass that:
|
||||
// - Finds all the integer constant (scalar and vector) definitions in the
|
||||
// module and adds the definitions of the integer with the same data words but
|
||||
// opposite signedness. If the synonym is already in the module, it does not
|
||||
// add a new one.
|
||||
// - For each use of an integer constant where its signedness does not matter,
|
||||
// decides whether to change it to the id of the toggled constant.
|
||||
class FuzzerPassInterchangeSignednessOfIntegerOperands : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassInterchangeSignednessOfIntegerOperands(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassInterchangeSignednessOfIntegerOperands() override;
|
||||
|
||||
void Apply() override;
|
||||
|
||||
private:
|
||||
// Given the id of an integer constant (scalar or vector), it finds or creates
|
||||
// the corresponding toggled constant (the integer with the same data words
|
||||
// but opposite signedness). Returns the id of the toggled instruction if the
|
||||
// constant is an integer scalar or vector, 0 otherwise.
|
||||
uint32_t FindOrCreateToggledIntegerConstant(uint32_t id);
|
||||
};
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_INTERCHANGE_SIGNEDNESS_OF_INTEGER_OPERANDS_
|
@ -57,24 +57,6 @@ uint32_t FuzzerPassInterchangeZeroLikeConstants::FindOrCreateToggledConstant(
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FuzzerPassInterchangeZeroLikeConstants::MaybeAddUseToReplace(
|
||||
opt::Instruction* use_inst, uint32_t use_index, uint32_t replacement_id,
|
||||
std::vector<std::pair<protobufs::IdUseDescriptor, uint32_t>>*
|
||||
uses_to_replace) {
|
||||
// Only consider this use if it is in a block
|
||||
if (!GetIRContext()->get_instr_block(use_inst)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the index of the operand restricted to input operands.
|
||||
uint32_t in_operand_index =
|
||||
fuzzerutil::InOperandIndexFromOperandIndex(*use_inst, use_index);
|
||||
auto id_use_descriptor =
|
||||
MakeIdUseDescriptorFromUse(GetIRContext(), use_inst, in_operand_index);
|
||||
uses_to_replace->emplace_back(
|
||||
std::make_pair(id_use_descriptor, replacement_id));
|
||||
}
|
||||
|
||||
void FuzzerPassInterchangeZeroLikeConstants::Apply() {
|
||||
// Make vector keeping track of all the uses we want to replace.
|
||||
// This is a vector of pairs, where the first element is an id use descriptor
|
||||
@ -118,7 +100,7 @@ void FuzzerPassInterchangeZeroLikeConstants::Apply() {
|
||||
});
|
||||
}
|
||||
|
||||
// Replace the ids
|
||||
// Replace the ids if it is allowed.
|
||||
for (auto use_to_replace : uses_to_replace) {
|
||||
MaybeApplyTransformation(TransformationReplaceIdWithSynonym(
|
||||
use_to_replace.first, use_to_replace.second));
|
||||
|
@ -46,16 +46,6 @@ class FuzzerPassInterchangeZeroLikeConstants : public FuzzerPass {
|
||||
// Returns the id of the toggled instruction if the constant is zero-like,
|
||||
// 0 otherwise.
|
||||
uint32_t FindOrCreateToggledConstant(opt::Instruction* declaration);
|
||||
|
||||
// Given an id use (described by an instruction and an index) and an id with
|
||||
// which the original one should be replaced, adds a pair (with the elements
|
||||
// being the corresponding id use descriptor and the replacement id) to
|
||||
// |uses_to_replace| if the use is in an instruction block, otherwise does
|
||||
// nothing.
|
||||
void MaybeAddUseToReplace(
|
||||
opt::Instruction* use_inst, uint32_t use_index, uint32_t replacement_id,
|
||||
std::vector<std::pair<protobufs::IdUseDescriptor, uint32_t>>*
|
||||
uses_to_replace);
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
|
@ -17,7 +17,9 @@
|
||||
#include <vector>
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
#include "source/fuzz/transformation_outline_function.h"
|
||||
#include "source/fuzz/transformation_split_block.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
@ -46,6 +48,33 @@ void FuzzerPassOutlineFunctions::Apply() {
|
||||
blocks.push_back(&block);
|
||||
}
|
||||
auto entry_block = blocks[GetFuzzerContext()->RandomIndex(blocks)];
|
||||
|
||||
// If the entry block starts with OpPhi, try to split it.
|
||||
if (entry_block->begin()->opcode() == SpvOpPhi) {
|
||||
// Find the first non-OpPhi instruction.
|
||||
opt::Instruction* non_phi_inst = nullptr;
|
||||
for (auto& instruction : *entry_block) {
|
||||
if (instruction.opcode() != SpvOpPhi) {
|
||||
non_phi_inst = &instruction;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert(non_phi_inst && "|non_phi_inst| must've been initialized");
|
||||
|
||||
// If the split was not applicable, the transformation will not work.
|
||||
uint32_t new_block_id = GetFuzzerContext()->GetFreshId();
|
||||
if (!MaybeApplyTransformation(TransformationSplitBlock(
|
||||
MakeInstructionDescriptor(non_phi_inst->result_id(),
|
||||
non_phi_inst->opcode(), 0),
|
||||
new_block_id))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The new entry block is the newly-created block.
|
||||
entry_block = &*function->FindBlock(new_block_id);
|
||||
}
|
||||
|
||||
auto dominator_analysis = GetIRContext()->GetDominatorAnalysis(function);
|
||||
auto postdominator_analysis =
|
||||
GetIRContext()->GetPostDominatorAnalysis(function);
|
||||
|
@ -34,19 +34,10 @@ FuzzerPassReplaceLinearAlgebraInstructions::
|
||||
~FuzzerPassReplaceLinearAlgebraInstructions() = default;
|
||||
|
||||
void FuzzerPassReplaceLinearAlgebraInstructions::Apply() {
|
||||
// For each instruction, checks whether it is a supported linear algebra
|
||||
// instruction. In this case, the transformation is randomly applied.
|
||||
// For each instruction, checks whether it is a linear algebra instruction. In
|
||||
// this case, the transformation is randomly applied.
|
||||
GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) {
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3354):
|
||||
// Right now we only support certain operations. When this issue is
|
||||
// addressed the following conditional can use the function
|
||||
// |spvOpcodeIsLinearAlgebra|.
|
||||
if (instruction->opcode() != SpvOpVectorTimesScalar &&
|
||||
instruction->opcode() != SpvOpMatrixTimesScalar &&
|
||||
instruction->opcode() != SpvOpVectorTimesMatrix &&
|
||||
instruction->opcode() != SpvOpMatrixTimesVector &&
|
||||
instruction->opcode() != SpvOpMatrixTimesMatrix &&
|
||||
instruction->opcode() != SpvOpDot) {
|
||||
if (!spvOpcodeIsLinearAlgebra(instruction->opcode())) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
105
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp
vendored
Normal file
105
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp
vendored
Normal file
@ -0,0 +1,105 @@
|
||||
// Copyright (c) 2020 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_replace_loads_stores_with_copy_memories.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
#include "source/fuzz/transformation_replace_load_store_with_copy_memory.h"
|
||||
#include "source/opt/instruction.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassReplaceLoadsStoresWithCopyMemories::
|
||||
FuzzerPassReplaceLoadsStoresWithCopyMemories(
|
||||
opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassReplaceLoadsStoresWithCopyMemories::
|
||||
~FuzzerPassReplaceLoadsStoresWithCopyMemories() = default;
|
||||
|
||||
void FuzzerPassReplaceLoadsStoresWithCopyMemories::Apply() {
|
||||
// We look for matching pairs of instructions OpLoad and
|
||||
// OpStore within the same block. Potential instructions OpLoad to be matched
|
||||
// are stored in a hash map. If we encounter instructions that write to memory
|
||||
// or instructions of memory barriers that could operate on variables within
|
||||
// unsafe storage classes we need to erase the hash map to avoid unsafe
|
||||
// operations.
|
||||
|
||||
// A vector of matching OpLoad and OpStore instructions.
|
||||
std::vector<std::pair<opt::Instruction*, opt::Instruction*>>
|
||||
op_load_store_pairs;
|
||||
|
||||
for (auto& function : *GetIRContext()->module()) {
|
||||
for (auto& block : function) {
|
||||
// A hash map storing potential OpLoad instructions.
|
||||
std::unordered_map<uint32_t, opt::Instruction*> current_op_loads;
|
||||
for (auto& instruction : block) {
|
||||
// Add a potential OpLoad instruction.
|
||||
if (instruction.opcode() == SpvOpLoad) {
|
||||
current_op_loads[instruction.result_id()] = &instruction;
|
||||
} else if (instruction.opcode() == SpvOpStore) {
|
||||
if (current_op_loads.find(instruction.GetSingleWordOperand(1)) !=
|
||||
current_op_loads.end()) {
|
||||
// We have found the matching OpLoad instruction to the current
|
||||
// OpStore instruction.
|
||||
op_load_store_pairs.push_back(std::make_pair(
|
||||
current_op_loads[instruction.GetSingleWordOperand(1)],
|
||||
&instruction));
|
||||
}
|
||||
}
|
||||
if (TransformationReplaceLoadStoreWithCopyMemory::IsMemoryWritingOpCode(
|
||||
instruction.opcode())) {
|
||||
current_op_loads.clear();
|
||||
} else if (TransformationReplaceLoadStoreWithCopyMemory::
|
||||
IsMemoryBarrierOpCode(instruction.opcode())) {
|
||||
for (auto it = current_op_loads.begin();
|
||||
it != current_op_loads.end();) {
|
||||
// Get the storage class.
|
||||
opt::Instruction* source_id =
|
||||
GetIRContext()->get_def_use_mgr()->GetDef(
|
||||
it->second->GetSingleWordOperand(2));
|
||||
SpvStorageClass storage_class =
|
||||
fuzzerutil::GetStorageClassFromPointerType(
|
||||
GetIRContext(), source_id->type_id());
|
||||
if (!TransformationReplaceLoadStoreWithCopyMemory::
|
||||
IsStorageClassSafeAcrossMemoryBarriers(storage_class)) {
|
||||
it = current_op_loads.erase(it);
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto instr_pair : op_load_store_pairs) {
|
||||
// Randomly decide to apply the transformation for the
|
||||
// potential pairs.
|
||||
if (!GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()
|
||||
->GetChanceOfReplacingLoadStoreWithCopyMemory())) {
|
||||
ApplyTransformation(TransformationReplaceLoadStoreWithCopyMemory(
|
||||
MakeInstructionDescriptor(GetIRContext(), instr_pair.first),
|
||||
MakeInstructionDescriptor(GetIRContext(), instr_pair.second)));
|
||||
}
|
||||
}
|
||||
} // namespace fuzz
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
41
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h
vendored
Normal file
41
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright (c) 2020 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 SPIRV_TOOLS_FUZZER_PASS_REPLACE_LOADS_STORES_WITH_COPY_MEMORIES_H
|
||||
#define SPIRV_TOOLS_FUZZER_PASS_REPLACE_LOADS_STORES_WITH_COPY_MEMORIES_H
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// A fuzzer pass that takes pairs of instruction descriptors to OpLoad and
|
||||
// OpStore that have the same intermediate value and in each pair replaces the
|
||||
// OpStore with an equivalent OpCopyMemory.
|
||||
class FuzzerPassReplaceLoadsStoresWithCopyMemories : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassReplaceLoadsStoresWithCopyMemories(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassReplaceLoadsStoresWithCopyMemories() override;
|
||||
|
||||
void Apply() override;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SPIRV_TOOLS_FUZZER_PASS_REPLACE_LOADS_STORES_WITH_COPY_MEMORIES_H
|
@ -98,7 +98,7 @@ void FuzzerPassReplaceParamsWithStruct::Apply() {
|
||||
parameter_id.push_back(params[index]->result_id());
|
||||
}
|
||||
|
||||
std::unordered_map<uint32_t, uint32_t> caller_id_to_fresh_id;
|
||||
std::map<uint32_t, uint32_t> caller_id_to_fresh_id;
|
||||
for (const auto* inst :
|
||||
fuzzerutil::GetCallers(GetIRContext(), function.result_id())) {
|
||||
caller_id_to_fresh_id[inst->result_id()] =
|
||||
|
119
3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp
vendored
119
3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp
vendored
@ -373,6 +373,28 @@ uint32_t GetArraySize(const opt::Instruction& array_type_instruction,
|
||||
return array_length_constant->GetU32();
|
||||
}
|
||||
|
||||
uint32_t GetBoundForCompositeIndex(const opt::Instruction& composite_type_inst,
|
||||
opt::IRContext* ir_context) {
|
||||
switch (composite_type_inst.opcode()) {
|
||||
case SpvOpTypeArray:
|
||||
return fuzzerutil::GetArraySize(composite_type_inst, ir_context);
|
||||
case SpvOpTypeMatrix:
|
||||
case SpvOpTypeVector:
|
||||
return composite_type_inst.GetSingleWordInOperand(1);
|
||||
case SpvOpTypeStruct: {
|
||||
return fuzzerutil::GetNumberOfStructMembers(composite_type_inst);
|
||||
}
|
||||
case SpvOpTypeRuntimeArray:
|
||||
assert(false &&
|
||||
"GetBoundForCompositeIndex should not be invoked with an "
|
||||
"OpTypeRuntimeArray, which does not have a static bound.");
|
||||
return 0;
|
||||
default:
|
||||
assert(false && "Unknown composite type.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsValid(opt::IRContext* context, spv_validator_options validator_options) {
|
||||
std::vector<uint32_t> binary;
|
||||
context->module()->ToBinary(&binary, false);
|
||||
@ -735,6 +757,20 @@ std::vector<opt::Instruction*> GetParameters(opt::IRContext* ir_context,
|
||||
return result;
|
||||
}
|
||||
|
||||
void RemoveParameter(opt::IRContext* ir_context, uint32_t parameter_id) {
|
||||
auto* function = GetFunctionFromParameterId(ir_context, parameter_id);
|
||||
assert(function && "|parameter_id| is invalid");
|
||||
assert(!FunctionIsEntryPoint(ir_context, function->result_id()) &&
|
||||
"Can't remove parameter from an entry point function");
|
||||
|
||||
function->RemoveParameter(parameter_id);
|
||||
|
||||
// We've just removed parameters from the function and cleared their memory.
|
||||
// Make sure analyses have no dangling pointers.
|
||||
ir_context->InvalidateAnalysesExceptFor(
|
||||
opt::IRContext::Analysis::kAnalysisNone);
|
||||
}
|
||||
|
||||
std::vector<opt::Instruction*> GetCallers(opt::IRContext* ir_context,
|
||||
uint32_t function_id) {
|
||||
assert(FindFunction(ir_context, function_id) &&
|
||||
@ -1104,6 +1140,32 @@ uint32_t MaybeGetIntegerConstant(
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t MaybeGetIntegerConstantFromValueAndType(opt::IRContext* ir_context,
|
||||
uint32_t value,
|
||||
uint32_t int_type_id) {
|
||||
auto int_type_inst = ir_context->get_def_use_mgr()->GetDef(int_type_id);
|
||||
|
||||
assert(int_type_inst && "The given type id must exist.");
|
||||
|
||||
auto int_type = ir_context->get_type_mgr()
|
||||
->GetType(int_type_inst->result_id())
|
||||
->AsInteger();
|
||||
|
||||
assert(int_type && int_type->width() == 32 &&
|
||||
"The given type id must correspond to an 32-bit integer type.");
|
||||
|
||||
opt::analysis::IntConstant constant(int_type, {value});
|
||||
|
||||
// Check that the constant exists in the module.
|
||||
if (!ir_context->get_constant_mgr()->FindConstant(&constant)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ir_context->get_constant_mgr()
|
||||
->GetDefiningInstruction(&constant)
|
||||
->result_id();
|
||||
}
|
||||
|
||||
uint32_t MaybeGetFloatConstant(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context,
|
||||
@ -1193,6 +1255,63 @@ void AddStructType(opt::IRContext* ir_context, uint32_t result_id,
|
||||
UpdateModuleIdBound(ir_context, result_id);
|
||||
}
|
||||
|
||||
bool TypesAreEqualUpToSign(opt::IRContext* ir_context, uint32_t type1_id,
|
||||
uint32_t type2_id) {
|
||||
if (type1_id == type2_id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto type1 = ir_context->get_type_mgr()->GetType(type1_id);
|
||||
auto type2 = ir_context->get_type_mgr()->GetType(type2_id);
|
||||
|
||||
// Integer scalar types must have the same width
|
||||
if (type1->AsInteger() && type2->AsInteger()) {
|
||||
return type1->AsInteger()->width() == type2->AsInteger()->width();
|
||||
}
|
||||
|
||||
// Integer vector types must have the same number of components and their
|
||||
// component types must be integers with the same width.
|
||||
if (type1->AsVector() && type2->AsVector()) {
|
||||
auto component_type1 = type1->AsVector()->element_type()->AsInteger();
|
||||
auto component_type2 = type2->AsVector()->element_type()->AsInteger();
|
||||
|
||||
// Only check the component count and width if they are integer.
|
||||
if (component_type1 && component_type2) {
|
||||
return type1->AsVector()->element_count() ==
|
||||
type2->AsVector()->element_count() &&
|
||||
component_type1->width() == component_type2->width();
|
||||
}
|
||||
}
|
||||
|
||||
// In all other cases, the types cannot be considered equal.
|
||||
return false;
|
||||
}
|
||||
|
||||
std::map<uint32_t, uint32_t> RepeatedUInt32PairToMap(
|
||||
const google::protobuf::RepeatedPtrField<protobufs::UInt32Pair>& data) {
|
||||
std::map<uint32_t, uint32_t> result;
|
||||
|
||||
for (const auto& entry : data) {
|
||||
result[entry.first()] = entry.second();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
google::protobuf::RepeatedPtrField<protobufs::UInt32Pair>
|
||||
MapToRepeatedUInt32Pair(const std::map<uint32_t, uint32_t>& data) {
|
||||
google::protobuf::RepeatedPtrField<protobufs::UInt32Pair> result;
|
||||
|
||||
for (const auto& entry : data) {
|
||||
protobufs::UInt32Pair pair;
|
||||
pair.set_first(entry.first);
|
||||
pair.set_second(entry.second);
|
||||
*result.Add() = std::move(pair);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace fuzzerutil
|
||||
|
||||
} // namespace fuzz
|
||||
|
42
3rdparty/spirv-tools/source/fuzz/fuzzer_util.h
vendored
42
3rdparty/spirv-tools/source/fuzz/fuzzer_util.h
vendored
@ -15,6 +15,7 @@
|
||||
#ifndef SOURCE_FUZZ_FUZZER_UTIL_H_
|
||||
#define SOURCE_FUZZ_FUZZER_UTIL_H_
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
||||
@ -141,6 +142,13 @@ uint32_t GetNumberOfStructMembers(
|
||||
uint32_t GetArraySize(const opt::Instruction& array_type_instruction,
|
||||
opt::IRContext* context);
|
||||
|
||||
// Returns the bound for indexing into a composite of type
|
||||
// |composite_type_inst|, i.e. the number of fields of a struct, the size of an
|
||||
// array, the number of components of a vector, or the number of columns of a
|
||||
// matrix. |composite_type_inst| must be the type of a composite.
|
||||
uint32_t GetBoundForCompositeIndex(const opt::Instruction& composite_type_inst,
|
||||
opt::IRContext* ir_context);
|
||||
|
||||
// Returns true if and only if |context| is valid, according to the validator
|
||||
// instantiated with |validator_options|.
|
||||
bool IsValid(opt::IRContext* context, spv_validator_options validator_options);
|
||||
@ -281,6 +289,15 @@ bool IsPermutationOfRange(const std::vector<uint32_t>& arr, uint32_t lo,
|
||||
std::vector<opt::Instruction*> GetParameters(opt::IRContext* ir_context,
|
||||
uint32_t function_id);
|
||||
|
||||
// Removes an OpFunctionParameter instruction with result id |parameter_id|
|
||||
// from the its function. Parameter's function must not be an entry-point
|
||||
// function. The function must have a parameter with result id |parameter_id|.
|
||||
//
|
||||
// Prefer using this function to opt::Function::RemoveParameter since
|
||||
// this function also guarantees that |ir_context| has no invalid pointers
|
||||
// to the removed parameter.
|
||||
void RemoveParameter(opt::IRContext* ir_context, uint32_t parameter_id);
|
||||
|
||||
// Returns all OpFunctionCall instructions that call a function with result id
|
||||
// |function_id|.
|
||||
std::vector<opt::Instruction*> GetCallers(opt::IRContext* ir_context,
|
||||
@ -394,6 +411,14 @@ uint32_t MaybeGetIntegerConstant(
|
||||
const std::vector<uint32_t>& words, uint32_t width, bool is_signed,
|
||||
bool is_irrelevant);
|
||||
|
||||
// Returns the id of a 32-bit integer constant in the module with type
|
||||
// |int_type_id| and value |value|, or 0 if no such constant exists in the
|
||||
// module. |int_type_id| must exist in the module and it must correspond to a
|
||||
// 32-bit integer type.
|
||||
uint32_t MaybeGetIntegerConstantFromValueAndType(opt::IRContext* ir_context,
|
||||
uint32_t value,
|
||||
uint32_t int_type_id);
|
||||
|
||||
// Returns the result id of an OpConstant instruction of floating-point type.
|
||||
// Returns 0 if no such instruction or type is present in the module.
|
||||
// The returned id either participates in IdIsIrrelevant fact or not, depending
|
||||
@ -441,6 +466,23 @@ inline uint32_t FloatToWord(float value) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Returns true if any of the following is true:
|
||||
// - |type1_id| and |type2_id| are the same id
|
||||
// - |type1_id| and |type2_id| refer to integer scalar or vector types, only
|
||||
// differing by their signedness.
|
||||
bool TypesAreEqualUpToSign(opt::IRContext* ir_context, uint32_t type1_id,
|
||||
uint32_t type2_id);
|
||||
|
||||
// Converts repeated field of UInt32Pair to a map. If two or more equal values
|
||||
// of |UInt32Pair::first()| are available in |data|, the last value of
|
||||
// |UInt32Pair::second()| is used.
|
||||
std::map<uint32_t, uint32_t> RepeatedUInt32PairToMap(
|
||||
const google::protobuf::RepeatedPtrField<protobufs::UInt32Pair>& data);
|
||||
|
||||
// Converts a map into a repeated field of UInt32Pair.
|
||||
google::protobuf::RepeatedPtrField<protobufs::UInt32Pair>
|
||||
MapToRepeatedUInt32Pair(const std::map<uint32_t, uint32_t>& data);
|
||||
|
||||
} // namespace fuzzerutil
|
||||
|
||||
} // namespace fuzz
|
||||
|
@ -406,6 +406,8 @@ message Transformation {
|
||||
TransformationReplaceParamsWithStruct replace_params_with_struct = 59;
|
||||
TransformationReplaceCopyObjectWithStoreLoad replace_copy_object_with_store_load = 60;
|
||||
TransformationReplaceCopyMemoryWithLoadStore replace_copy_memory_with_load_store = 61;
|
||||
TransformationReplaceLoadStoreWithCopyMemory replace_load_store_with_copy_memory = 62;
|
||||
TransformationAddLoopPreheader add_loop_preheader = 63;
|
||||
// Add additional option using the next available number.
|
||||
}
|
||||
}
|
||||
@ -416,6 +418,13 @@ message TransformationAccessChain {
|
||||
|
||||
// Adds an access chain instruction based on a given pointer and indices.
|
||||
|
||||
// When accessing a struct, the corresponding indices must be 32-bit integer constants.
|
||||
// For any other composite, the indices can be any 32-bit integer, and the transformation
|
||||
// adds two instructions for each such index to clamp it to the bound, as follows:
|
||||
//
|
||||
// %t1 = OpULessThanEqual %bool %index %bound_minus_one
|
||||
// %t2 = OpSelect %int_type %t1 %index %bound_minus_one
|
||||
|
||||
// Result id for the access chain
|
||||
uint32 fresh_id = 1;
|
||||
|
||||
@ -429,6 +438,10 @@ message TransformationAccessChain {
|
||||
// OpAccessChain instruction should be inserted
|
||||
InstructionDescriptor instruction_to_insert_before = 4;
|
||||
|
||||
// Additional fresh ids, required to clamp index variables. A pair is needed
|
||||
// for each access to a non-struct composite.
|
||||
repeated UInt32Pair fresh_ids_for_clamping = 5;
|
||||
|
||||
}
|
||||
|
||||
message TransformationAddConstantBoolean {
|
||||
@ -690,6 +703,34 @@ message TransformationAddLocalVariable {
|
||||
|
||||
}
|
||||
|
||||
message TransformationAddLoopPreheader {
|
||||
|
||||
// A transformation that adds a loop preheader block before the given loop header.
|
||||
|
||||
// The id of the loop header block
|
||||
uint32 loop_header_block = 1;
|
||||
|
||||
// A fresh id for the preheader block
|
||||
uint32 fresh_id = 2;
|
||||
|
||||
// Fresh ids for splitting the OpPhi instructions in the header.
|
||||
// A new OpPhi instruction in the preheader is needed for each OpPhi instruction in the header,
|
||||
// if the header has more than one predecessor outside of the loop.
|
||||
// This allows turning instructions of the form:
|
||||
//
|
||||
// %loop_header_block = OpLabel
|
||||
// %id1 = OpPhi %type %val1 %pred1_id %val2 %pred2_id %val3 %backedge_block_id
|
||||
//
|
||||
// into:
|
||||
// %fresh_id = OpLabel
|
||||
// %phi_id1 = OpPhi %type %val1 %pred1_id %val2 %pred2_id
|
||||
// OpBranch %header_id
|
||||
// %loop_header_block = OpLabel
|
||||
// %id1 = OpPhi %type %phi_id1 %fresh_id %val3 %backedge_block_id
|
||||
repeated uint32 phi_id = 3;
|
||||
|
||||
}
|
||||
|
||||
message TransformationAddNoContractionDecoration {
|
||||
|
||||
// Applies OpDecorate NoContraction to the given result id
|
||||
@ -1331,24 +1372,22 @@ message TransformationReplaceLinearAlgebraInstruction {
|
||||
repeated uint32 fresh_ids = 1;
|
||||
|
||||
// A descriptor for a linear algebra instruction.
|
||||
// This transformation is only applicable if the described instruction has one of the following opcodes.
|
||||
// Supported:
|
||||
// OpVectorTimesScalar
|
||||
// OpMatrixTimesScalar
|
||||
// OpVectorTimesMatrix
|
||||
// OpMatrixTimesVector
|
||||
// OpMatrixTimesMatrix
|
||||
// OpDot
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3354):
|
||||
// Right now we only support certain operations. When this issue is addressed
|
||||
// the supporting comments can be removed.
|
||||
// To be supported in the future:
|
||||
// OpTranspose
|
||||
// OpOuterProduct
|
||||
InstructionDescriptor instruction_descriptor = 2;
|
||||
|
||||
}
|
||||
|
||||
message TransformationReplaceLoadStoreWithCopyMemory {
|
||||
// A transformation that takes a pair of instruction descriptors
|
||||
// to OpLoad and OpStore that have the same intermediate value
|
||||
// and replaces the OpStore with an equivalent OpCopyMemory.
|
||||
|
||||
// The instruction descriptor to OpLoad
|
||||
InstructionDescriptor load_instruction_descriptor = 1;
|
||||
|
||||
// The instruction descriptor to OpStore
|
||||
InstructionDescriptor store_instruction_descriptor = 2;
|
||||
}
|
||||
|
||||
message TransformationReplaceParamsWithStruct {
|
||||
|
||||
// Replaces parameters of the function with a struct containing
|
||||
@ -1365,12 +1404,9 @@ message TransformationReplaceParamsWithStruct {
|
||||
uint32 fresh_parameter_id = 3;
|
||||
|
||||
// Fresh ids for struct objects containing values of replaced parameters.
|
||||
// This map contains a fresh id for at least every result id of a relevant
|
||||
// This field contains a fresh id for at least every result id of a relevant
|
||||
// OpFunctionCall instruction.
|
||||
//
|
||||
// While maps are not fully deterministic, the way this map is used does not
|
||||
// exhibit nondeterminism. Change to repeated Uint32Pair if this changes.
|
||||
map<uint32, uint32> caller_id_to_fresh_composite_id = 4;
|
||||
repeated UInt32Pair caller_id_to_fresh_composite_id = 4;
|
||||
|
||||
}
|
||||
|
||||
|
21
3rdparty/spirv-tools/source/fuzz/shrinker.cpp
vendored
21
3rdparty/spirv-tools/source/fuzz/shrinker.cpp
vendored
@ -121,10 +121,12 @@ Shrinker::ShrinkerResultStatus Shrinker::Run(
|
||||
// succeeds, (b) get the binary that results from running these
|
||||
// transformations, and (c) get the subsequence of the initial transformations
|
||||
// that actually apply (in principle this could be a strict subsequence).
|
||||
if (Replayer(impl_->target_env, impl_->validate_during_replay,
|
||||
impl_->validator_options)
|
||||
.Run(binary_in, initial_facts, transformation_sequence_in,
|
||||
transformation_sequence_in.transformation_size(),
|
||||
Replayer replayer(impl_->target_env, impl_->validate_during_replay,
|
||||
impl_->validator_options);
|
||||
replayer.SetMessageConsumer(impl_->consumer);
|
||||
if (replayer.Run(binary_in, initial_facts, transformation_sequence_in,
|
||||
static_cast<uint32_t>(
|
||||
transformation_sequence_in.transformation_size()),
|
||||
¤t_best_binary, ¤t_best_transformations) !=
|
||||
Replayer::ReplayerResultStatus::kComplete) {
|
||||
return ShrinkerResultStatus::kReplayFailed;
|
||||
@ -185,7 +187,8 @@ Shrinker::ShrinkerResultStatus Shrinker::Run(
|
||||
// Remove a chunk of transformations according to the current index and
|
||||
// chunk size.
|
||||
auto transformations_with_chunk_removed =
|
||||
RemoveChunk(current_best_transformations, chunk_index, chunk_size);
|
||||
RemoveChunk(current_best_transformations,
|
||||
static_cast<uint32_t>(chunk_index), chunk_size);
|
||||
|
||||
// Replay the smaller sequence of transformations to get a next binary and
|
||||
// transformation sequence. Note that the transformations arising from
|
||||
@ -194,10 +197,10 @@ Shrinker::ShrinkerResultStatus Shrinker::Run(
|
||||
// transformations inapplicable.
|
||||
std::vector<uint32_t> next_binary;
|
||||
protobufs::TransformationSequence next_transformation_sequence;
|
||||
if (Replayer(impl_->target_env, impl_->validate_during_replay,
|
||||
impl_->validator_options)
|
||||
.Run(binary_in, initial_facts, transformations_with_chunk_removed,
|
||||
transformations_with_chunk_removed.transformation_size(),
|
||||
if (replayer.Run(
|
||||
binary_in, initial_facts, transformations_with_chunk_removed,
|
||||
static_cast<uint32_t>(
|
||||
transformations_with_chunk_removed.transformation_size()),
|
||||
&next_binary, &next_transformation_sequence) !=
|
||||
Replayer::ReplayerResultStatus::kComplete) {
|
||||
// Replay should not fail; if it does, we need to abort shrinking.
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include "source/fuzz/transformation_add_global_variable.h"
|
||||
#include "source/fuzz/transformation_add_image_sample_unused_components.h"
|
||||
#include "source/fuzz/transformation_add_local_variable.h"
|
||||
#include "source/fuzz/transformation_add_loop_preheader.h"
|
||||
#include "source/fuzz/transformation_add_no_contraction_decoration.h"
|
||||
#include "source/fuzz/transformation_add_parameter.h"
|
||||
#include "source/fuzz/transformation_add_relaxed_decoration.h"
|
||||
@ -66,6 +67,7 @@
|
||||
#include "source/fuzz/transformation_replace_copy_object_with_store_load.h"
|
||||
#include "source/fuzz/transformation_replace_id_with_synonym.h"
|
||||
#include "source/fuzz/transformation_replace_linear_algebra_instruction.h"
|
||||
#include "source/fuzz/transformation_replace_load_store_with_copy_memory.h"
|
||||
#include "source/fuzz/transformation_replace_parameter_with_global.h"
|
||||
#include "source/fuzz/transformation_replace_params_with_struct.h"
|
||||
#include "source/fuzz/transformation_set_function_control.h"
|
||||
@ -126,6 +128,9 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
|
||||
case protobufs::Transformation::TransformationCase::kAddLocalVariable:
|
||||
return MakeUnique<TransformationAddLocalVariable>(
|
||||
message.add_local_variable());
|
||||
case protobufs::Transformation::TransformationCase::kAddLoopPreheader:
|
||||
return MakeUnique<TransformationAddLoopPreheader>(
|
||||
message.add_loop_preheader());
|
||||
case protobufs::Transformation::TransformationCase::
|
||||
kAddNoContractionDecoration:
|
||||
return MakeUnique<TransformationAddNoContractionDecoration>(
|
||||
@ -233,6 +238,10 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
|
||||
kReplaceLinearAlgebraInstruction:
|
||||
return MakeUnique<TransformationReplaceLinearAlgebraInstruction>(
|
||||
message.replace_linear_algebra_instruction());
|
||||
case protobufs::Transformation::TransformationCase::
|
||||
kReplaceLoadStoreWithCopyMemory:
|
||||
return MakeUnique<TransformationReplaceLoadStoreWithCopyMemory>(
|
||||
message.replace_load_store_with_copy_memory());
|
||||
case protobufs::Transformation::TransformationCase::
|
||||
kReplaceParamsWithStruct:
|
||||
return MakeUnique<TransformationReplaceParamsWithStruct>(
|
||||
|
@ -29,7 +29,8 @@ TransformationAccessChain::TransformationAccessChain(
|
||||
TransformationAccessChain::TransformationAccessChain(
|
||||
uint32_t fresh_id, uint32_t pointer_id,
|
||||
const std::vector<uint32_t>& index_id,
|
||||
const protobufs::InstructionDescriptor& instruction_to_insert_before) {
|
||||
const protobufs::InstructionDescriptor& instruction_to_insert_before,
|
||||
const std::vector<std::pair<uint32_t, uint32_t>>& fresh_ids_for_clamping) {
|
||||
message_.set_fresh_id(fresh_id);
|
||||
message_.set_pointer_id(pointer_id);
|
||||
for (auto id : index_id) {
|
||||
@ -37,12 +38,22 @@ TransformationAccessChain::TransformationAccessChain(
|
||||
}
|
||||
*message_.mutable_instruction_to_insert_before() =
|
||||
instruction_to_insert_before;
|
||||
for (auto clamping_ids_pair : fresh_ids_for_clamping) {
|
||||
protobufs::UInt32Pair pair;
|
||||
pair.set_first(clamping_ids_pair.first);
|
||||
pair.set_second(clamping_ids_pair.second);
|
||||
*message_.add_fresh_ids_for_clamping() = pair;
|
||||
}
|
||||
}
|
||||
|
||||
bool TransformationAccessChain::IsApplicable(
|
||||
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
|
||||
// The result id must be fresh
|
||||
if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
|
||||
// Keep track of the fresh ids used to make sure that they are distinct.
|
||||
std::set<uint32_t> fresh_ids_used;
|
||||
|
||||
// The result id must be fresh.
|
||||
if (!CheckIdIsFreshAndNotUsedByThisTransformation(
|
||||
message_.fresh_id(), ir_context, &fresh_ids_used)) {
|
||||
return false;
|
||||
}
|
||||
// The pointer id must exist and have a type.
|
||||
@ -50,7 +61,7 @@ bool TransformationAccessChain::IsApplicable(
|
||||
if (!pointer || !pointer->type_id()) {
|
||||
return false;
|
||||
}
|
||||
// The type must indeed be a pointer
|
||||
// The type must indeed be a pointer.
|
||||
auto pointer_type = ir_context->get_def_use_mgr()->GetDef(pointer->type_id());
|
||||
if (pointer_type->opcode() != SpvOpTypePointer) {
|
||||
return false;
|
||||
@ -96,23 +107,86 @@ bool TransformationAccessChain::IsApplicable(
|
||||
// Start from the base type of the pointer.
|
||||
uint32_t subobject_type_id = pointer_type->GetSingleWordInOperand(1);
|
||||
|
||||
int id_pairs_used = 0;
|
||||
|
||||
// Consider the given index ids in turn.
|
||||
for (auto index_id : message_.index_id()) {
|
||||
// Try to get the integer value associated with this index is. The first
|
||||
// component of the result will be false if the id did not correspond to an
|
||||
// integer. Otherwise, the integer with which the id is associated is the
|
||||
// second component.
|
||||
std::pair<bool, uint32_t> maybe_index_value =
|
||||
GetIndexValue(ir_context, index_id);
|
||||
if (!maybe_index_value.first) {
|
||||
// There was no integer: this index is no good.
|
||||
// The index value will correspond to the value of the index if the object
|
||||
// is a struct, otherwise the value 0 will be used.
|
||||
uint32_t index_value;
|
||||
|
||||
// Check whether the object is a struct.
|
||||
if (ir_context->get_def_use_mgr()->GetDef(subobject_type_id)->opcode() ==
|
||||
SpvOpTypeStruct) {
|
||||
// It is a struct: we need to retrieve the integer value.
|
||||
|
||||
bool successful;
|
||||
std::tie(successful, index_value) =
|
||||
GetIndexValue(ir_context, index_id, subobject_type_id);
|
||||
|
||||
if (!successful) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// It is not a struct: the index will need clamping.
|
||||
|
||||
if (message_.fresh_ids_for_clamping().size() <= id_pairs_used) {
|
||||
// We don't have enough ids
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get two new ids to use and update the amount used.
|
||||
protobufs::UInt32Pair fresh_ids =
|
||||
message_.fresh_ids_for_clamping()[id_pairs_used++];
|
||||
|
||||
// Valid ids need to have been given
|
||||
if (fresh_ids.first() == 0 || fresh_ids.second() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that the ids are actually fresh and not already used by this
|
||||
// transformation.
|
||||
if (!CheckIdIsFreshAndNotUsedByThisTransformation(
|
||||
fresh_ids.first(), ir_context, &fresh_ids_used) ||
|
||||
!CheckIdIsFreshAndNotUsedByThisTransformation(
|
||||
fresh_ids.second(), ir_context, &fresh_ids_used)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ValidIndexToComposite(ir_context, index_id, subobject_type_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Perform the clamping using the fresh ids at our disposal.
|
||||
auto index_instruction = ir_context->get_def_use_mgr()->GetDef(index_id);
|
||||
|
||||
uint32_t bound = fuzzerutil::GetBoundForCompositeIndex(
|
||||
*ir_context->get_def_use_mgr()->GetDef(subobject_type_id),
|
||||
ir_context);
|
||||
|
||||
// The module must have an integer constant of value bound-1 of the same
|
||||
// type as the index.
|
||||
if (!fuzzerutil::MaybeGetIntegerConstantFromValueAndType(
|
||||
ir_context, bound - 1, index_instruction->type_id())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The module must have the definition of bool type to make a comparison.
|
||||
if (!fuzzerutil::MaybeGetBoolType(ir_context)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The index is not necessarily a constant, so we may not know its value.
|
||||
// We can use index 0 because the components of a non-struct composite
|
||||
// all have the same type, and index 0 is always in bounds.
|
||||
index_value = 0;
|
||||
}
|
||||
|
||||
// Try to walk down the type using this index. This will yield 0 if the
|
||||
// type is not a composite or the index is out of bounds, and the id of
|
||||
// the next type otherwise.
|
||||
subobject_type_id = fuzzerutil::WalkOneCompositeTypeIndex(
|
||||
ir_context, subobject_type_id, maybe_index_value.second);
|
||||
ir_context, subobject_type_id, index_value);
|
||||
if (!subobject_type_id) {
|
||||
// Either the type was not a composite (so that too many indices were
|
||||
// provided), or the index was out of bounds.
|
||||
@ -152,25 +226,105 @@ void TransformationAccessChain::Apply(
|
||||
ir_context->get_def_use_mgr()->GetDef(message_.pointer_id())->type_id());
|
||||
uint32_t subobject_type_id = pointer_type->GetSingleWordInOperand(1);
|
||||
|
||||
uint32_t id_pairs_used = 0;
|
||||
|
||||
// Go through the index ids in turn.
|
||||
for (auto index_id : message_.index_id()) {
|
||||
// Add the index id to the operands.
|
||||
operands.push_back({SPV_OPERAND_TYPE_ID, {index_id}});
|
||||
// Get the integer value associated with the index id.
|
||||
uint32_t index_value = GetIndexValue(ir_context, index_id).second;
|
||||
uint32_t index_value;
|
||||
|
||||
// Actual id to be used in the instruction: the original id
|
||||
// or the clamped one.
|
||||
uint32_t new_index_id;
|
||||
|
||||
// Check whether the object is a struct.
|
||||
if (ir_context->get_def_use_mgr()->GetDef(subobject_type_id)->opcode() ==
|
||||
SpvOpTypeStruct) {
|
||||
// It is a struct: we need to retrieve the integer value.
|
||||
|
||||
index_value =
|
||||
GetIndexValue(ir_context, index_id, subobject_type_id).second;
|
||||
|
||||
new_index_id = index_id;
|
||||
|
||||
} else {
|
||||
// It is not a struct: the index will need clamping.
|
||||
|
||||
// Get two new ids to use and update the amount used.
|
||||
protobufs::UInt32Pair fresh_ids =
|
||||
message_.fresh_ids_for_clamping()[id_pairs_used++];
|
||||
|
||||
// Perform the clamping using the fresh ids at our disposal.
|
||||
// The module will not be changed if |add_clamping_instructions| is not
|
||||
// set.
|
||||
auto index_instruction = ir_context->get_def_use_mgr()->GetDef(index_id);
|
||||
|
||||
uint32_t bound = fuzzerutil::GetBoundForCompositeIndex(
|
||||
*ir_context->get_def_use_mgr()->GetDef(subobject_type_id),
|
||||
ir_context);
|
||||
|
||||
auto bound_minus_one_id =
|
||||
fuzzerutil::MaybeGetIntegerConstantFromValueAndType(
|
||||
ir_context, bound - 1, index_instruction->type_id());
|
||||
|
||||
assert(bound_minus_one_id &&
|
||||
"A constant of value bound - 1 and the same type as the index "
|
||||
"must exist as a precondition.");
|
||||
|
||||
uint32_t bool_type_id = fuzzerutil::MaybeGetBoolType(ir_context);
|
||||
|
||||
assert(bool_type_id &&
|
||||
"An OpTypeBool instruction must exist as a precondition.");
|
||||
|
||||
auto int_type_inst =
|
||||
ir_context->get_def_use_mgr()->GetDef(index_instruction->type_id());
|
||||
|
||||
// Clamp the integer and add the corresponding instructions in the module
|
||||
// if |add_clamping_instructions| is set.
|
||||
auto instruction_to_insert_before =
|
||||
FindInstruction(message_.instruction_to_insert_before(), ir_context);
|
||||
|
||||
// Compare the index with the bound via an instruction of the form:
|
||||
// %fresh_ids.first = OpULessThanEqual %bool %int_id %bound_minus_one.
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, fresh_ids.first());
|
||||
instruction_to_insert_before->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpULessThanEqual, bool_type_id, fresh_ids.first(),
|
||||
opt::Instruction::OperandList(
|
||||
{{SPV_OPERAND_TYPE_ID, {index_instruction->result_id()}},
|
||||
{SPV_OPERAND_TYPE_ID, {bound_minus_one_id}}})));
|
||||
|
||||
// Select the index if in-bounds, otherwise one less than the bound:
|
||||
// %fresh_ids.second = OpSelect %int_type %fresh_ids.first %int_id
|
||||
// %bound_minus_one
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, fresh_ids.second());
|
||||
instruction_to_insert_before->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpSelect, int_type_inst->result_id(),
|
||||
fresh_ids.second(),
|
||||
opt::Instruction::OperandList(
|
||||
{{SPV_OPERAND_TYPE_ID, {fresh_ids.first()}},
|
||||
{SPV_OPERAND_TYPE_ID, {index_instruction->result_id()}},
|
||||
{SPV_OPERAND_TYPE_ID, {bound_minus_one_id}}})));
|
||||
|
||||
new_index_id = fresh_ids.second();
|
||||
|
||||
index_value = 0;
|
||||
}
|
||||
|
||||
// Add the correct index id to the operands.
|
||||
operands.push_back({SPV_OPERAND_TYPE_ID, {new_index_id}});
|
||||
|
||||
// Walk to the next type in the composite object using this index.
|
||||
subobject_type_id = fuzzerutil::WalkOneCompositeTypeIndex(
|
||||
ir_context, subobject_type_id, index_value);
|
||||
}
|
||||
// The access chain's result type is a pointer to the composite component that
|
||||
// was reached after following all indices. The storage class is that of the
|
||||
// original pointer.
|
||||
// The access chain's result type is a pointer to the composite component
|
||||
// that was reached after following all indices. The storage class is that
|
||||
// of the original pointer.
|
||||
uint32_t result_type = fuzzerutil::MaybeGetPointerType(
|
||||
ir_context, subobject_type_id,
|
||||
static_cast<SpvStorageClass>(pointer_type->GetSingleWordInOperand(0)));
|
||||
|
||||
// Add the access chain instruction to the module, and update the module's id
|
||||
// bound.
|
||||
// Add the access chain instruction to the module, and update the module's
|
||||
// id bound.
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
|
||||
FindInstruction(message_.instruction_to_insert_before(), ir_context)
|
||||
->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
@ -180,8 +334,8 @@ void TransformationAccessChain::Apply(
|
||||
// Conservatively invalidate all analyses.
|
||||
ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
|
||||
|
||||
// If the base pointer's pointee value was irrelevant, the same is true of the
|
||||
// pointee value of the result of this access chain.
|
||||
// If the base pointer's pointee value was irrelevant, the same is true of
|
||||
// the pointee value of the result of this access chain.
|
||||
if (transformation_context->GetFactManager()->PointeeValueIsIrrelevant(
|
||||
message_.pointer_id())) {
|
||||
transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
|
||||
@ -196,21 +350,61 @@ protobufs::Transformation TransformationAccessChain::ToMessage() const {
|
||||
}
|
||||
|
||||
std::pair<bool, uint32_t> TransformationAccessChain::GetIndexValue(
|
||||
opt::IRContext* ir_context, uint32_t index_id) const {
|
||||
auto index_instruction = ir_context->get_def_use_mgr()->GetDef(index_id);
|
||||
if (!index_instruction || !spvOpcodeIsConstant(index_instruction->opcode())) {
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3179) We could
|
||||
// allow non-constant indices when looking up non-structs, using clamping
|
||||
// to ensure they are in-bounds.
|
||||
opt::IRContext* ir_context, uint32_t index_id,
|
||||
uint32_t object_type_id) const {
|
||||
if (!ValidIndexToComposite(ir_context, index_id, object_type_id)) {
|
||||
return {false, 0};
|
||||
}
|
||||
auto index_instruction = ir_context->get_def_use_mgr()->GetDef(index_id);
|
||||
|
||||
uint32_t bound = fuzzerutil::GetBoundForCompositeIndex(
|
||||
*ir_context->get_def_use_mgr()->GetDef(object_type_id), ir_context);
|
||||
|
||||
// The index must be a constant
|
||||
if (!spvOpcodeIsConstant(index_instruction->opcode())) {
|
||||
return {false, 0};
|
||||
}
|
||||
|
||||
// The index must be in bounds.
|
||||
uint32_t value = index_instruction->GetSingleWordInOperand(0);
|
||||
|
||||
if (value >= bound) {
|
||||
return {false, 0};
|
||||
}
|
||||
|
||||
return {true, value};
|
||||
}
|
||||
|
||||
bool TransformationAccessChain::ValidIndexToComposite(
|
||||
opt::IRContext* ir_context, uint32_t index_id, uint32_t object_type_id) {
|
||||
auto object_type_def = ir_context->get_def_use_mgr()->GetDef(object_type_id);
|
||||
// The object being indexed must be a composite.
|
||||
if (!spvOpcodeIsComposite(object_type_def->opcode())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the defining instruction of the index.
|
||||
auto index_instruction = ir_context->get_def_use_mgr()->GetDef(index_id);
|
||||
if (!index_instruction) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The index type must be 32-bit integer.
|
||||
auto index_type =
|
||||
ir_context->get_def_use_mgr()->GetDef(index_instruction->type_id());
|
||||
if (index_type->opcode() != SpvOpTypeInt ||
|
||||
index_type->GetSingleWordInOperand(0) != 32) {
|
||||
return {false, 0};
|
||||
return false;
|
||||
}
|
||||
return {true, index_instruction->GetSingleWordInOperand(0)};
|
||||
|
||||
// If the object being traversed is a struct, the id must correspond to an
|
||||
// in-bound constant.
|
||||
if (object_type_def->opcode() == SpvOpTypeStruct) {
|
||||
if (!spvOpcodeIsConstant(index_instruction->opcode())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
|
@ -33,20 +33,28 @@ class TransformationAccessChain : public Transformation {
|
||||
TransformationAccessChain(
|
||||
uint32_t fresh_id, uint32_t pointer_id,
|
||||
const std::vector<uint32_t>& index_id,
|
||||
const protobufs::InstructionDescriptor& instruction_to_insert_before);
|
||||
const protobufs::InstructionDescriptor& instruction_to_insert_before,
|
||||
const std::vector<std::pair<uint32_t, uint32_t>>& fresh_ids_for_clamping =
|
||||
{});
|
||||
|
||||
// - |message_.fresh_id| must be fresh
|
||||
// - |message_.fresh_id| must be fresh.
|
||||
// - |message_.instruction_to_insert_before| must identify an instruction
|
||||
// before which it is legitimate to insert an OpAccessChain instruction
|
||||
// before which it is legitimate to insert an OpAccessChain instruction.
|
||||
// - |message_.pointer_id| must be a result id with pointer type that is
|
||||
// available (according to dominance rules) at the insertion point.
|
||||
// - The pointer must not be OpConstantNull or OpUndef
|
||||
// - |message_.index_id| must be a sequence of ids of 32-bit integer constants
|
||||
// - The pointer must not be OpConstantNull or OpUndef.
|
||||
// - |message_.index_id| must be a sequence of ids of 32-bit integers
|
||||
// such that it is possible to walk the pointee type of
|
||||
// |message_.pointer_id| using these indices, remaining in-bounds.
|
||||
// |message_.pointer_id| using these indices.
|
||||
// - All indices used to access a struct must be OpConstant.
|
||||
// - The indices used to index non-struct composites will be clamped to be
|
||||
// in bound. Enough fresh ids must be given in
|
||||
// |message_.fresh_id_for_clamping| to perform clamping (2 for
|
||||
// each index accessing a non-struct). This requires the bool type and
|
||||
// a constant of value (bound - 1) to be declared in the module.
|
||||
// - If type t is the final type reached by walking these indices, the module
|
||||
// must include an instruction "OpTypePointer SC %t" where SC is the storage
|
||||
// class associated with |message_.pointer_id|
|
||||
// class associated with |message_.pointer_id|.
|
||||
bool IsApplicable(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const override;
|
||||
@ -58,6 +66,9 @@ class TransformationAccessChain : public Transformation {
|
||||
// the indices in |message_.index_id|, and with the same storage class as
|
||||
// |message_.pointer_id|.
|
||||
//
|
||||
// For each of the indices traversing non-struct composites, two clamping
|
||||
// instructions are added using ids in |message_.fresh_id_for_clamping|.
|
||||
//
|
||||
// If the fact manager in |transformation_context| reports that
|
||||
// |message_.pointer_id| has an irrelevant pointee value, then the fact that
|
||||
// |message_.fresh_id| (the result of the access chain) also has an irrelevant
|
||||
@ -68,11 +79,21 @@ class TransformationAccessChain : public Transformation {
|
||||
protobufs::Transformation ToMessage() const override;
|
||||
|
||||
private:
|
||||
// Returns {false, 0} if |index_id| does not correspond to a 32-bit integer
|
||||
// constant. Otherwise, returns {true, value}, where value is the value of
|
||||
// the 32-bit integer constant to which |index_id| corresponds.
|
||||
// Returns {false, 0} in each of the following cases:
|
||||
// - |index_id| does not correspond to a 32-bit integer constant
|
||||
// - the object being indexed is not a composite type
|
||||
// - the constant at |index_id| is out of bounds.
|
||||
// Otherwise, returns {true, value}, where value is the value of the constant
|
||||
// at |index_id|.
|
||||
std::pair<bool, uint32_t> GetIndexValue(opt::IRContext* ir_context,
|
||||
uint32_t index_id) const;
|
||||
uint32_t index_id,
|
||||
uint32_t object_type_id) const;
|
||||
|
||||
// Returns true if |index_id| corresponds, in the given context, to a 32-bit
|
||||
// integer which can be used to index an object of the type specified by
|
||||
// |object_type_id|. Returns false otherwise.
|
||||
static bool ValidIndexToComposite(opt::IRContext* ir_context,
|
||||
uint32_t index_id, uint32_t object_type_id);
|
||||
|
||||
protobufs::TransformationAccessChain message_;
|
||||
};
|
||||
|
@ -488,7 +488,20 @@ bool TransformationAddFunction::TryToAddLoopLimiters(
|
||||
// move on from this loop.
|
||||
continue;
|
||||
}
|
||||
auto back_edge_block = ir_context->cfg()->block(back_edge_block_id);
|
||||
|
||||
// If the loop's merge block is unreachable, then there are no constraints
|
||||
// on where the merge block appears in relation to the blocks of the loop.
|
||||
// This means we need to be careful when adding a branch from the back-edge
|
||||
// block to the merge block: the branch might make the loop merge reachable,
|
||||
// and it might then be dominated by the loop header and possibly by other
|
||||
// blocks in the loop. Since a block needs to appear before those blocks it
|
||||
// strictly dominates, this could make the module invalid. To avoid this
|
||||
// problem we bail out in the case where the loop header does not dominate
|
||||
// the loop merge.
|
||||
if (!ir_context->GetDominatorAnalysis(added_function)
|
||||
->Dominates(loop_header->id(), loop_header->MergeBlockId())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Go through the sequence of loop limiter infos and find the one
|
||||
// corresponding to this loop.
|
||||
@ -560,6 +573,7 @@ bool TransformationAddFunction::TryToAddLoopLimiters(
|
||||
// %t4 = OpLogicalOr %bool %c %t3
|
||||
// OpBranchConditional %t4 %loop_merge %loop_header
|
||||
|
||||
auto back_edge_block = ir_context->cfg()->block(back_edge_block_id);
|
||||
auto back_edge_block_terminator = back_edge_block->terminator();
|
||||
bool compare_using_greater_than_equal;
|
||||
if (back_edge_block_terminator->opcode() == SpvOpBranch) {
|
||||
@ -675,10 +689,6 @@ bool TransformationAddFunction::TryToAddLoopLimiters(
|
||||
}
|
||||
|
||||
// Add the new edge, by changing OpBranch to OpBranchConditional.
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3162): This
|
||||
// could be a problem if the merge block was originally unreachable: it
|
||||
// might now be dominated by other blocks that it appears earlier than in
|
||||
// the module.
|
||||
back_edge_block_terminator->SetOpcode(SpvOpBranchConditional);
|
||||
back_edge_block_terminator->SetInOperands(opt::Instruction::OperandList(
|
||||
{{SPV_OPERAND_TYPE_ID, {loop_limiter_info.compare_id()}},
|
||||
@ -794,8 +804,8 @@ bool TransformationAddFunction::TryToClampAccessChainIndices(
|
||||
|
||||
// Get the bound for the composite being indexed into; e.g. the number of
|
||||
// columns of matrix or the size of an array.
|
||||
uint32_t bound =
|
||||
GetBoundForCompositeIndex(ir_context, *should_be_composite_type);
|
||||
uint32_t bound = fuzzerutil::GetBoundForCompositeIndex(
|
||||
*should_be_composite_type, ir_context);
|
||||
|
||||
// Get the instruction associated with the index and figure out its integer
|
||||
// type.
|
||||
@ -873,28 +883,6 @@ bool TransformationAddFunction::TryToClampAccessChainIndices(
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t TransformationAddFunction::GetBoundForCompositeIndex(
|
||||
opt::IRContext* ir_context, const opt::Instruction& composite_type_inst) {
|
||||
switch (composite_type_inst.opcode()) {
|
||||
case SpvOpTypeArray:
|
||||
return fuzzerutil::GetArraySize(composite_type_inst, ir_context);
|
||||
case SpvOpTypeMatrix:
|
||||
case SpvOpTypeVector:
|
||||
return composite_type_inst.GetSingleWordInOperand(1);
|
||||
case SpvOpTypeStruct: {
|
||||
return fuzzerutil::GetNumberOfStructMembers(composite_type_inst);
|
||||
}
|
||||
case SpvOpTypeRuntimeArray:
|
||||
assert(false &&
|
||||
"GetBoundForCompositeIndex should not be invoked with an "
|
||||
"OpTypeRuntimeArray, which does not have a static bound.");
|
||||
return 0;
|
||||
default:
|
||||
assert(false && "Unknown composite type.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
opt::Instruction* TransformationAddFunction::FollowCompositeIndex(
|
||||
opt::IRContext* ir_context, const opt::Instruction& composite_type_inst,
|
||||
uint32_t index_id) {
|
||||
|
@ -58,13 +58,6 @@ class TransformationAddFunction : public Transformation {
|
||||
|
||||
protobufs::Transformation ToMessage() const override;
|
||||
|
||||
// Helper method that returns the bound for indexing into a composite of type
|
||||
// |composite_type_inst|, i.e. the number of fields of a struct, the size of
|
||||
// an array, the number of components of a vector, or the number of columns of
|
||||
// a matrix.
|
||||
static uint32_t GetBoundForCompositeIndex(
|
||||
opt::IRContext* ir_context, const opt::Instruction& composite_type_inst);
|
||||
|
||||
// Helper method that, given composite type |composite_type_inst|, returns the
|
||||
// type of the sub-object at index |index_id|, which is required to be in-
|
||||
// bounds.
|
||||
|
227
3rdparty/spirv-tools/source/fuzz/transformation_add_loop_preheader.cpp
vendored
Normal file
227
3rdparty/spirv-tools/source/fuzz/transformation_add_loop_preheader.cpp
vendored
Normal file
@ -0,0 +1,227 @@
|
||||
// Copyright (c) 2020 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 "transformation_add_loop_preheader.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/opt/instruction.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
TransformationAddLoopPreheader::TransformationAddLoopPreheader(
|
||||
const protobufs::TransformationAddLoopPreheader& message)
|
||||
: message_(message) {}
|
||||
|
||||
TransformationAddLoopPreheader::TransformationAddLoopPreheader(
|
||||
uint32_t loop_header_block, uint32_t fresh_id,
|
||||
std::vector<uint32_t> phi_id) {
|
||||
message_.set_loop_header_block(loop_header_block);
|
||||
message_.set_fresh_id(fresh_id);
|
||||
for (auto id : phi_id) {
|
||||
message_.add_phi_id(id);
|
||||
}
|
||||
}
|
||||
|
||||
bool TransformationAddLoopPreheader::IsApplicable(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& /* unused */) const {
|
||||
// |message_.loop_header_block()| must be the id of a loop header block.
|
||||
opt::BasicBlock* loop_header_block =
|
||||
fuzzerutil::MaybeFindBlock(ir_context, message_.loop_header_block());
|
||||
if (!loop_header_block || !loop_header_block->IsLoopHeader()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The id for the preheader must actually be fresh.
|
||||
std::set<uint32_t> used_ids;
|
||||
if (!CheckIdIsFreshAndNotUsedByThisTransformation(message_.fresh_id(),
|
||||
ir_context, &used_ids)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t num_predecessors =
|
||||
ir_context->cfg()->preds(message_.loop_header_block()).size();
|
||||
|
||||
// The block must have at least 2 predecessors (the back-edge block and
|
||||
// another predecessor outside of the loop)
|
||||
if (num_predecessors < 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the block only has one predecessor outside of the loop (and thus 2 in
|
||||
// total), then no additional fresh ids are necessary.
|
||||
if (num_predecessors == 2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Count the number of OpPhi instructions.
|
||||
int32_t num_phi_insts = 0;
|
||||
loop_header_block->ForEachPhiInst(
|
||||
[&num_phi_insts](opt::Instruction* /* unused */) { num_phi_insts++; });
|
||||
|
||||
// There must be enough fresh ids for the OpPhi instructions.
|
||||
if (num_phi_insts > message_.phi_id_size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that the needed ids are fresh and distinct.
|
||||
for (int32_t i = 0; i < num_phi_insts; i++) {
|
||||
if (!CheckIdIsFreshAndNotUsedByThisTransformation(message_.phi_id(i),
|
||||
ir_context, &used_ids)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TransformationAddLoopPreheader::Apply(
|
||||
opt::IRContext* ir_context,
|
||||
TransformationContext* /* transformation_context */) const {
|
||||
// Find the loop header.
|
||||
opt::BasicBlock* loop_header =
|
||||
fuzzerutil::MaybeFindBlock(ir_context, message_.loop_header_block());
|
||||
|
||||
auto dominator_analysis =
|
||||
ir_context->GetDominatorAnalysis(loop_header->GetParent());
|
||||
|
||||
uint32_t back_edge_block_id = 0;
|
||||
|
||||
// Update the branching instructions of the out-of-loop predecessors of the
|
||||
// header. Set |back_edge_block_id| to be the id of the back-edge block.
|
||||
ir_context->get_def_use_mgr()->ForEachUse(
|
||||
loop_header->id(),
|
||||
[this, &ir_context, &dominator_analysis, &loop_header,
|
||||
&back_edge_block_id](opt::Instruction* use_inst, uint32_t use_index) {
|
||||
|
||||
if (dominator_analysis->Dominates(loop_header->GetLabelInst(),
|
||||
use_inst)) {
|
||||
// If |use_inst| is a branch instruction dominated by the header, the
|
||||
// block containing it is the back-edge block.
|
||||
if (use_inst->IsBranch()) {
|
||||
assert(back_edge_block_id == 0 &&
|
||||
"There should only be one back-edge block");
|
||||
back_edge_block_id = ir_context->get_instr_block(use_inst)->id();
|
||||
}
|
||||
// References to the header inside the loop should not be updated
|
||||
return;
|
||||
}
|
||||
|
||||
// If |use_inst| is not a branch or merge instruction, it should not be
|
||||
// changed.
|
||||
if (!use_inst->IsBranch() &&
|
||||
use_inst->opcode() != SpvOpSelectionMerge &&
|
||||
use_inst->opcode() != SpvOpLoopMerge) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the reference.
|
||||
use_inst->SetOperand(use_index, {message_.fresh_id()});
|
||||
});
|
||||
|
||||
assert(back_edge_block_id && "The back-edge block should have been found");
|
||||
|
||||
// Make a new block for the preheader.
|
||||
std::unique_ptr<opt::BasicBlock> preheader = MakeUnique<opt::BasicBlock>(
|
||||
std::unique_ptr<opt::Instruction>(new opt::Instruction(
|
||||
ir_context, SpvOpLabel, 0, message_.fresh_id(), {})));
|
||||
|
||||
uint32_t phi_ids_used = 0;
|
||||
|
||||
// Update the OpPhi instructions and, if there is more than one out-of-loop
|
||||
// predecessor, add necessary OpPhi instructions so the preheader.
|
||||
loop_header->ForEachPhiInst([this, &ir_context, &preheader,
|
||||
&back_edge_block_id,
|
||||
&phi_ids_used](opt::Instruction* phi_inst) {
|
||||
|
||||
// The loop header must have at least 2 incoming edges (the back edge, and
|
||||
// at least one from outside the loop).
|
||||
assert(phi_inst->NumInOperands() >= 4);
|
||||
|
||||
if (phi_inst->NumInOperands() == 4) {
|
||||
// There is just one out-of-loop predecessor, so no additional
|
||||
// instructions in the preheader are necessary. The reference to the
|
||||
// original out-of-loop predecessor needs to be updated so that it refers
|
||||
// to the preheader.
|
||||
uint32_t index_of_out_of_loop_pred_id =
|
||||
phi_inst->GetInOperand(1).words[0] == back_edge_block_id ? 3 : 1;
|
||||
phi_inst->SetInOperand(index_of_out_of_loop_pred_id, {preheader->id()});
|
||||
} else {
|
||||
// There is more than one out-of-loop predecessor, so an OpPhi instruction
|
||||
// needs to be added to the preheader, and its value will depend on all
|
||||
// the current out-of-loop predecessors of the header.
|
||||
|
||||
// Get the operand list and the value corresponding to the back-edge
|
||||
// block.
|
||||
std::vector<opt::Operand> preheader_in_operands;
|
||||
uint32_t back_edge_val = 0;
|
||||
|
||||
for (uint32_t i = 0; i < phi_inst->NumInOperands(); i += 2) {
|
||||
// Only add operands if they don't refer to the back-edge block.
|
||||
if (phi_inst->GetInOperand(i + 1).words[0] == back_edge_block_id) {
|
||||
back_edge_val = phi_inst->GetInOperand(i).words[0];
|
||||
} else {
|
||||
preheader_in_operands.push_back(std::move(phi_inst->GetInOperand(i)));
|
||||
preheader_in_operands.push_back(
|
||||
std::move(phi_inst->GetInOperand(i + 1)));
|
||||
}
|
||||
}
|
||||
|
||||
// Add the new instruction to the preheader.
|
||||
uint32_t fresh_phi_id = message_.phi_id(phi_ids_used++);
|
||||
|
||||
// Update id bound.
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, fresh_phi_id);
|
||||
|
||||
preheader->AddInstruction(std::unique_ptr<opt::Instruction>(
|
||||
new opt::Instruction(ir_context, SpvOpPhi, phi_inst->type_id(),
|
||||
fresh_phi_id, preheader_in_operands)));
|
||||
|
||||
// Update the OpPhi instruction in the header so that it refers to the
|
||||
// back edge block and the preheader as the predecessors, and it uses the
|
||||
// newly-defined OpPhi in the preheader for the corresponding value.
|
||||
phi_inst->SetInOperands(
|
||||
{{SPV_OPERAND_TYPE_RESULT_ID, {fresh_phi_id}},
|
||||
{SPV_OPERAND_TYPE_RESULT_ID, {preheader->id()}},
|
||||
{SPV_OPERAND_TYPE_RESULT_ID, {back_edge_val}},
|
||||
{SPV_OPERAND_TYPE_RESULT_ID, {back_edge_block_id}}});
|
||||
}
|
||||
});
|
||||
|
||||
// Update id bound.
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
|
||||
|
||||
// Add an unconditional branch from the preheader to the header.
|
||||
preheader->AddInstruction(std::unique_ptr<opt::Instruction>(
|
||||
new opt::Instruction(ir_context, SpvOpBranch, 0, 0,
|
||||
std::initializer_list<opt::Operand>{opt::Operand(
|
||||
spv_operand_type_t::SPV_OPERAND_TYPE_RESULT_ID,
|
||||
{loop_header->id()})})));
|
||||
|
||||
// Insert the preheader in the module.
|
||||
loop_header->GetParent()->InsertBasicBlockBefore(std::move(preheader),
|
||||
loop_header);
|
||||
|
||||
// Invalidate analyses because the structure of the program changed.
|
||||
ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
|
||||
}
|
||||
|
||||
protobufs::Transformation TransformationAddLoopPreheader::ToMessage() const {
|
||||
protobufs::Transformation result;
|
||||
*result.mutable_add_loop_preheader() = message_;
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
57
3rdparty/spirv-tools/source/fuzz/transformation_add_loop_preheader.h
vendored
Normal file
57
3rdparty/spirv-tools/source/fuzz/transformation_add_loop_preheader.h
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
// Copyright (c) 2020 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 SOURCE_FUZZ_TRANSFORMATION_ADD_LOOP_PREHEADER_H
|
||||
#define SOURCE_FUZZ_TRANSFORMATION_ADD_LOOP_PREHEADER_H
|
||||
|
||||
#include "source/fuzz/transformation.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
class TransformationAddLoopPreheader : public Transformation {
|
||||
public:
|
||||
explicit TransformationAddLoopPreheader(
|
||||
const protobufs::TransformationAddLoopPreheader& message);
|
||||
|
||||
TransformationAddLoopPreheader(uint32_t loop_header_block, uint32_t fresh_id,
|
||||
std::vector<uint32_t> phi_id);
|
||||
|
||||
// - |message_.loop_header_block| must be the id of a loop header block in
|
||||
// the given module.
|
||||
// - |message_.fresh_id| must be an available id.
|
||||
// - |message_.phi_ids| must be a list of available ids.
|
||||
// It can be empty if the loop header only has one predecessor outside of
|
||||
// the loop. Otherwise, it must contain at least as many ids as OpPhi
|
||||
// instructions in the loop header block.
|
||||
bool IsApplicable(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const override;
|
||||
|
||||
// Adds a preheader block as the unique out-of-loop predecessor of the given
|
||||
// loop header block. All of the existing out-of-loop predecessors of the
|
||||
// header are changed so that they branch to the preheader instead.
|
||||
void Apply(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const override;
|
||||
|
||||
protobufs::Transformation ToMessage() const override;
|
||||
|
||||
private:
|
||||
protobufs::TransformationAddLoopPreheader message_;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_LOOP_PREHEADER_H
|
@ -21,20 +21,6 @@
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
namespace {
|
||||
|
||||
std::map<uint32_t, uint32_t> PairSequenceToMap(
|
||||
const google::protobuf::RepeatedPtrField<protobufs::UInt32Pair>&
|
||||
pair_sequence) {
|
||||
std::map<uint32_t, uint32_t> result;
|
||||
for (auto& pair : pair_sequence) {
|
||||
result[pair.first()] = pair.second();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TransformationOutlineFunction::TransformationOutlineFunction(
|
||||
const spvtools::fuzz::protobufs::TransformationOutlineFunction& message)
|
||||
: message_(message) {}
|
||||
@ -55,18 +41,10 @@ TransformationOutlineFunction::TransformationOutlineFunction(
|
||||
message_.set_new_function_region_entry_block(new_function_region_entry_block);
|
||||
message_.set_new_caller_result_id(new_caller_result_id);
|
||||
message_.set_new_callee_result_id(new_callee_result_id);
|
||||
for (auto& entry : input_id_to_fresh_id) {
|
||||
protobufs::UInt32Pair pair;
|
||||
pair.set_first(entry.first);
|
||||
pair.set_second(entry.second);
|
||||
*message_.add_input_id_to_fresh_id() = pair;
|
||||
}
|
||||
for (auto& entry : output_id_to_fresh_id) {
|
||||
protobufs::UInt32Pair pair;
|
||||
pair.set_first(entry.first);
|
||||
pair.set_second(entry.second);
|
||||
*message_.add_output_id_to_fresh_id() = pair;
|
||||
}
|
||||
*message_.mutable_input_id_to_fresh_id() =
|
||||
fuzzerutil::MapToRepeatedUInt32Pair(input_id_to_fresh_id);
|
||||
*message_.mutable_output_id_to_fresh_id() =
|
||||
fuzzerutil::MapToRepeatedUInt32Pair(output_id_to_fresh_id);
|
||||
}
|
||||
|
||||
bool TransformationOutlineFunction::IsApplicable(
|
||||
@ -252,8 +230,8 @@ bool TransformationOutlineFunction::IsApplicable(
|
||||
|
||||
// For each region input id, i.e. every id defined outside the region but
|
||||
// used inside the region, ...
|
||||
std::map<uint32_t, uint32_t> input_id_to_fresh_id_map =
|
||||
PairSequenceToMap(message_.input_id_to_fresh_id());
|
||||
auto input_id_to_fresh_id_map =
|
||||
fuzzerutil::RepeatedUInt32PairToMap(message_.input_id_to_fresh_id());
|
||||
for (auto id : GetRegionInputIds(ir_context, region_set, exit_block)) {
|
||||
// There needs to be a corresponding fresh id to be used as a function
|
||||
// parameter.
|
||||
@ -280,8 +258,8 @@ bool TransformationOutlineFunction::IsApplicable(
|
||||
|
||||
// For each region output id -- i.e. every id defined inside the region but
|
||||
// used outside the region, ...
|
||||
std::map<uint32_t, uint32_t> output_id_to_fresh_id_map =
|
||||
PairSequenceToMap(message_.output_id_to_fresh_id());
|
||||
auto output_id_to_fresh_id_map =
|
||||
fuzzerutil::RepeatedUInt32PairToMap(message_.output_id_to_fresh_id());
|
||||
for (auto id : GetRegionOutputIds(ir_context, region_set, exit_block)) {
|
||||
if (
|
||||
// ... there needs to be a corresponding fresh id that can hold the
|
||||
@ -323,10 +301,10 @@ void TransformationOutlineFunction::Apply(
|
||||
GetRegionOutputIds(ir_context, region_blocks, original_region_exit_block);
|
||||
|
||||
// Maps from input and output ids to fresh ids.
|
||||
std::map<uint32_t, uint32_t> input_id_to_fresh_id_map =
|
||||
PairSequenceToMap(message_.input_id_to_fresh_id());
|
||||
std::map<uint32_t, uint32_t> output_id_to_fresh_id_map =
|
||||
PairSequenceToMap(message_.output_id_to_fresh_id());
|
||||
auto input_id_to_fresh_id_map =
|
||||
fuzzerutil::RepeatedUInt32PairToMap(message_.input_id_to_fresh_id());
|
||||
auto output_id_to_fresh_id_map =
|
||||
fuzzerutil::RepeatedUInt32PairToMap(message_.output_id_to_fresh_id());
|
||||
|
||||
UpdateModuleIdBoundForFreshIds(ir_context, input_id_to_fresh_id_map,
|
||||
output_id_to_fresh_id_map);
|
||||
|
@ -73,7 +73,7 @@ class TransformationOutlineFunction : public Transformation {
|
||||
// - Unless the type required for the new function is already known,
|
||||
// |message_.new_function_type_id| is used as the type id for a new function
|
||||
// type, and the new function uses this type.
|
||||
// - The new function starts with a dummy block with id
|
||||
// - The new function starts with a placeholder block with id
|
||||
// |message_.new_function_first_block|, which jumps straight to a successor
|
||||
// block, to avoid violating rules on what the first block in a function may
|
||||
// look like.
|
||||
|
@ -15,6 +15,8 @@
|
||||
|
||||
#include "transformation_record_synonymous_constants.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
@ -76,19 +78,17 @@ bool TransformationRecordSynonymousConstants::AreEquivalentConstants(
|
||||
return false;
|
||||
}
|
||||
|
||||
// The type ids must be the same
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3536): Somehow
|
||||
// relax this for integers (so that unsigned integer and signed integer are
|
||||
// considered the same type)
|
||||
if (def_1->type_id() != def_2->type_id()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto constant1 = ir_context->get_constant_mgr()->GetConstantFromInst(def_1);
|
||||
auto constant2 = ir_context->get_constant_mgr()->GetConstantFromInst(def_2);
|
||||
|
||||
assert(constant1 && constant2 && "The ids must refer to constants.");
|
||||
|
||||
// The types must be compatible.
|
||||
if (!fuzzerutil::TypesAreEqualUpToSign(ir_context, def_1->type_id(),
|
||||
def_2->type_id())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If either constant is null, the other is equivalent iff it is zero-like
|
||||
if (constant1->AsNullConstant()) {
|
||||
return constant2->IsZero();
|
||||
|
@ -32,16 +32,17 @@ class TransformationRecordSynonymousConstants : public Transformation {
|
||||
// - |message_.constant_id| and |message_.synonym_id| are distinct ids
|
||||
// of constants
|
||||
// - |message_.constant_id| and |message_.synonym_id| refer to constants
|
||||
// that are equal or equivalent.
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3536): Signed and
|
||||
// unsigned integers are currently considered non-equivalent
|
||||
// Two integers with the same width and value are equal, even if one is
|
||||
// signed and the other is not.
|
||||
// Constants are equivalent:
|
||||
// - if both of them represent zero-like values of the same type
|
||||
// - if they are composite constants with the same type and their
|
||||
// components are pairwise equivalent.
|
||||
// - |constant1_id| and |constant2_id| may not be irrelevant.
|
||||
// that are equivalent.
|
||||
// Constants are equivalent if at least one of the following holds:
|
||||
// - they are equal (i.e. they have the same type ids and equal values)
|
||||
// - both of them represent zero-like values of compatible types
|
||||
// - they are composite constants with compatible types and their
|
||||
// components are pairwise equivalent
|
||||
// Two types are compatible if at least one of the following holds:
|
||||
// - they have the same id
|
||||
// - they are integer scalar types with the same width
|
||||
// - they are integer vectors and their components have the same width
|
||||
// (this is always the case if the components are equivalent)
|
||||
bool IsApplicable(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const override;
|
||||
|
@ -57,6 +57,21 @@ bool TransformationReplaceIdWithSynonym::IsApplicable(
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t type_id_of_interest =
|
||||
ir_context->get_def_use_mgr()->GetDef(id_of_interest)->type_id();
|
||||
uint32_t type_id_synonym = ir_context->get_def_use_mgr()
|
||||
->GetDef(message_.synonymous_id())
|
||||
->type_id();
|
||||
|
||||
// If the id of interest and the synonym are scalar or vector integer
|
||||
// constants with different signedness, their use can only be swapped if the
|
||||
// instruction is agnostic to the signedness of the operand.
|
||||
if (!TypesAreCompatible(ir_context, use_instruction->opcode(),
|
||||
message_.id_use_descriptor().in_operand_index(),
|
||||
type_id_of_interest, type_id_synonym)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Is the use suitable for being replaced in principle?
|
||||
if (!UseCanBeReplacedWithSynonym(
|
||||
ir_context, use_instruction,
|
||||
@ -182,5 +197,58 @@ bool TransformationReplaceIdWithSynonym::UseCanBeReplacedWithSynonym(
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3582): Add all
|
||||
// opcodes that are agnostic to signedness of operands to function.
|
||||
// This is not exhaustive yet.
|
||||
bool TransformationReplaceIdWithSynonym::IsAgnosticToSignednessOfOperand(
|
||||
SpvOp opcode, uint32_t use_in_operand_index) {
|
||||
switch (opcode) {
|
||||
case SpvOpSNegate:
|
||||
case SpvOpNot:
|
||||
case SpvOpIAdd:
|
||||
case SpvOpISub:
|
||||
case SpvOpIMul:
|
||||
case SpvOpSDiv:
|
||||
case SpvOpSRem:
|
||||
case SpvOpSMod:
|
||||
case SpvOpShiftRightLogical:
|
||||
case SpvOpShiftRightArithmetic:
|
||||
case SpvOpShiftLeftLogical:
|
||||
case SpvOpBitwiseOr:
|
||||
case SpvOpBitwiseXor:
|
||||
case SpvOpBitwiseAnd:
|
||||
case SpvOpIEqual:
|
||||
case SpvOpINotEqual:
|
||||
case SpvOpULessThan:
|
||||
case SpvOpSLessThan:
|
||||
case SpvOpUGreaterThan:
|
||||
case SpvOpSGreaterThan:
|
||||
case SpvOpULessThanEqual:
|
||||
case SpvOpSLessThanEqual:
|
||||
case SpvOpUGreaterThanEqual:
|
||||
case SpvOpSGreaterThanEqual:
|
||||
return true;
|
||||
case SpvOpAccessChain:
|
||||
// The signedness of indices does not matter.
|
||||
return use_in_operand_index > 0;
|
||||
default:
|
||||
// Conservatively assume that the id cannot be swapped in other
|
||||
// instructions.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool TransformationReplaceIdWithSynonym::TypesAreCompatible(
|
||||
opt::IRContext* ir_context, SpvOp opcode, uint32_t use_in_operand_index,
|
||||
uint32_t type_id_1, uint32_t type_id_2) {
|
||||
assert(ir_context->get_type_mgr()->GetType(type_id_1) &&
|
||||
ir_context->get_type_mgr()->GetType(type_id_2) &&
|
||||
"Type ids are invalid");
|
||||
|
||||
return type_id_1 == type_id_2 ||
|
||||
(IsAgnosticToSignednessOfOperand(opcode, use_in_operand_index) &&
|
||||
fuzzerutil::TypesAreEqualUpToSign(ir_context, type_id_1, type_id_2));
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
@ -64,7 +64,22 @@ class TransformationReplaceIdWithSynonym : public Transformation {
|
||||
opt::Instruction* use_instruction,
|
||||
uint32_t use_in_operand_index);
|
||||
|
||||
// Returns true if |type_id_1| and |type_id_2| represent compatible types
|
||||
// given the context of the instruction with |opcode| (i.e. we can replace
|
||||
// an operand of |opcode| of the first type with an id of the second type
|
||||
// and vice-versa).
|
||||
static bool TypesAreCompatible(opt::IRContext* ir_context, SpvOp opcode,
|
||||
uint32_t use_in_operand_index,
|
||||
uint32_t type_id_1, uint32_t type_id_2);
|
||||
|
||||
private:
|
||||
// Returns true if the instruction with opcode |opcode| does not change its
|
||||
// behaviour depending on the signedness of the operand at
|
||||
// |use_in_operand_index|.
|
||||
// Assumes that the operand must be the id of an integer scalar or vector.
|
||||
static bool IsAgnosticToSignednessOfOperand(SpvOp opcode,
|
||||
uint32_t use_in_operand_index);
|
||||
|
||||
protobufs::TransformationReplaceIdWithSynonym message_;
|
||||
};
|
||||
|
||||
|
@ -41,16 +41,8 @@ bool TransformationReplaceLinearAlgebraInstruction::IsApplicable(
|
||||
auto instruction =
|
||||
FindInstruction(message_.instruction_descriptor(), ir_context);
|
||||
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3354):
|
||||
// Right now we only support certain operations. When this issue is addressed
|
||||
// the following conditional can use the function |spvOpcodeIsLinearAlgebra|.
|
||||
// It must be a supported linear algebra instruction.
|
||||
if (instruction->opcode() != SpvOpVectorTimesScalar &&
|
||||
instruction->opcode() != SpvOpMatrixTimesScalar &&
|
||||
instruction->opcode() != SpvOpVectorTimesMatrix &&
|
||||
instruction->opcode() != SpvOpMatrixTimesVector &&
|
||||
instruction->opcode() != SpvOpMatrixTimesMatrix &&
|
||||
instruction->opcode() != SpvOpDot) {
|
||||
// It must be a linear algebra instruction.
|
||||
if (!spvOpcodeIsLinearAlgebra(instruction->opcode())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -77,6 +69,9 @@ void TransformationReplaceLinearAlgebraInstruction::Apply(
|
||||
FindInstruction(message_.instruction_descriptor(), ir_context);
|
||||
|
||||
switch (linear_algebra_instruction->opcode()) {
|
||||
case SpvOpTranspose:
|
||||
ReplaceOpTranspose(ir_context, linear_algebra_instruction);
|
||||
break;
|
||||
case SpvOpVectorTimesScalar:
|
||||
ReplaceOpVectorTimesScalar(ir_context, linear_algebra_instruction);
|
||||
break;
|
||||
@ -92,6 +87,9 @@ void TransformationReplaceLinearAlgebraInstruction::Apply(
|
||||
case SpvOpMatrixTimesMatrix:
|
||||
ReplaceOpMatrixTimesMatrix(ir_context, linear_algebra_instruction);
|
||||
break;
|
||||
case SpvOpOuterProduct:
|
||||
ReplaceOpOuterProduct(ir_context, linear_algebra_instruction);
|
||||
break;
|
||||
case SpvOpDot:
|
||||
ReplaceOpDot(ir_context, linear_algebra_instruction);
|
||||
break;
|
||||
@ -115,6 +113,24 @@ uint32_t TransformationReplaceLinearAlgebraInstruction::GetRequiredFreshIdCount(
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3354):
|
||||
// Right now we only support certain operations.
|
||||
switch (instruction->opcode()) {
|
||||
case SpvOpTranspose: {
|
||||
// For each matrix row, |2 * matrix_column_count| OpCompositeExtract and 1
|
||||
// OpCompositeConstruct will be inserted.
|
||||
auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef(
|
||||
instruction->GetSingleWordInOperand(0));
|
||||
uint32_t matrix_column_count =
|
||||
ir_context->get_type_mgr()
|
||||
->GetType(matrix_instruction->type_id())
|
||||
->AsMatrix()
|
||||
->element_count();
|
||||
uint32_t matrix_row_count = ir_context->get_type_mgr()
|
||||
->GetType(matrix_instruction->type_id())
|
||||
->AsMatrix()
|
||||
->element_type()
|
||||
->AsVector()
|
||||
->element_count();
|
||||
return matrix_row_count * (2 * matrix_column_count + 1);
|
||||
}
|
||||
case SpvOpVectorTimesScalar:
|
||||
// For each vector component, 1 OpCompositeExtract and 1 OpFMul will be
|
||||
// inserted.
|
||||
@ -213,6 +229,26 @@ uint32_t TransformationReplaceLinearAlgebraInstruction::GetRequiredFreshIdCount(
|
||||
return matrix_2_column_count *
|
||||
(2 + matrix_1_row_count * (5 * matrix_1_column_count - 1));
|
||||
}
|
||||
case SpvOpOuterProduct: {
|
||||
// For each |vector_2| component, |vector_1_component_count + 1|
|
||||
// OpCompositeExtract, |vector_1_component_count| OpFMul and 1
|
||||
// OpCompositeConstruct instructions will be inserted.
|
||||
auto vector_1_instruction = ir_context->get_def_use_mgr()->GetDef(
|
||||
instruction->GetSingleWordInOperand(0));
|
||||
auto vector_2_instruction = ir_context->get_def_use_mgr()->GetDef(
|
||||
instruction->GetSingleWordInOperand(1));
|
||||
uint32_t vector_1_component_count =
|
||||
ir_context->get_type_mgr()
|
||||
->GetType(vector_1_instruction->type_id())
|
||||
->AsVector()
|
||||
->element_count();
|
||||
uint32_t vector_2_component_count =
|
||||
ir_context->get_type_mgr()
|
||||
->GetType(vector_2_instruction->type_id())
|
||||
->AsVector()
|
||||
->element_count();
|
||||
return 2 * vector_2_component_count * (vector_1_component_count + 1);
|
||||
}
|
||||
case SpvOpDot:
|
||||
// For each pair of vector components, 2 OpCompositeExtract and 1 OpFMul
|
||||
// will be inserted. The first two OpFMul instructions will result the
|
||||
@ -233,6 +269,80 @@ uint32_t TransformationReplaceLinearAlgebraInstruction::GetRequiredFreshIdCount(
|
||||
}
|
||||
}
|
||||
|
||||
void TransformationReplaceLinearAlgebraInstruction::ReplaceOpTranspose(
|
||||
opt::IRContext* ir_context,
|
||||
opt::Instruction* linear_algebra_instruction) const {
|
||||
// Gets OpTranspose instruction information.
|
||||
auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef(
|
||||
linear_algebra_instruction->GetSingleWordInOperand(0));
|
||||
uint32_t matrix_column_count = ir_context->get_type_mgr()
|
||||
->GetType(matrix_instruction->type_id())
|
||||
->AsMatrix()
|
||||
->element_count();
|
||||
auto matrix_column_type = ir_context->get_type_mgr()
|
||||
->GetType(matrix_instruction->type_id())
|
||||
->AsMatrix()
|
||||
->element_type();
|
||||
auto matrix_column_component_type =
|
||||
matrix_column_type->AsVector()->element_type();
|
||||
uint32_t matrix_row_count = matrix_column_type->AsVector()->element_count();
|
||||
auto resulting_matrix_column_type =
|
||||
ir_context->get_type_mgr()
|
||||
->GetType(linear_algebra_instruction->type_id())
|
||||
->AsMatrix()
|
||||
->element_type();
|
||||
|
||||
uint32_t fresh_id_index = 0;
|
||||
std::vector<uint32_t> result_column_ids(matrix_row_count);
|
||||
for (uint32_t i = 0; i < matrix_row_count; i++) {
|
||||
std::vector<uint32_t> column_component_ids(matrix_column_count);
|
||||
for (uint32_t j = 0; j < matrix_column_count; j++) {
|
||||
// Extracts the matrix column.
|
||||
uint32_t matrix_column_id = message_.fresh_ids(fresh_id_index++);
|
||||
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpCompositeExtract,
|
||||
ir_context->get_type_mgr()->GetId(matrix_column_type),
|
||||
matrix_column_id,
|
||||
opt::Instruction::OperandList(
|
||||
{{SPV_OPERAND_TYPE_ID, {matrix_instruction->result_id()}},
|
||||
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {j}}})));
|
||||
|
||||
// Extracts the matrix column component.
|
||||
column_component_ids[j] = message_.fresh_ids(fresh_id_index++);
|
||||
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpCompositeExtract,
|
||||
ir_context->get_type_mgr()->GetId(matrix_column_component_type),
|
||||
column_component_ids[j],
|
||||
opt::Instruction::OperandList(
|
||||
{{SPV_OPERAND_TYPE_ID, {matrix_column_id}},
|
||||
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
|
||||
}
|
||||
|
||||
// Inserts the resulting matrix column.
|
||||
opt::Instruction::OperandList in_operands;
|
||||
for (auto& column_component_id : column_component_ids) {
|
||||
in_operands.push_back({SPV_OPERAND_TYPE_ID, {column_component_id}});
|
||||
}
|
||||
result_column_ids[i] = message_.fresh_ids(fresh_id_index++);
|
||||
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpCompositeConstruct,
|
||||
ir_context->get_type_mgr()->GetId(resulting_matrix_column_type),
|
||||
result_column_ids[i], opt::Instruction::OperandList(in_operands)));
|
||||
}
|
||||
|
||||
// The OpTranspose instruction is changed to an OpCompositeConstruct
|
||||
// instruction.
|
||||
linear_algebra_instruction->SetOpcode(SpvOpCompositeConstruct);
|
||||
linear_algebra_instruction->SetInOperand(0, {result_column_ids[0]});
|
||||
for (uint32_t i = 1; i < result_column_ids.size(); i++) {
|
||||
linear_algebra_instruction->AddOperand(
|
||||
{SPV_OPERAND_TYPE_ID, {result_column_ids[i]}});
|
||||
}
|
||||
|
||||
fuzzerutil::UpdateModuleIdBound(
|
||||
ir_context, message_.fresh_ids(message_.fresh_ids().size() - 1));
|
||||
}
|
||||
|
||||
void TransformationReplaceLinearAlgebraInstruction::ReplaceOpVectorTimesScalar(
|
||||
opt::IRContext* ir_context,
|
||||
opt::Instruction* linear_algebra_instruction) const {
|
||||
@ -740,6 +850,92 @@ void TransformationReplaceLinearAlgebraInstruction::ReplaceOpMatrixTimesMatrix(
|
||||
ir_context, message_.fresh_ids(message_.fresh_ids().size() - 1));
|
||||
}
|
||||
|
||||
void TransformationReplaceLinearAlgebraInstruction::ReplaceOpOuterProduct(
|
||||
opt::IRContext* ir_context,
|
||||
opt::Instruction* linear_algebra_instruction) const {
|
||||
// Gets vector 1 information.
|
||||
auto vector_1_instruction = ir_context->get_def_use_mgr()->GetDef(
|
||||
linear_algebra_instruction->GetSingleWordInOperand(0));
|
||||
uint32_t vector_1_component_count =
|
||||
ir_context->get_type_mgr()
|
||||
->GetType(vector_1_instruction->type_id())
|
||||
->AsVector()
|
||||
->element_count();
|
||||
auto vector_1_component_type = ir_context->get_type_mgr()
|
||||
->GetType(vector_1_instruction->type_id())
|
||||
->AsVector()
|
||||
->element_type();
|
||||
|
||||
// Gets vector 2 information.
|
||||
auto vector_2_instruction = ir_context->get_def_use_mgr()->GetDef(
|
||||
linear_algebra_instruction->GetSingleWordInOperand(1));
|
||||
uint32_t vector_2_component_count =
|
||||
ir_context->get_type_mgr()
|
||||
->GetType(vector_2_instruction->type_id())
|
||||
->AsVector()
|
||||
->element_count();
|
||||
|
||||
uint32_t fresh_id_index = 0;
|
||||
std::vector<uint32_t> result_column_ids(vector_2_component_count);
|
||||
for (uint32_t i = 0; i < vector_2_component_count; i++) {
|
||||
// Extracts |vector_2| component.
|
||||
uint32_t vector_2_component_id = message_.fresh_ids(fresh_id_index++);
|
||||
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpCompositeExtract,
|
||||
ir_context->get_type_mgr()->GetId(vector_1_component_type),
|
||||
vector_2_component_id,
|
||||
opt::Instruction::OperandList(
|
||||
{{SPV_OPERAND_TYPE_ID, {vector_2_instruction->result_id()}},
|
||||
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
|
||||
|
||||
std::vector<uint32_t> column_component_ids(vector_1_component_count);
|
||||
for (uint32_t j = 0; j < vector_1_component_count; j++) {
|
||||
// Extracts |vector_1| component.
|
||||
uint32_t vector_1_component_id = message_.fresh_ids(fresh_id_index++);
|
||||
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpCompositeExtract,
|
||||
ir_context->get_type_mgr()->GetId(vector_1_component_type),
|
||||
vector_1_component_id,
|
||||
opt::Instruction::OperandList(
|
||||
{{SPV_OPERAND_TYPE_ID, {vector_1_instruction->result_id()}},
|
||||
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {j}}})));
|
||||
|
||||
// Multiplies |vector_1| and |vector_2| components.
|
||||
column_component_ids[j] = message_.fresh_ids(fresh_id_index++);
|
||||
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpFMul,
|
||||
ir_context->get_type_mgr()->GetId(vector_1_component_type),
|
||||
column_component_ids[j],
|
||||
opt::Instruction::OperandList(
|
||||
{{SPV_OPERAND_TYPE_ID, {vector_2_component_id}},
|
||||
{SPV_OPERAND_TYPE_ID, {vector_1_component_id}}})));
|
||||
}
|
||||
|
||||
// Inserts the resulting matrix column.
|
||||
opt::Instruction::OperandList in_operands;
|
||||
for (auto& column_component_id : column_component_ids) {
|
||||
in_operands.push_back({SPV_OPERAND_TYPE_ID, {column_component_id}});
|
||||
}
|
||||
result_column_ids[i] = message_.fresh_ids(fresh_id_index++);
|
||||
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpCompositeConstruct, vector_1_instruction->type_id(),
|
||||
result_column_ids[i], in_operands));
|
||||
}
|
||||
|
||||
// The OpOuterProduct instruction is changed to an OpCompositeConstruct
|
||||
// instruction.
|
||||
linear_algebra_instruction->SetOpcode(SpvOpCompositeConstruct);
|
||||
linear_algebra_instruction->SetInOperand(0, {result_column_ids[0]});
|
||||
linear_algebra_instruction->SetInOperand(1, {result_column_ids[1]});
|
||||
for (uint32_t i = 2; i < result_column_ids.size(); i++) {
|
||||
linear_algebra_instruction->AddOperand(
|
||||
{SPV_OPERAND_TYPE_ID, {result_column_ids[i]}});
|
||||
}
|
||||
|
||||
fuzzerutil::UpdateModuleIdBound(
|
||||
ir_context, message_.fresh_ids(message_.fresh_ids().size() - 1));
|
||||
}
|
||||
|
||||
void TransformationReplaceLinearAlgebraInstruction::ReplaceOpDot(
|
||||
opt::IRContext* ir_context,
|
||||
opt::Instruction* linear_algebra_instruction) const {
|
||||
|
@ -52,6 +52,10 @@ class TransformationReplaceLinearAlgebraInstruction : public Transformation {
|
||||
private:
|
||||
protobufs::TransformationReplaceLinearAlgebraInstruction message_;
|
||||
|
||||
// Replaces an OpTranspose instruction.
|
||||
void ReplaceOpTranspose(opt::IRContext* ir_context,
|
||||
opt::Instruction* instruction) const;
|
||||
|
||||
// Replaces an OpVectorTimesScalar instruction.
|
||||
void ReplaceOpVectorTimesScalar(opt::IRContext* ir_context,
|
||||
opt::Instruction* instruction) const;
|
||||
@ -72,6 +76,10 @@ class TransformationReplaceLinearAlgebraInstruction : public Transformation {
|
||||
void ReplaceOpMatrixTimesMatrix(opt::IRContext* ir_context,
|
||||
opt::Instruction* instruction) const;
|
||||
|
||||
// Replaces an OpOuterProduct instruction.
|
||||
void ReplaceOpOuterProduct(opt::IRContext* ir_context,
|
||||
opt::Instruction* instruction) const;
|
||||
|
||||
// Replaces an OpDot instruction.
|
||||
void ReplaceOpDot(opt::IRContext* ir_context,
|
||||
opt::Instruction* instruction) const;
|
||||
|
192
3rdparty/spirv-tools/source/fuzz/transformation_replace_load_store_with_copy_memory.cpp
vendored
Normal file
192
3rdparty/spirv-tools/source/fuzz/transformation_replace_load_store_with_copy_memory.cpp
vendored
Normal file
@ -0,0 +1,192 @@
|
||||
// Copyright (c) 2020 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 "transformation_replace_load_store_with_copy_memory.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
#include "source/opcode.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
namespace {
|
||||
const uint32_t kOpStoreOperandIndexTargetVariable = 0;
|
||||
const uint32_t kOpStoreOperandIndexIntermediateIdToWrite = 1;
|
||||
const uint32_t kOpLoadOperandIndexSourceVariable = 2;
|
||||
} // namespace
|
||||
|
||||
TransformationReplaceLoadStoreWithCopyMemory::
|
||||
TransformationReplaceLoadStoreWithCopyMemory(
|
||||
const spvtools::fuzz::protobufs::
|
||||
TransformationReplaceLoadStoreWithCopyMemory& message)
|
||||
: message_(message) {}
|
||||
|
||||
TransformationReplaceLoadStoreWithCopyMemory::
|
||||
TransformationReplaceLoadStoreWithCopyMemory(
|
||||
const protobufs::InstructionDescriptor& load_instruction_descriptor,
|
||||
const protobufs::InstructionDescriptor& store_instruction_descriptor) {
|
||||
*message_.mutable_load_instruction_descriptor() = load_instruction_descriptor;
|
||||
*message_.mutable_store_instruction_descriptor() =
|
||||
store_instruction_descriptor;
|
||||
}
|
||||
bool TransformationReplaceLoadStoreWithCopyMemory::IsApplicable(
|
||||
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
|
||||
// This transformation is only applicable to the pair of OpLoad and OpStore
|
||||
// instructions.
|
||||
if (message_.load_instruction_descriptor().target_instruction_opcode() !=
|
||||
SpvOpLoad) {
|
||||
return false;
|
||||
}
|
||||
if (message_.store_instruction_descriptor().target_instruction_opcode() !=
|
||||
SpvOpStore) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The OpLoad instruction must be defined.
|
||||
auto load_instruction =
|
||||
FindInstruction(message_.load_instruction_descriptor(), ir_context);
|
||||
if (!load_instruction) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The OpStore instruction must be defined.
|
||||
auto store_instruction =
|
||||
FindInstruction(message_.store_instruction_descriptor(), ir_context);
|
||||
if (!store_instruction) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Intermediate values of the OpLoad and the OpStore must match.
|
||||
if (load_instruction->result_id() !=
|
||||
store_instruction->GetSingleWordOperand(
|
||||
kOpStoreOperandIndexIntermediateIdToWrite)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get storage class of the variable pointed by the source operand in OpLoad.
|
||||
opt::Instruction* source_id = ir_context->get_def_use_mgr()->GetDef(
|
||||
load_instruction->GetSingleWordOperand(2));
|
||||
SpvStorageClass storage_class = fuzzerutil::GetStorageClassFromPointerType(
|
||||
ir_context, source_id->type_id());
|
||||
|
||||
// Iterate over all instructions between |load_instruction| and
|
||||
// |store_instruction|.
|
||||
for (auto it = load_instruction; it != store_instruction;
|
||||
it = it->NextNode()) {
|
||||
//|load_instruction| and |store_instruction| are not in the same block.
|
||||
if (it == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We need to make sure that the value pointed to by the source of the
|
||||
// OpLoad hasn't changed by the time we see the matching OpStore
|
||||
// instruction.
|
||||
if (IsMemoryWritingOpCode(it->opcode())) {
|
||||
return false;
|
||||
} else if (IsMemoryBarrierOpCode(it->opcode()) &&
|
||||
!IsStorageClassSafeAcrossMemoryBarriers(storage_class)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void TransformationReplaceLoadStoreWithCopyMemory::Apply(
|
||||
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
|
||||
// OpLoad and OpStore instructions must be defined.
|
||||
auto load_instruction =
|
||||
FindInstruction(message_.load_instruction_descriptor(), ir_context);
|
||||
assert(load_instruction && load_instruction->opcode() == SpvOpLoad &&
|
||||
"The required OpLoad instruction must be defined.");
|
||||
auto store_instruction =
|
||||
FindInstruction(message_.store_instruction_descriptor(), ir_context);
|
||||
assert(store_instruction && store_instruction->opcode() == SpvOpStore &&
|
||||
"The required OpStore instruction must be defined.");
|
||||
|
||||
// Intermediate values of the OpLoad and the OpStore must match.
|
||||
assert(load_instruction->result_id() ==
|
||||
store_instruction->GetSingleWordOperand(
|
||||
kOpStoreOperandIndexIntermediateIdToWrite) &&
|
||||
"OpLoad and OpStore must refer to the same value.");
|
||||
|
||||
// Get the ids of the source operand of the OpLoad and the target operand of
|
||||
// the OpStore.
|
||||
uint32_t source_variable_id =
|
||||
load_instruction->GetSingleWordOperand(kOpLoadOperandIndexSourceVariable);
|
||||
uint32_t target_variable_id = store_instruction->GetSingleWordOperand(
|
||||
kOpStoreOperandIndexTargetVariable);
|
||||
|
||||
// Insert the OpCopyMemory instruction before the OpStore instruction.
|
||||
store_instruction->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpCopyMemory, 0, 0,
|
||||
opt::Instruction::OperandList(
|
||||
{{SPV_OPERAND_TYPE_ID, {target_variable_id}},
|
||||
{SPV_OPERAND_TYPE_ID, {source_variable_id}}})));
|
||||
|
||||
// Remove the OpStore instruction.
|
||||
ir_context->KillInst(store_instruction);
|
||||
|
||||
ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
|
||||
}
|
||||
|
||||
bool TransformationReplaceLoadStoreWithCopyMemory::IsMemoryWritingOpCode(
|
||||
SpvOp op_code) {
|
||||
if (spvOpcodeIsAtomicOp(op_code)) {
|
||||
return op_code != SpvOpAtomicLoad;
|
||||
}
|
||||
switch (op_code) {
|
||||
case SpvOpStore:
|
||||
case SpvOpCopyMemory:
|
||||
case SpvOpCopyMemorySized:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool TransformationReplaceLoadStoreWithCopyMemory::IsMemoryBarrierOpCode(
|
||||
SpvOp op_code) {
|
||||
switch (op_code) {
|
||||
case SpvOpMemoryBarrier:
|
||||
case SpvOpMemoryNamedBarrier:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool TransformationReplaceLoadStoreWithCopyMemory::
|
||||
IsStorageClassSafeAcrossMemoryBarriers(SpvStorageClass storage_class) {
|
||||
switch (storage_class) {
|
||||
case SpvStorageClassUniformConstant:
|
||||
case SpvStorageClassInput:
|
||||
case SpvStorageClassUniform:
|
||||
case SpvStorageClassPrivate:
|
||||
case SpvStorageClassFunction:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protobufs::Transformation
|
||||
TransformationReplaceLoadStoreWithCopyMemory::ToMessage() const {
|
||||
protobufs::Transformation result;
|
||||
*result.mutable_replace_load_store_with_copy_memory() = message_;
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
76
3rdparty/spirv-tools/source/fuzz/transformation_replace_load_store_with_copy_memory.h
vendored
Normal file
76
3rdparty/spirv-tools/source/fuzz/transformation_replace_load_store_with_copy_memory.h
vendored
Normal file
@ -0,0 +1,76 @@
|
||||
// Copyright (c) 2020 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 SPIRV_TOOLS_TRANSFORMATION_REPLACE_LOAD_STORE_WITH_COPY_MEMORY_H
|
||||
#define SPIRV_TOOLS_TRANSFORMATION_REPLACE_LOAD_STORE_WITH_COPY_MEMORY_H
|
||||
|
||||
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
||||
#include "source/fuzz/transformation.h"
|
||||
#include "source/fuzz/transformation_context.h"
|
||||
#include "source/opt/ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
class TransformationReplaceLoadStoreWithCopyMemory : public Transformation {
|
||||
public:
|
||||
explicit TransformationReplaceLoadStoreWithCopyMemory(
|
||||
const protobufs::TransformationReplaceLoadStoreWithCopyMemory& message);
|
||||
|
||||
TransformationReplaceLoadStoreWithCopyMemory(
|
||||
const protobufs::InstructionDescriptor& load_instruction_descriptor,
|
||||
const protobufs::InstructionDescriptor& store_instruction_descriptor);
|
||||
|
||||
// - |message_.load_instruction_descriptor| must identify an OpLoad
|
||||
// instruction.
|
||||
// - |message_.store_instruction_descriptor| must identify an OpStore
|
||||
// instruction.
|
||||
// - The OpStore must write the intermediate value loaded by the OpLoad.
|
||||
// - The OpLoad and the OpStore must not have certain instruction in between
|
||||
// (checked by IsMemoryWritingOpCode(), IsMemoryBarrierOpCode(),
|
||||
// IsStorageClassSafeAcrossMemoryBarriers()).
|
||||
bool IsApplicable(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const override;
|
||||
|
||||
// Takes a pair of instruction descriptors to OpLoad and OpStore that have the
|
||||
// same intermediate value and replaces the OpStore with an equivalent
|
||||
// OpCopyMemory.
|
||||
void Apply(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const override;
|
||||
|
||||
// Checks if the instruction that has an |op_code| might write to
|
||||
// the source operand of the OpLoad instruction.
|
||||
static bool IsMemoryWritingOpCode(SpvOp op_code);
|
||||
|
||||
// Checks if the instruction that has an |op_code| is a memory barrier that
|
||||
// could interfere with the source operand of the OpLoad instruction
|
||||
static bool IsMemoryBarrierOpCode(SpvOp op_code);
|
||||
|
||||
// Checks if the |storage_class| of the source operand of the OpLoad
|
||||
// instruction implies that this variable cannot change (due to other threads)
|
||||
// across memory barriers.
|
||||
static bool IsStorageClassSafeAcrossMemoryBarriers(
|
||||
SpvStorageClass storage_class);
|
||||
|
||||
protobufs::Transformation ToMessage() const override;
|
||||
|
||||
private:
|
||||
protobufs::TransformationReplaceLoadStoreWithCopyMemory message_;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SPIRV_TOOLS_TRANSFORMATION_REPLACE_LOAD_STORE_WITH_COPY_MEMORY_H
|
@ -161,7 +161,7 @@ void TransformationReplaceParameterWithGlobal::Apply(
|
||||
}
|
||||
|
||||
// Remove the parameter from the function.
|
||||
function->RemoveParameter(message_.parameter_id());
|
||||
fuzzerutil::RemoveParameter(ir_context, message_.parameter_id());
|
||||
|
||||
// Update function's type.
|
||||
{
|
||||
|
@ -28,8 +28,7 @@ TransformationReplaceParamsWithStruct::TransformationReplaceParamsWithStruct(
|
||||
TransformationReplaceParamsWithStruct::TransformationReplaceParamsWithStruct(
|
||||
const std::vector<uint32_t>& parameter_id, uint32_t fresh_function_type_id,
|
||||
uint32_t fresh_parameter_id,
|
||||
const std::unordered_map<uint32_t, uint32_t>&
|
||||
caller_id_to_fresh_composite_id) {
|
||||
const std::map<uint32_t, uint32_t>& caller_id_to_fresh_composite_id) {
|
||||
message_.set_fresh_function_type_id(fresh_function_type_id);
|
||||
message_.set_fresh_parameter_id(fresh_parameter_id);
|
||||
|
||||
@ -37,9 +36,8 @@ TransformationReplaceParamsWithStruct::TransformationReplaceParamsWithStruct(
|
||||
message_.add_parameter_id(id);
|
||||
}
|
||||
|
||||
message_.mutable_caller_id_to_fresh_composite_id()->insert(
|
||||
caller_id_to_fresh_composite_id.begin(),
|
||||
caller_id_to_fresh_composite_id.end());
|
||||
*message_.mutable_caller_id_to_fresh_composite_id() =
|
||||
fuzzerutil::MapToRepeatedUInt32Pair(caller_id_to_fresh_composite_id);
|
||||
}
|
||||
|
||||
bool TransformationReplaceParamsWithStruct::IsApplicable(
|
||||
@ -103,13 +101,15 @@ bool TransformationReplaceParamsWithStruct::IsApplicable(
|
||||
return false;
|
||||
}
|
||||
|
||||
auto caller_id_to_fresh_composite_id = fuzzerutil::RepeatedUInt32PairToMap(
|
||||
message_.caller_id_to_fresh_composite_id());
|
||||
|
||||
// Check that |callee_id_to_fresh_composite_id| is valid.
|
||||
for (const auto* inst :
|
||||
fuzzerutil::GetCallers(ir_context, function->result_id())) {
|
||||
// Check that the callee is present in the map. It's ok if the map contains
|
||||
// more ids that there are callees (those ids will not be used).
|
||||
if (!message_.caller_id_to_fresh_composite_id().contains(
|
||||
inst->result_id())) {
|
||||
if (!caller_id_to_fresh_composite_id.count(inst->result_id())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -118,7 +118,7 @@ bool TransformationReplaceParamsWithStruct::IsApplicable(
|
||||
std::vector<uint32_t> fresh_ids = {message_.fresh_function_type_id(),
|
||||
message_.fresh_parameter_id()};
|
||||
|
||||
for (const auto& entry : message_.caller_id_to_fresh_composite_id()) {
|
||||
for (const auto& entry : caller_id_to_fresh_composite_id) {
|
||||
fresh_ids.push_back(entry.second);
|
||||
}
|
||||
|
||||
@ -167,6 +167,9 @@ void TransformationReplaceParamsWithStruct::Apply(
|
||||
}
|
||||
}
|
||||
|
||||
auto caller_id_to_fresh_composite_id = fuzzerutil::RepeatedUInt32PairToMap(
|
||||
message_.caller_id_to_fresh_composite_id());
|
||||
|
||||
// Update all function calls.
|
||||
for (auto* inst : fuzzerutil::GetCallers(ir_context, function->result_id())) {
|
||||
// Create a list of operands for the OpCompositeConstruct instruction.
|
||||
@ -189,7 +192,7 @@ void TransformationReplaceParamsWithStruct::Apply(
|
||||
|
||||
// Insert OpCompositeConstruct before the function call.
|
||||
auto fresh_composite_id =
|
||||
message_.caller_id_to_fresh_composite_id().at(inst->result_id());
|
||||
caller_id_to_fresh_composite_id.at(inst->result_id());
|
||||
inst->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpCompositeConstruct, struct_type_id, fresh_composite_id,
|
||||
std::move(composite_components)));
|
||||
@ -227,7 +230,7 @@ void TransformationReplaceParamsWithStruct::Apply(
|
||||
{SPV_OPERAND_TYPE_ID, {message_.fresh_parameter_id()}},
|
||||
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {static_cast<uint32_t>(i)}}}));
|
||||
|
||||
function->RemoveParameter(param_inst->result_id());
|
||||
fuzzerutil::RemoveParameter(ir_context, param_inst->result_id());
|
||||
}
|
||||
|
||||
// Update function's type.
|
||||
|
@ -15,7 +15,7 @@
|
||||
#ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMS_WITH_STRUCT_H_
|
||||
#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMS_WITH_STRUCT_H_
|
||||
|
||||
#include <unordered_map>
|
||||
#include <map>
|
||||
|
||||
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
||||
#include "source/fuzz/transformation.h"
|
||||
@ -33,8 +33,7 @@ class TransformationReplaceParamsWithStruct : public Transformation {
|
||||
TransformationReplaceParamsWithStruct(
|
||||
const std::vector<uint32_t>& parameter_id,
|
||||
uint32_t fresh_function_type_id, uint32_t fresh_parameter_id,
|
||||
const std::unordered_map<uint32_t, uint32_t>&
|
||||
caller_id_to_fresh_composite_id);
|
||||
const std::map<uint32_t, uint32_t>& caller_id_to_fresh_composite_id);
|
||||
|
||||
// - Each element of |parameter_id| is a valid result id of some
|
||||
// OpFunctionParameter instruction. All parameter ids must correspond to
|
||||
|
@ -42,8 +42,8 @@ bool TransformationSetLoopControl::IsApplicable(
|
||||
return false;
|
||||
}
|
||||
|
||||
// We sanity-check that the transformation does not try to set any meaningless
|
||||
// bits of the loop control mask.
|
||||
// We assert that the transformation does not try to set any meaningless bits
|
||||
// of the loop control mask.
|
||||
uint32_t all_loop_control_mask_bits_set =
|
||||
SpvLoopControlUnrollMask | SpvLoopControlDontUnrollMask |
|
||||
SpvLoopControlDependencyInfiniteMask |
|
||||
|
@ -135,11 +135,10 @@ void TransformationSplitBlock::Apply(
|
||||
// predecessor operand so that the block they used to be inside is now the
|
||||
// predecessor.
|
||||
new_bb->ForEachPhiInst([block_to_split](opt::Instruction* phi_inst) {
|
||||
// The following assertion is a sanity check. It is guaranteed to hold
|
||||
// if IsApplicable holds.
|
||||
assert(phi_inst->NumInOperands() == 2 &&
|
||||
"We can only split a block before an OpPhi if block has exactly "
|
||||
"one predecessor.");
|
||||
assert(
|
||||
phi_inst->NumInOperands() == 2 &&
|
||||
"Precondition: a block can only be split before an OpPhi if the block"
|
||||
"has exactly one predecessor.");
|
||||
phi_inst->SetInOperand(1, {block_to_split->id()});
|
||||
});
|
||||
|
||||
|
@ -11,7 +11,7 @@
|
||||
# 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_library(SPIRV-Tools-link
|
||||
add_library(SPIRV-Tools-link STATIC
|
||||
linker.cpp
|
||||
)
|
||||
|
||||
|
2
3rdparty/spirv-tools/source/name_mapper.cpp
vendored
2
3rdparty/spirv-tools/source/name_mapper.cpp
vendored
@ -324,7 +324,7 @@ std::string FriendlyNameMapper::NameForEnumOperand(spv_operand_type_t type,
|
||||
if (SPV_SUCCESS == grammar_.lookupOperand(type, word, &desc)) {
|
||||
return desc->name;
|
||||
} else {
|
||||
// Invalid input. Just give something sane.
|
||||
// Invalid input. Just give something.
|
||||
return std::string("StorageClass") + to_string(word);
|
||||
}
|
||||
}
|
||||
|
31
3rdparty/spirv-tools/source/operand.cpp
vendored
31
3rdparty/spirv-tools/source/operand.cpp
vendored
@ -265,7 +265,6 @@ const char* spvOperandTypeStr(spv_operand_type_t type) {
|
||||
case SPV_OPERAND_TYPE_NONE:
|
||||
return "NONE";
|
||||
default:
|
||||
assert(0 && "Unhandled operand type!");
|
||||
break;
|
||||
}
|
||||
return "unknown";
|
||||
@ -371,13 +370,35 @@ bool spvOperandIsConcreteMask(spv_operand_type_t type) {
|
||||
}
|
||||
|
||||
bool spvOperandIsOptional(spv_operand_type_t type) {
|
||||
return SPV_OPERAND_TYPE_FIRST_OPTIONAL_TYPE <= type &&
|
||||
type <= SPV_OPERAND_TYPE_LAST_OPTIONAL_TYPE;
|
||||
switch (type) {
|
||||
case SPV_OPERAND_TYPE_OPTIONAL_ID:
|
||||
case SPV_OPERAND_TYPE_OPTIONAL_IMAGE:
|
||||
case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS:
|
||||
case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER:
|
||||
case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_NUMBER:
|
||||
case SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER:
|
||||
case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_STRING:
|
||||
case SPV_OPERAND_TYPE_OPTIONAL_ACCESS_QUALIFIER:
|
||||
case SPV_OPERAND_TYPE_OPTIONAL_CIV:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// Any variable operand is also optional.
|
||||
return spvOperandIsVariable(type);
|
||||
}
|
||||
|
||||
bool spvOperandIsVariable(spv_operand_type_t type) {
|
||||
return SPV_OPERAND_TYPE_FIRST_VARIABLE_TYPE <= type &&
|
||||
type <= SPV_OPERAND_TYPE_LAST_VARIABLE_TYPE;
|
||||
switch (type) {
|
||||
case SPV_OPERAND_TYPE_VARIABLE_ID:
|
||||
case SPV_OPERAND_TYPE_VARIABLE_LITERAL_INTEGER:
|
||||
case SPV_OPERAND_TYPE_VARIABLE_LITERAL_INTEGER_ID:
|
||||
case SPV_OPERAND_TYPE_VARIABLE_ID_LITERAL_INTEGER:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool spvExpandOperandSequenceOnce(spv_operand_type_t type,
|
||||
|
@ -233,7 +233,7 @@ endif()
|
||||
|
||||
spvtools_pch(SPIRV_TOOLS_OPT_SOURCES pch_source_opt)
|
||||
|
||||
add_library(SPIRV-Tools-opt ${SPIRV_TOOLS_OPT_SOURCES})
|
||||
add_library(SPIRV-Tools-opt STATIC ${SPIRV_TOOLS_OPT_SOURCES})
|
||||
|
||||
spvtools_default_compile_options(SPIRV-Tools-opt)
|
||||
target_include_directories(SPIRV-Tools-opt
|
||||
@ -245,7 +245,7 @@ target_include_directories(SPIRV-Tools-opt
|
||||
)
|
||||
# We need the assembling and disassembling functionalities in the main library.
|
||||
target_link_libraries(SPIRV-Tools-opt
|
||||
PUBLIC ${SPIRV_TOOLS})
|
||||
PUBLIC ${SPIRV_TOOLS}-static)
|
||||
|
||||
set_property(TARGET SPIRV-Tools-opt PROPERTY FOLDER "SPIRV-Tools libraries")
|
||||
spvtools_check_symbol_exports(SPIRV-Tools-opt)
|
||||
|
@ -696,8 +696,8 @@ Pass::Status AggressiveDCEPass::ProcessImpl() {
|
||||
// been marked, it is safe to remove dead global values.
|
||||
modified |= ProcessGlobalValues();
|
||||
|
||||
// Sanity check.
|
||||
assert(to_kill_.size() == 0 || modified);
|
||||
assert((to_kill_.empty() || modified) &&
|
||||
"A dead instruction was identified, but no change recorded.");
|
||||
|
||||
// Kill all dead instructions.
|
||||
for (auto inst : to_kill_) {
|
||||
|
@ -396,6 +396,12 @@ uint32_t ConstantManager::GetFloatConst(float val) {
|
||||
return GetDefiningInstruction(c)->result_id();
|
||||
}
|
||||
|
||||
uint32_t ConstantManager::GetSIntConst(int32_t val) {
|
||||
Type* sint_type = context()->get_type_mgr()->GetSIntType();
|
||||
const Constant* c = GetConstant(sint_type, {static_cast<uint32_t>(val)});
|
||||
return GetDefiningInstruction(c)->result_id();
|
||||
}
|
||||
|
||||
std::vector<const analysis::Constant*> Constant::GetVectorComponents(
|
||||
analysis::ConstantManager* const_mgr) const {
|
||||
std::vector<const analysis::Constant*> components;
|
||||
|
3
3rdparty/spirv-tools/source/opt/constants.h
vendored
3
3rdparty/spirv-tools/source/opt/constants.h
vendored
@ -630,6 +630,9 @@ class ConstantManager {
|
||||
// Returns the id of a 32-bit floating point constant with value |val|.
|
||||
uint32_t GetFloatConst(float val);
|
||||
|
||||
// Returns the id of a 32-bit signed integer constant with value |val|.
|
||||
uint32_t GetSIntConst(int32_t val);
|
||||
|
||||
private:
|
||||
// Creates a Constant instance with the given type and a vector of constant
|
||||
// defining words. Returns a unique pointer to the created Constant instance
|
||||
|
@ -33,10 +33,13 @@ static const uint32_t kDebugDeclareOperandLocalVariableIndex = 4;
|
||||
static const uint32_t kDebugDeclareOperandVariableIndex = 5;
|
||||
static const uint32_t kDebugValueOperandLocalVariableIndex = 4;
|
||||
static const uint32_t kDebugValueOperandExpressionIndex = 6;
|
||||
static const uint32_t kDebugValueOperandIndexesIndex = 7;
|
||||
static const uint32_t kDebugOperationOperandOperationIndex = 4;
|
||||
static const uint32_t kOpVariableOperandStorageClassIndex = 2;
|
||||
static const uint32_t kDebugLocalVariableOperandParentIndex = 9;
|
||||
static const uint32_t kDebugOperationOperandOpCodeIndex = 4;
|
||||
static const uint32_t kExtInstInstructionInIdx = 1;
|
||||
static const uint32_t kDebugGlobalVariableOperandFlagsIndex = 12;
|
||||
static const uint32_t kDebugLocalVariableOperandFlagsIndex = 10;
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
@ -369,7 +372,7 @@ Instruction* DebugInfoManager::CloneDebugInlinedAt(uint32_t clone_inlined_at_id,
|
||||
std::move(new_inlined_at));
|
||||
}
|
||||
|
||||
bool DebugInfoManager::IsDebugDeclared(uint32_t variable_id) {
|
||||
bool DebugInfoManager::IsVariableDebugDeclared(uint32_t variable_id) {
|
||||
auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id);
|
||||
return dbg_decl_itr != var_id_to_dbg_decl_.end();
|
||||
}
|
||||
@ -446,17 +449,11 @@ bool DebugInfoManager::IsDeclareVisibleToInstr(Instruction* dbg_declare,
|
||||
return IsAncestorOfScope(instr_scope_id, decl_scope_id);
|
||||
}
|
||||
|
||||
void DebugInfoManager::AddDebugValue(Instruction* scope_and_line,
|
||||
uint32_t variable_id, uint32_t value_id,
|
||||
Instruction* insert_pos) {
|
||||
auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id);
|
||||
if (dbg_decl_itr == var_id_to_dbg_decl_.end()) return;
|
||||
|
||||
uint32_t instr_scope_id = scope_and_line->GetDebugScope().GetLexicalScope();
|
||||
for (auto* dbg_decl_or_val : dbg_decl_itr->second) {
|
||||
if (!IsDeclareVisibleToInstr(dbg_decl_or_val, instr_scope_id)) continue;
|
||||
|
||||
Instruction* DebugInfoManager::AddDebugValueWithIndex(
|
||||
uint32_t dbg_local_var_id, uint32_t value_id, uint32_t expr_id,
|
||||
uint32_t index_id, Instruction* insert_before) {
|
||||
uint32_t result_id = context()->TakeNextId();
|
||||
if (!result_id) return nullptr;
|
||||
std::unique_ptr<Instruction> new_dbg_value(new Instruction(
|
||||
context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
|
||||
result_id,
|
||||
@ -467,25 +464,39 @@ void DebugInfoManager::AddDebugValue(Instruction* scope_and_line,
|
||||
->GetExtInstImportId_OpenCL100DebugInfo()}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
|
||||
{static_cast<uint32_t>(OpenCLDebugInfo100DebugValue)}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
|
||||
{dbg_decl_or_val->GetSingleWordOperand(
|
||||
kDebugValueOperandLocalVariableIndex)}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {dbg_local_var_id}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {value_id}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
|
||||
{GetEmptyDebugExpression()->result_id()}},
|
||||
{expr_id == 0 ? GetEmptyDebugExpression()->result_id() : expr_id}},
|
||||
}));
|
||||
if (index_id) {
|
||||
new_dbg_value->AddOperand(
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {index_id}});
|
||||
}
|
||||
|
||||
if (dbg_decl_or_val->NumOperands() >
|
||||
kDebugValueOperandExpressionIndex + 1) {
|
||||
assert(dbg_decl_or_val->GetOpenCL100DebugOpcode() ==
|
||||
OpenCLDebugInfo100DebugValue);
|
||||
for (uint32_t i = kDebugValueOperandExpressionIndex + 1;
|
||||
i < dbg_decl_or_val->NumOperands(); ++i) {
|
||||
new_dbg_value->AddOperand({spv_operand_type_t::SPV_OPERAND_TYPE_ID,
|
||||
{dbg_decl_or_val->GetSingleWordOperand(i)}});
|
||||
Instruction* added_dbg_value =
|
||||
insert_before->InsertBefore(std::move(new_dbg_value));
|
||||
AnalyzeDebugInst(added_dbg_value);
|
||||
if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
|
||||
context()->get_def_use_mgr()->AnalyzeInstDefUse(added_dbg_value);
|
||||
if (context()->AreAnalysesValid(
|
||||
IRContext::Analysis::kAnalysisInstrToBlockMapping)) {
|
||||
auto insert_blk = context()->get_instr_block(insert_before);
|
||||
context()->set_instr_block(added_dbg_value, insert_blk);
|
||||
}
|
||||
return added_dbg_value;
|
||||
}
|
||||
|
||||
void DebugInfoManager::AddDebugValueIfVarDeclIsVisible(
|
||||
Instruction* scope_and_line, uint32_t variable_id, uint32_t value_id,
|
||||
Instruction* insert_pos) {
|
||||
auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id);
|
||||
if (dbg_decl_itr == var_id_to_dbg_decl_.end()) return;
|
||||
|
||||
uint32_t instr_scope_id = scope_and_line->GetDebugScope().GetLexicalScope();
|
||||
for (auto* dbg_decl_or_val : dbg_decl_itr->second) {
|
||||
if (!IsDeclareVisibleToInstr(dbg_decl_or_val, instr_scope_id)) continue;
|
||||
|
||||
// Avoid inserting the new DebugValue between OpPhi or OpVariable
|
||||
// instructions.
|
||||
Instruction* insert_before = insert_pos->NextNode();
|
||||
@ -494,17 +505,18 @@ void DebugInfoManager::AddDebugValue(Instruction* scope_and_line,
|
||||
insert_before = insert_before->NextNode();
|
||||
}
|
||||
|
||||
Instruction* added_dbg_value =
|
||||
insert_before->InsertBefore(std::move(new_dbg_value));
|
||||
added_dbg_value->UpdateDebugInfo(scope_and_line);
|
||||
AnalyzeDebugInst(added_dbg_value);
|
||||
if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
|
||||
context()->get_def_use_mgr()->AnalyzeInstDefUse(added_dbg_value);
|
||||
if (context()->AreAnalysesValid(
|
||||
IRContext::Analysis::kAnalysisInstrToBlockMapping)) {
|
||||
auto insert_blk = context()->get_instr_block(insert_before);
|
||||
context()->set_instr_block(added_dbg_value, insert_blk);
|
||||
uint32_t index_id = 0;
|
||||
if (dbg_decl_or_val->NumOperands() > kDebugValueOperandIndexesIndex) {
|
||||
index_id =
|
||||
dbg_decl_or_val->GetSingleWordOperand(kDebugValueOperandIndexesIndex);
|
||||
}
|
||||
|
||||
Instruction* added_dbg_value =
|
||||
AddDebugValueWithIndex(dbg_decl_or_val->GetSingleWordOperand(
|
||||
kDebugValueOperandLocalVariableIndex),
|
||||
value_id, 0, index_id, insert_before);
|
||||
assert(added_dbg_value != nullptr);
|
||||
added_dbg_value->UpdateDebugInfoFrom(scope_and_line);
|
||||
}
|
||||
}
|
||||
|
||||
@ -542,6 +554,12 @@ uint32_t DebugInfoManager::GetVariableIdOfDebugValueUsedForDeclare(
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool DebugInfoManager::IsDebugDeclare(Instruction* instr) {
|
||||
if (!instr->IsOpenCL100DebugInstr()) return false;
|
||||
return instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare ||
|
||||
GetVariableIdOfDebugValueUsedForDeclare(instr) != 0;
|
||||
}
|
||||
|
||||
void DebugInfoManager::AnalyzeDebugInst(Instruction* dbg_inst) {
|
||||
if (!dbg_inst->IsOpenCL100DebugInstr()) return;
|
||||
|
||||
@ -556,7 +574,7 @@ void DebugInfoManager::AnalyzeDebugInst(Instruction* dbg_inst) {
|
||||
|
||||
if (deref_operation_ == nullptr &&
|
||||
dbg_inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugOperation &&
|
||||
dbg_inst->GetSingleWordOperand(kDebugOperationOperandOpCodeIndex) ==
|
||||
dbg_inst->GetSingleWordOperand(kDebugOperationOperandOperationIndex) ==
|
||||
OpenCLDebugInfo100Deref) {
|
||||
deref_operation_ = dbg_inst;
|
||||
}
|
||||
@ -581,6 +599,56 @@ void DebugInfoManager::AnalyzeDebugInst(Instruction* dbg_inst) {
|
||||
}
|
||||
}
|
||||
|
||||
void DebugInfoManager::ConvertDebugGlobalToLocalVariable(
|
||||
Instruction* dbg_global_var, Instruction* local_var) {
|
||||
if (dbg_global_var->GetOpenCL100DebugOpcode() !=
|
||||
OpenCLDebugInfo100DebugGlobalVariable) {
|
||||
return;
|
||||
}
|
||||
assert(local_var->opcode() == SpvOpVariable ||
|
||||
local_var->opcode() == SpvOpFunctionParameter);
|
||||
|
||||
// Convert |dbg_global_var| to DebugLocalVariable
|
||||
dbg_global_var->SetInOperand(kExtInstInstructionInIdx,
|
||||
{OpenCLDebugInfo100DebugLocalVariable});
|
||||
auto flags = dbg_global_var->GetSingleWordOperand(
|
||||
kDebugGlobalVariableOperandFlagsIndex);
|
||||
for (uint32_t i = dbg_global_var->NumInOperands() - 1;
|
||||
i >= kDebugLocalVariableOperandFlagsIndex; --i) {
|
||||
dbg_global_var->RemoveOperand(i);
|
||||
}
|
||||
dbg_global_var->SetOperand(kDebugLocalVariableOperandFlagsIndex, {flags});
|
||||
context()->ForgetUses(dbg_global_var);
|
||||
context()->AnalyzeUses(dbg_global_var);
|
||||
|
||||
// Create a DebugDeclare
|
||||
std::unique_ptr<Instruction> new_dbg_decl(new Instruction(
|
||||
context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
|
||||
context()->TakeNextId(),
|
||||
{
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
|
||||
{context()
|
||||
->get_feature_mgr()
|
||||
->GetExtInstImportId_OpenCL100DebugInfo()}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
|
||||
{static_cast<uint32_t>(OpenCLDebugInfo100DebugDeclare)}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
|
||||
{dbg_global_var->result_id()}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {local_var->result_id()}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
|
||||
{GetEmptyDebugExpression()->result_id()}},
|
||||
}));
|
||||
auto* added_dbg_decl =
|
||||
local_var->NextNode()->InsertBefore(std::move(new_dbg_decl));
|
||||
if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
|
||||
context()->get_def_use_mgr()->AnalyzeInstDefUse(added_dbg_decl);
|
||||
if (context()->AreAnalysesValid(
|
||||
IRContext::Analysis::kAnalysisInstrToBlockMapping)) {
|
||||
auto insert_blk = context()->get_instr_block(local_var);
|
||||
context()->set_instr_block(added_dbg_decl, insert_blk);
|
||||
}
|
||||
}
|
||||
|
||||
void DebugInfoManager::AnalyzeDebugInsts(Module& module) {
|
||||
deref_operation_ = nullptr;
|
||||
debug_info_none_inst_ = nullptr;
|
||||
@ -638,7 +706,8 @@ void DebugInfoManager::ClearDebugInfo(Instruction* instr) {
|
||||
dbg_instr_itr->GetOpenCL100DebugOpcode() ==
|
||||
OpenCLDebugInfo100DebugOperation &&
|
||||
dbg_instr_itr->GetSingleWordOperand(
|
||||
kDebugOperationOperandOpCodeIndex) == OpenCLDebugInfo100Deref) {
|
||||
kDebugOperationOperandOperationIndex) ==
|
||||
OpenCLDebugInfo100Deref) {
|
||||
deref_operation_ = &*dbg_instr_itr;
|
||||
break;
|
||||
}
|
||||
|
@ -133,17 +133,27 @@ class DebugInfoManager {
|
||||
uint32_t BuildDebugInlinedAtChain(uint32_t callee_inlined_at,
|
||||
DebugInlinedAtContext* inlined_at_ctx);
|
||||
|
||||
// Return true if |variable_id| has DebugDeclare or DebugVal.
|
||||
bool IsDebugDeclared(uint32_t variable_id);
|
||||
// Returns true if there is a debug declaration instruction whose
|
||||
// 'Local Variable' operand is |variable_id|.
|
||||
bool IsVariableDebugDeclared(uint32_t variable_id);
|
||||
|
||||
// Kill all DebugDeclares for |variable_id|
|
||||
// Kills all debug declaration instructions with Deref whose 'Local Variable'
|
||||
// operand is |variable_id|.
|
||||
void KillDebugDeclares(uint32_t variable_id);
|
||||
|
||||
// Generates a DebugValue instruction with value |value_id| for every local
|
||||
// variable that is in the scope of |scope_and_line| and whose memory is
|
||||
// |variable_id| and inserts it after the instruction |insert_pos|.
|
||||
void AddDebugValue(Instruction* scope_and_line, uint32_t variable_id,
|
||||
uint32_t value_id, Instruction* insert_pos);
|
||||
void AddDebugValueIfVarDeclIsVisible(Instruction* scope_and_line,
|
||||
uint32_t variable_id, uint32_t value_id,
|
||||
Instruction* insert_pos);
|
||||
|
||||
// Generates a DebugValue instruction with |dbg_local_var_id|, |value_id|,
|
||||
// |expr_id|, |index_id| operands and inserts it before |insert_before|.
|
||||
Instruction* AddDebugValueWithIndex(uint32_t dbg_local_var_id,
|
||||
uint32_t value_id, uint32_t expr_id,
|
||||
uint32_t index_id,
|
||||
Instruction* insert_before);
|
||||
|
||||
// Erases |instr| from data structures of this class.
|
||||
void ClearDebugInfo(Instruction* instr);
|
||||
@ -153,6 +163,14 @@ class DebugInfoManager {
|
||||
// Function storage class. Otherwise, returns 0.
|
||||
uint32_t GetVariableIdOfDebugValueUsedForDeclare(Instruction* inst);
|
||||
|
||||
// Converts DebugGlobalVariable |dbg_global_var| to a DebugLocalVariable and
|
||||
// creates a DebugDeclare mapping the new DebugLocalVariable to |local_var|.
|
||||
void ConvertDebugGlobalToLocalVariable(Instruction* dbg_global_var,
|
||||
Instruction* local_var);
|
||||
|
||||
// Returns true if |instr| is a debug declaration instruction.
|
||||
bool IsDebugDeclare(Instruction* instr);
|
||||
|
||||
private:
|
||||
IRContext* context() { return context_; }
|
||||
|
||||
|
@ -22,8 +22,8 @@
|
||||
|
||||
// Calculates the dominator or postdominator tree for a given function.
|
||||
// 1 - Compute the successors and predecessors for each BasicBlock. We add a
|
||||
// dummy node for the start node or for postdominators the exit. This node will
|
||||
// point to all entry or all exit nodes.
|
||||
// placeholder node for the start node or for postdominators the exit. This node
|
||||
// will point to all entry or all exit nodes.
|
||||
// 2 - Using the CFA::DepthFirstTraversal get a depth first postordered list of
|
||||
// all BasicBlocks. Using the successors (or for postdominator, predecessors)
|
||||
// calculated in step 1 to traverse the tree.
|
||||
@ -107,8 +107,9 @@ class BasicBlockSuccessorHelper {
|
||||
|
||||
public:
|
||||
// For compliance with the dominance tree computation, entry nodes are
|
||||
// connected to a single dummy node.
|
||||
BasicBlockSuccessorHelper(Function& func, const BasicBlock* dummy_start_node,
|
||||
// connected to a single placeholder node.
|
||||
BasicBlockSuccessorHelper(Function& func,
|
||||
const BasicBlock* placeholder_start_node,
|
||||
bool post);
|
||||
|
||||
// CFA::CalculateDominators requires std::vector<BasicBlock*>.
|
||||
@ -139,23 +140,24 @@ class BasicBlockSuccessorHelper {
|
||||
// Build the successors and predecessors map for each basic blocks |f|.
|
||||
// If |invert_graph_| is true, all edges are reversed (successors becomes
|
||||
// predecessors and vice versa).
|
||||
// For convenience, the start of the graph is |dummy_start_node|.
|
||||
// For convenience, the start of the graph is |placeholder_start_node|.
|
||||
// The dominator tree construction requires a unique entry node, which cannot
|
||||
// be guaranteed for the postdominator graph. The |dummy_start_node| BB is
|
||||
// here to gather all entry nodes.
|
||||
void CreateSuccessorMap(Function& f, const BasicBlock* dummy_start_node);
|
||||
// be guaranteed for the postdominator graph. The |placeholder_start_node| BB
|
||||
// is here to gather all entry nodes.
|
||||
void CreateSuccessorMap(Function& f,
|
||||
const BasicBlock* placeholder_start_node);
|
||||
};
|
||||
|
||||
template <typename BBType>
|
||||
BasicBlockSuccessorHelper<BBType>::BasicBlockSuccessorHelper(
|
||||
Function& func, const BasicBlock* dummy_start_node, bool invert)
|
||||
Function& func, const BasicBlock* placeholder_start_node, bool invert)
|
||||
: invert_graph_(invert) {
|
||||
CreateSuccessorMap(func, dummy_start_node);
|
||||
CreateSuccessorMap(func, placeholder_start_node);
|
||||
}
|
||||
|
||||
template <typename BBType>
|
||||
void BasicBlockSuccessorHelper<BBType>::CreateSuccessorMap(
|
||||
Function& f, const BasicBlock* dummy_start_node) {
|
||||
Function& f, const BasicBlock* placeholder_start_node) {
|
||||
std::map<uint32_t, BasicBlock*> id_to_BB_map;
|
||||
auto GetSuccessorBasicBlock = [&f, &id_to_BB_map](uint32_t successor_id) {
|
||||
BasicBlock*& Succ = id_to_BB_map[successor_id];
|
||||
@ -173,11 +175,10 @@ void BasicBlockSuccessorHelper<BBType>::CreateSuccessorMap(
|
||||
if (invert_graph_) {
|
||||
// For the post dominator tree, we see the inverted graph.
|
||||
// successors_ in the inverted graph are the predecessors in the CFG.
|
||||
// The tree construction requires 1 entry point, so we add a dummy node
|
||||
// that is connected to all function exiting basic blocks.
|
||||
// An exiting basic block is a block with an OpKill, OpUnreachable,
|
||||
// OpReturn, OpReturnValue, or OpTerminateInvocation as terminator
|
||||
// instruction.
|
||||
// The tree construction requires 1 entry point, so we add a placeholder
|
||||
// node that is connected to all function exiting basic blocks. An exiting
|
||||
// basic block is a block with an OpKill, OpUnreachable, OpReturn,
|
||||
// OpReturnValue, or OpTerminateInvocation as terminator instruction.
|
||||
for (BasicBlock& bb : f) {
|
||||
if (bb.hasSuccessor()) {
|
||||
BasicBlockListTy& pred_list = predecessors_[&bb];
|
||||
@ -192,14 +193,15 @@ void BasicBlockSuccessorHelper<BBType>::CreateSuccessorMap(
|
||||
pred_list.push_back(succ);
|
||||
});
|
||||
} else {
|
||||
successors_[dummy_start_node].push_back(&bb);
|
||||
predecessors_[&bb].push_back(const_cast<BasicBlock*>(dummy_start_node));
|
||||
successors_[placeholder_start_node].push_back(&bb);
|
||||
predecessors_[&bb].push_back(
|
||||
const_cast<BasicBlock*>(placeholder_start_node));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
successors_[dummy_start_node].push_back(f.entry().get());
|
||||
successors_[placeholder_start_node].push_back(f.entry().get());
|
||||
predecessors_[f.entry().get()].push_back(
|
||||
const_cast<BasicBlock*>(dummy_start_node));
|
||||
const_cast<BasicBlock*>(placeholder_start_node));
|
||||
for (BasicBlock& bb : f) {
|
||||
BasicBlockListTy& succ_list = successors_[&bb];
|
||||
|
||||
@ -288,7 +290,7 @@ DominatorTreeNode* DominatorTree::GetOrInsertNode(BasicBlock* bb) {
|
||||
}
|
||||
|
||||
void DominatorTree::GetDominatorEdges(
|
||||
const Function* f, const BasicBlock* dummy_start_node,
|
||||
const Function* f, const BasicBlock* placeholder_start_node,
|
||||
std::vector<std::pair<BasicBlock*, BasicBlock*>>* edges) {
|
||||
// Each time the depth first traversal calls the postorder callback
|
||||
// std::function we push that node into the postorder vector to create our
|
||||
@ -302,7 +304,7 @@ void DominatorTree::GetDominatorEdges(
|
||||
// BB are derived from F, so we need to const cast it at some point
|
||||
// no modification is made on F.
|
||||
BasicBlockSuccessorHelper<BasicBlock> helper{
|
||||
*const_cast<Function*>(f), dummy_start_node, postdominator_};
|
||||
*const_cast<Function*>(f), placeholder_start_node, postdominator_};
|
||||
|
||||
// The successor function tells DepthFirstTraversal how to move to successive
|
||||
// nodes by providing an interface to get a list of successor nodes from any
|
||||
@ -316,7 +318,7 @@ void DominatorTree::GetDominatorEdges(
|
||||
// If we're building a post dominator tree we traverse the tree in reverse
|
||||
// using the predecessor function in place of the successor function and vice
|
||||
// versa.
|
||||
DepthFirstSearchPostOrder(dummy_start_node, successor_functor,
|
||||
DepthFirstSearchPostOrder(placeholder_start_node, successor_functor,
|
||||
postorder_function);
|
||||
*edges = CFA<BasicBlock>::CalculateDominators(postorder, predecessor_functor);
|
||||
}
|
||||
@ -329,12 +331,12 @@ void DominatorTree::InitializeTree(const CFG& cfg, const Function* f) {
|
||||
return;
|
||||
}
|
||||
|
||||
const BasicBlock* dummy_start_node =
|
||||
const BasicBlock* placeholder_start_node =
|
||||
postdominator_ ? cfg.pseudo_exit_block() : cfg.pseudo_entry_block();
|
||||
|
||||
// Get the immediate dominator for each node.
|
||||
std::vector<std::pair<BasicBlock*, BasicBlock*>> edges;
|
||||
GetDominatorEdges(f, dummy_start_node, &edges);
|
||||
GetDominatorEdges(f, placeholder_start_node, &edges);
|
||||
|
||||
// Transform the vector<pair> into the tree structure which we can use to
|
||||
// efficiently query dominance.
|
||||
@ -380,7 +382,7 @@ void DominatorTree::DumpTreeAsDot(std::ostream& out_stream) const {
|
||||
}
|
||||
|
||||
// Print the arrow from the parent to this node. Entry nodes will not have
|
||||
// parents so draw them as children from the dummy node.
|
||||
// parents so draw them as children from the placeholder node.
|
||||
if (node->parent_) {
|
||||
out_stream << node->parent_->bb_->id() << " -> " << node->bb_->id()
|
||||
<< ";\n";
|
||||
|
@ -246,6 +246,11 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
|
||||
// Clear line-related debug instructions attached to this instruction.
|
||||
void clear_dbg_line_insts() { dbg_line_insts_.clear(); }
|
||||
|
||||
// Set line-related debug instructions.
|
||||
void set_dbg_line_insts(const std::vector<Instruction>& lines) {
|
||||
dbg_line_insts_ = lines;
|
||||
}
|
||||
|
||||
// Same semantics as in the base class except the list the InstructionList
|
||||
// containing |pos| will now assume ownership of |this|.
|
||||
// inline void MoveBefore(Instruction* pos);
|
||||
@ -301,7 +306,7 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
|
||||
return dbg_scope_.GetInlinedAt();
|
||||
}
|
||||
// Updates OpLine and DebugScope based on the information of |from|.
|
||||
inline void UpdateDebugInfo(const Instruction* from);
|
||||
inline void UpdateDebugInfoFrom(const Instruction* from);
|
||||
// Remove the |index|-th operand
|
||||
void RemoveOperand(uint32_t index) {
|
||||
operands_.erase(operands_.begin() + index);
|
||||
@ -667,7 +672,7 @@ inline void Instruction::UpdateDebugInlinedAt(uint32_t new_inlined_at) {
|
||||
}
|
||||
}
|
||||
|
||||
inline void Instruction::UpdateDebugInfo(const Instruction* from) {
|
||||
inline void Instruction::UpdateDebugInfoFrom(const Instruction* from) {
|
||||
if (from == nullptr) return;
|
||||
clear_dbg_line_insts();
|
||||
if (!from->dbg_line_insts().empty())
|
||||
|
8
3rdparty/spirv-tools/source/opt/ir_builder.h
vendored
8
3rdparty/spirv-tools/source/opt/ir_builder.h
vendored
@ -601,15 +601,15 @@ class InstructionBuilder {
|
||||
return preserved_analyses_ & analysis;
|
||||
}
|
||||
|
||||
// Updates the def/use manager if the user requested it. If he did not request
|
||||
// an update, this function does nothing.
|
||||
// Updates the def/use manager if the user requested it. If an update was not
|
||||
// requested, this function does nothing.
|
||||
inline void UpdateDefUseMgr(Instruction* insn) {
|
||||
if (IsAnalysisUpdateRequested(IRContext::kAnalysisDefUse))
|
||||
GetContext()->get_def_use_mgr()->AnalyzeInstDefUse(insn);
|
||||
}
|
||||
|
||||
// Updates the instruction to block analysis if the user requested it. If he
|
||||
// did not request an update, this function does nothing.
|
||||
// Updates the instruction to block analysis if the user requested it. If
|
||||
// an update was not requested, this function does nothing.
|
||||
inline void UpdateInstrToBlockMapping(Instruction* insn) {
|
||||
if (IsAnalysisUpdateRequested(IRContext::kAnalysisInstrToBlockMapping) &&
|
||||
parent_)
|
||||
|
@ -222,13 +222,6 @@ bool IRContext::KillDef(uint32_t id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void IRContext::KillDebugDeclareInsts(Function* fn) {
|
||||
fn->ForEachInst([this](Instruction* inst) {
|
||||
if (inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare)
|
||||
KillInst(inst);
|
||||
});
|
||||
}
|
||||
|
||||
bool IRContext::ReplaceAllUsesWith(uint32_t before, uint32_t after) {
|
||||
return ReplaceAllUsesWithPredicate(
|
||||
before, after, [](Instruction*, uint32_t) { return true; });
|
||||
|
3
3rdparty/spirv-tools/source/opt/ir_context.h
vendored
3
3rdparty/spirv-tools/source/opt/ir_context.h
vendored
@ -403,9 +403,6 @@ class IRContext {
|
||||
// instruction exists.
|
||||
Instruction* KillInst(Instruction* inst);
|
||||
|
||||
// Deletes DebugDeclare instructions in the given function |fn|.
|
||||
void KillDebugDeclareInsts(Function* fn);
|
||||
|
||||
// Returns true if all of the given analyses are valid.
|
||||
bool AreAnalysesValid(Analysis set) { return (set & valid_analyses_) == set; }
|
||||
|
||||
|
@ -83,7 +83,8 @@ bool LocalSingleBlockLoadStoreElimPass::LocalSingleBlockLoadStoreElim(
|
||||
auto prev_store = var2store_.find(varId);
|
||||
if (prev_store != var2store_.end() &&
|
||||
instructions_to_save.count(prev_store->second) == 0 &&
|
||||
!context()->get_debug_info_mgr()->IsDebugDeclared(varId)) {
|
||||
!context()->get_debug_info_mgr()->IsVariableDebugDeclared(
|
||||
varId)) {
|
||||
instructions_to_kill.push_back(prev_store->second);
|
||||
modified = true;
|
||||
}
|
||||
|
@ -142,12 +142,12 @@ bool LocalSingleStoreElimPass::ProcessVariable(Instruction* var_inst) {
|
||||
// the DebugDeclare.
|
||||
uint32_t var_id = var_inst->result_id();
|
||||
if (all_rewritten &&
|
||||
context()->get_debug_info_mgr()->IsDebugDeclared(var_id)) {
|
||||
context()->get_debug_info_mgr()->IsVariableDebugDeclared(var_id)) {
|
||||
const analysis::Type* var_type =
|
||||
context()->get_type_mgr()->GetType(var_inst->type_id());
|
||||
const analysis::Type* store_type = var_type->AsPointer()->pointee_type();
|
||||
if (!(store_type->AsStruct() || store_type->AsArray())) {
|
||||
context()->get_debug_info_mgr()->AddDebugValue(
|
||||
context()->get_debug_info_mgr()->AddDebugValueIfVarDeclIsVisible(
|
||||
store_inst, var_id, store_inst->GetSingleWordInOperand(1),
|
||||
store_inst);
|
||||
context()->get_debug_info_mgr()->KillDebugDeclares(var_id);
|
||||
|
@ -511,7 +511,7 @@ void Loop::ComputeLoopStructuredOrder(
|
||||
}
|
||||
|
||||
LoopDescriptor::LoopDescriptor(IRContext* context, const Function* f)
|
||||
: loops_(), dummy_top_loop_(nullptr) {
|
||||
: loops_(), placeholder_top_loop_(nullptr) {
|
||||
PopulateList(context, f);
|
||||
}
|
||||
|
||||
@ -592,7 +592,7 @@ void LoopDescriptor::PopulateList(IRContext* context, const Function* f) {
|
||||
}
|
||||
}
|
||||
for (Loop* loop : loops_) {
|
||||
if (!loop->HasParent()) dummy_top_loop_.nested_loops_.push_back(loop);
|
||||
if (!loop->HasParent()) placeholder_top_loop_.nested_loops_.push_back(loop);
|
||||
}
|
||||
}
|
||||
|
||||
@ -986,7 +986,7 @@ void LoopDescriptor::ClearLoops() {
|
||||
// Adds a new loop nest to the descriptor set.
|
||||
Loop* LoopDescriptor::AddLoopNest(std::unique_ptr<Loop> new_loop) {
|
||||
Loop* loop = new_loop.release();
|
||||
if (!loop->HasParent()) dummy_top_loop_.nested_loops_.push_back(loop);
|
||||
if (!loop->HasParent()) placeholder_top_loop_.nested_loops_.push_back(loop);
|
||||
// Iterate from inner to outer most loop, adding basic block to loop mapping
|
||||
// as we go.
|
||||
for (Loop& current_loop :
|
||||
@ -1000,7 +1000,7 @@ Loop* LoopDescriptor::AddLoopNest(std::unique_ptr<Loop> new_loop) {
|
||||
}
|
||||
|
||||
void LoopDescriptor::RemoveLoop(Loop* loop) {
|
||||
Loop* parent = loop->GetParent() ? loop->GetParent() : &dummy_top_loop_;
|
||||
Loop* parent = loop->GetParent() ? loop->GetParent() : &placeholder_top_loop_;
|
||||
parent->nested_loops_.erase(std::find(parent->nested_loops_.begin(),
|
||||
parent->nested_loops_.end(), loop));
|
||||
std::for_each(
|
||||
|
@ -406,8 +406,8 @@ class Loop {
|
||||
// the iterators.
|
||||
bool loop_is_marked_for_removal_;
|
||||
|
||||
// This is only to allow LoopDescriptor::dummy_top_loop_ to add top level
|
||||
// loops as child.
|
||||
// This is only to allow LoopDescriptor::placeholder_top_loop_ to add top
|
||||
// level loops as child.
|
||||
friend class LoopDescriptor;
|
||||
friend class LoopUtils;
|
||||
};
|
||||
@ -430,14 +430,14 @@ class LoopDescriptor {
|
||||
// Disable copy constructor, to avoid double-free on destruction.
|
||||
LoopDescriptor(const LoopDescriptor&) = delete;
|
||||
// Move constructor.
|
||||
LoopDescriptor(LoopDescriptor&& other) : dummy_top_loop_(nullptr) {
|
||||
LoopDescriptor(LoopDescriptor&& other) : placeholder_top_loop_(nullptr) {
|
||||
// We need to take ownership of the Loop objects in the other
|
||||
// LoopDescriptor, to avoid double-free.
|
||||
loops_ = std::move(other.loops_);
|
||||
other.loops_.clear();
|
||||
basic_block_to_loop_ = std::move(other.basic_block_to_loop_);
|
||||
other.basic_block_to_loop_.clear();
|
||||
dummy_top_loop_ = std::move(other.dummy_top_loop_);
|
||||
placeholder_top_loop_ = std::move(other.placeholder_top_loop_);
|
||||
}
|
||||
|
||||
// Destructor
|
||||
@ -470,25 +470,27 @@ class LoopDescriptor {
|
||||
|
||||
// Iterators for post order depth first traversal of the loops.
|
||||
// Inner most loops will be visited first.
|
||||
inline iterator begin() { return iterator::begin(&dummy_top_loop_); }
|
||||
inline iterator end() { return iterator::end(&dummy_top_loop_); }
|
||||
inline iterator begin() { return iterator::begin(&placeholder_top_loop_); }
|
||||
inline iterator end() { return iterator::end(&placeholder_top_loop_); }
|
||||
inline const_iterator begin() const { return cbegin(); }
|
||||
inline const_iterator end() const { return cend(); }
|
||||
inline const_iterator cbegin() const {
|
||||
return const_iterator::begin(&dummy_top_loop_);
|
||||
return const_iterator::begin(&placeholder_top_loop_);
|
||||
}
|
||||
inline const_iterator cend() const {
|
||||
return const_iterator::end(&dummy_top_loop_);
|
||||
return const_iterator::end(&placeholder_top_loop_);
|
||||
}
|
||||
|
||||
// Iterators for pre-order depth first traversal of the loops.
|
||||
// Inner most loops will be visited first.
|
||||
inline pre_iterator pre_begin() { return ++pre_iterator(&dummy_top_loop_); }
|
||||
inline pre_iterator pre_begin() {
|
||||
return ++pre_iterator(&placeholder_top_loop_);
|
||||
}
|
||||
inline pre_iterator pre_end() { return pre_iterator(); }
|
||||
inline const_pre_iterator pre_begin() const { return pre_cbegin(); }
|
||||
inline const_pre_iterator pre_end() const { return pre_cend(); }
|
||||
inline const_pre_iterator pre_cbegin() const {
|
||||
return ++const_pre_iterator(&dummy_top_loop_);
|
||||
return ++const_pre_iterator(&placeholder_top_loop_);
|
||||
}
|
||||
inline const_pre_iterator pre_cend() const { return const_pre_iterator(); }
|
||||
|
||||
@ -524,14 +526,14 @@ class LoopDescriptor {
|
||||
void RemoveLoop(Loop* loop);
|
||||
|
||||
void SetAsTopLoop(Loop* loop) {
|
||||
assert(std::find(dummy_top_loop_.begin(), dummy_top_loop_.end(), loop) ==
|
||||
dummy_top_loop_.end() &&
|
||||
assert(std::find(placeholder_top_loop_.begin(), placeholder_top_loop_.end(),
|
||||
loop) == placeholder_top_loop_.end() &&
|
||||
"already registered");
|
||||
dummy_top_loop_.nested_loops_.push_back(loop);
|
||||
placeholder_top_loop_.nested_loops_.push_back(loop);
|
||||
}
|
||||
|
||||
Loop* GetDummyRootLoop() { return &dummy_top_loop_; }
|
||||
const Loop* GetDummyRootLoop() const { return &dummy_top_loop_; }
|
||||
Loop* GetPlaceholderRootLoop() { return &placeholder_top_loop_; }
|
||||
const Loop* GetPlaceholderRootLoop() const { return &placeholder_top_loop_; }
|
||||
|
||||
private:
|
||||
// TODO(dneto): This should be a vector of unique_ptr. But VisualStudio 2013
|
||||
@ -558,8 +560,8 @@ class LoopDescriptor {
|
||||
// objects.
|
||||
LoopContainerType loops_;
|
||||
|
||||
// Dummy root: this "loop" is only there to help iterators creation.
|
||||
Loop dummy_top_loop_;
|
||||
// Placeholder root: this "loop" is only there to help iterators creation.
|
||||
Loop placeholder_top_loop_;
|
||||
|
||||
std::unordered_map<uint32_t, Loop*> basic_block_to_loop_;
|
||||
|
||||
|
@ -1063,7 +1063,7 @@ LoopPeelingPass::LoopPeelingInfo::HandleInequality(CmpOperator cmp_op,
|
||||
}
|
||||
|
||||
uint32_t cast_iteration = 0;
|
||||
// sanity check: can we fit |iteration| in a uint32_t ?
|
||||
// coherence check: can we fit |iteration| in a uint32_t ?
|
||||
if (static_cast<uint64_t>(iteration) < std::numeric_limits<uint32_t>::max()) {
|
||||
cast_iteration = static_cast<uint32_t>(iteration);
|
||||
}
|
||||
|
@ -286,6 +286,9 @@ class LoopUnrollerUtilsImpl {
|
||||
// to be the actual value of the phi at that point.
|
||||
void LinkLastPhisToStart(Loop* loop) const;
|
||||
|
||||
// Kill all debug declaration instructions from |bb|.
|
||||
void KillDebugDeclares(BasicBlock* bb);
|
||||
|
||||
// A pointer to the IRContext. Used to add/remove instructions and for usedef
|
||||
// chains.
|
||||
IRContext* context_;
|
||||
@ -598,6 +601,20 @@ void LoopUnrollerUtilsImpl::FullyUnroll(Loop* loop) {
|
||||
IRContext::Analysis::kAnalysisDefUse);
|
||||
}
|
||||
|
||||
void LoopUnrollerUtilsImpl::KillDebugDeclares(BasicBlock* bb) {
|
||||
// We cannot kill an instruction inside BasicBlock::ForEachInst()
|
||||
// because it will generate dangling pointers. We use |to_be_killed|
|
||||
// to kill them after the loop.
|
||||
std::vector<Instruction*> to_be_killed;
|
||||
|
||||
bb->ForEachInst([&to_be_killed, this](Instruction* inst) {
|
||||
if (context_->get_debug_info_mgr()->IsDebugDeclare(inst)) {
|
||||
to_be_killed.push_back(inst);
|
||||
}
|
||||
});
|
||||
for (auto* inst : to_be_killed) context_->KillInst(inst);
|
||||
}
|
||||
|
||||
// Copy a given basic block, give it a new result_id, and store the new block
|
||||
// and the id mapping in the state. |preserve_instructions| is used to determine
|
||||
// whether or not this function should edit instructions other than the
|
||||
@ -608,6 +625,9 @@ void LoopUnrollerUtilsImpl::CopyBasicBlock(Loop* loop, const BasicBlock* itr,
|
||||
BasicBlock* basic_block = itr->Clone(context_);
|
||||
basic_block->SetParent(itr->GetParent());
|
||||
|
||||
// We do not want to duplicate DebugDeclare.
|
||||
KillDebugDeclares(basic_block);
|
||||
|
||||
// Assign each result a new unique ID and keep a mapping of the old ids to
|
||||
// the new ones.
|
||||
AssignNewResultIds(basic_block);
|
||||
@ -674,21 +694,21 @@ void LoopUnrollerUtilsImpl::CopyBody(Loop* loop, bool eliminate_conditions) {
|
||||
std::vector<Instruction*> inductions;
|
||||
loop->GetInductionVariables(inductions);
|
||||
for (size_t index = 0; index < inductions.size(); ++index) {
|
||||
Instruction* master_copy = inductions[index];
|
||||
Instruction* primary_copy = inductions[index];
|
||||
|
||||
assert(master_copy->result_id() != 0);
|
||||
assert(primary_copy->result_id() != 0);
|
||||
Instruction* induction_clone =
|
||||
state_.ids_to_new_inst[state_.new_inst[master_copy->result_id()]];
|
||||
state_.ids_to_new_inst[state_.new_inst[primary_copy->result_id()]];
|
||||
|
||||
state_.new_phis_.push_back(induction_clone);
|
||||
assert(induction_clone->result_id() != 0);
|
||||
|
||||
if (!state_.previous_phis_.empty()) {
|
||||
state_.new_inst[master_copy->result_id()] = GetPhiDefID(
|
||||
state_.new_inst[primary_copy->result_id()] = GetPhiDefID(
|
||||
state_.previous_phis_[index], state_.previous_latch_block_->id());
|
||||
} else {
|
||||
// Do not replace the first phi block ids.
|
||||
state_.new_inst[master_copy->result_id()] = master_copy->result_id();
|
||||
state_.new_inst[primary_copy->result_id()] = primary_copy->result_id();
|
||||
}
|
||||
}
|
||||
|
||||
@ -729,13 +749,19 @@ void LoopUnrollerUtilsImpl::FoldConditionBlock(BasicBlock* condition_block,
|
||||
Instruction& old_branch = *condition_block->tail();
|
||||
uint32_t new_target = old_branch.GetSingleWordOperand(operand_label);
|
||||
|
||||
DebugScope scope = old_branch.GetDebugScope();
|
||||
const std::vector<Instruction> lines = old_branch.dbg_line_insts();
|
||||
|
||||
context_->KillInst(&old_branch);
|
||||
// Add the new unconditional branch to the merge block.
|
||||
InstructionBuilder builder(
|
||||
context_, condition_block,
|
||||
IRContext::Analysis::kAnalysisDefUse |
|
||||
IRContext::Analysis::kAnalysisInstrToBlockMapping);
|
||||
builder.AddBranch(new_target);
|
||||
Instruction* new_branch = builder.AddBranch(new_target);
|
||||
|
||||
new_branch->set_dbg_line_insts(lines);
|
||||
new_branch->SetDebugScope(scope);
|
||||
}
|
||||
|
||||
void LoopUnrollerUtilsImpl::CloseUnrolledLoop(Loop* loop) {
|
||||
|
@ -594,8 +594,8 @@ bool LoopUnswitchPass::ProcessFunction(Function* f) {
|
||||
bool loop_changed = true;
|
||||
while (loop_changed) {
|
||||
loop_changed = false;
|
||||
for (Loop& loop :
|
||||
make_range(++TreeDFIterator<Loop>(loop_descriptor.GetDummyRootLoop()),
|
||||
for (Loop& loop : make_range(
|
||||
++TreeDFIterator<Loop>(loop_descriptor.GetPlaceholderRootLoop()),
|
||||
TreeDFIterator<Loop>())) {
|
||||
if (processed_loop.count(&loop)) continue;
|
||||
processed_loop.insert(&loop);
|
||||
|
@ -111,7 +111,7 @@ bool MergeReturnPass::ProcessStructured(
|
||||
}
|
||||
|
||||
RecordImmediateDominators(function);
|
||||
AddDummySwitchAroundFunction();
|
||||
AddSingleCaseSwitchAroundFunction();
|
||||
|
||||
std::list<BasicBlock*> order;
|
||||
cfg()->ComputeStructuredOrder(function, &*function->begin(), &order);
|
||||
@ -223,7 +223,8 @@ void MergeReturnPass::ProcessStructuredBlock(BasicBlock* block) {
|
||||
|
||||
if (tail_opcode == SpvOpReturn || tail_opcode == SpvOpReturnValue ||
|
||||
tail_opcode == SpvOpUnreachable) {
|
||||
assert(CurrentState().InBreakable() && "Should be in the dummy construct.");
|
||||
assert(CurrentState().InBreakable() &&
|
||||
"Should be in the placeholder construct.");
|
||||
BranchToBlock(block, CurrentState().BreakMergeId());
|
||||
return_blocks_.insert(block->id());
|
||||
}
|
||||
@ -408,7 +409,7 @@ bool MergeReturnPass::PredicateBlocks(
|
||||
if (!predicated->insert(block).second) break;
|
||||
// Skip structured subgraphs.
|
||||
assert(state->InBreakable() &&
|
||||
"Should be in the dummy construct at the very least.");
|
||||
"Should be in the placeholder construct at the very least.");
|
||||
Instruction* break_merge_inst = state->BreakMergeInst();
|
||||
uint32_t merge_block_id = break_merge_inst->GetSingleWordInOperand(0);
|
||||
while (state->BreakMergeId() == merge_block_id) {
|
||||
@ -768,7 +769,7 @@ void MergeReturnPass::InsertAfterElement(BasicBlock* element,
|
||||
list->insert(pos, new_element);
|
||||
}
|
||||
|
||||
void MergeReturnPass::AddDummySwitchAroundFunction() {
|
||||
void MergeReturnPass::AddSingleCaseSwitchAroundFunction() {
|
||||
CreateReturnBlock();
|
||||
CreateReturn(final_return_block_);
|
||||
|
||||
@ -776,7 +777,7 @@ void MergeReturnPass::AddDummySwitchAroundFunction() {
|
||||
cfg()->RegisterBlock(final_return_block_);
|
||||
}
|
||||
|
||||
CreateDummySwitch(final_return_block_);
|
||||
CreateSingleCaseSwitch(final_return_block_);
|
||||
}
|
||||
|
||||
BasicBlock* MergeReturnPass::CreateContinueTarget(uint32_t header_label_id) {
|
||||
@ -811,7 +812,7 @@ BasicBlock* MergeReturnPass::CreateContinueTarget(uint32_t header_label_id) {
|
||||
return new_block;
|
||||
}
|
||||
|
||||
void MergeReturnPass::CreateDummySwitch(BasicBlock* merge_target) {
|
||||
void MergeReturnPass::CreateSingleCaseSwitch(BasicBlock* merge_target) {
|
||||
// Insert the switch before any code is run. We have to split the entry
|
||||
// block to make sure the OpVariable instructions remain in the entry block.
|
||||
BasicBlock* start_block = &*function_->begin();
|
||||
|
@ -48,13 +48,13 @@ namespace opt {
|
||||
* is the final return. This block should branch to the new return block (its
|
||||
* direct successor). If the current block is within structured control flow,
|
||||
* the branch destination should be the innermost construct's merge. This
|
||||
* merge will always exist because a dummy switch is added around the
|
||||
* merge will always exist because a single case switch is added around the
|
||||
* entire function. If the merge block produces any live values it will need to
|
||||
* be predicated. While the merge is nested in structured control flow, the
|
||||
* predication path should branch to the merge block of the inner-most loop
|
||||
* (or switch if no loop) it is contained in. Once structured control flow has
|
||||
* been exited, it will be at the merge of the dummy switch, which will simply
|
||||
* return.
|
||||
* been exited, it will be at the merge of the single case switch, which will
|
||||
* simply return.
|
||||
*
|
||||
* In the final return block, the return value should be loaded and returned.
|
||||
* Memory promotion passes should be able to promote the newly introduced
|
||||
@ -73,7 +73,7 @@ namespace opt {
|
||||
* ||
|
||||
* \/
|
||||
*
|
||||
* 0 (dummy switch header)
|
||||
* 0 (single case switch header)
|
||||
* |
|
||||
* 1 (loop header)
|
||||
* / \
|
||||
@ -83,7 +83,7 @@ namespace opt {
|
||||
* / \
|
||||
* | 3 (original code in 3)
|
||||
* \ /
|
||||
* (ret) 4 (dummy switch merge)
|
||||
* (ret) 4 (single case switch merge)
|
||||
*
|
||||
* In the above (simple) example, the return originally in |2| is passed through
|
||||
* the loop merge. That merge is predicated such that the old body of the block
|
||||
@ -277,7 +277,7 @@ class MergeReturnPass : public MemPass {
|
||||
// current function where the switch and case value are both zero and the
|
||||
// default is the merge block. Returns after the switch is executed. Sets
|
||||
// |final_return_block_|.
|
||||
void AddDummySwitchAroundFunction();
|
||||
void AddSingleCaseSwitchAroundFunction();
|
||||
|
||||
// Creates a new basic block that branches to |header_label_id|. Returns the
|
||||
// new basic block. The block will be the second last basic block in the
|
||||
@ -286,7 +286,7 @@ class MergeReturnPass : public MemPass {
|
||||
|
||||
// Creates a one case switch around the executable code of the function with
|
||||
// |merge_target| as the merge node.
|
||||
void CreateDummySwitch(BasicBlock* merge_target);
|
||||
void CreateSingleCaseSwitch(BasicBlock* merge_target);
|
||||
|
||||
// Returns true if |function| has an unreachable block that is not a continue
|
||||
// target that simply branches back to the header, or a merge block containing
|
||||
|
@ -579,8 +579,8 @@ bool Optimizer::Run(const uint32_t* original_binary,
|
||||
|
||||
#ifndef NDEBUG
|
||||
// We do not keep the result id of DebugScope in struct DebugScope.
|
||||
// Instead, we assign random ids for them, which results in sanity
|
||||
// check failures. We want to skip the sanity check when the module
|
||||
// Instead, we assign random ids for them, which results in coherence
|
||||
// check failures. We want to skip the coherence check when the module
|
||||
// contains DebugScope instructions.
|
||||
if (status == opt::Pass::Status::SuccessWithoutChange &&
|
||||
!context->module()->ContainsDebugScope()) {
|
||||
|
@ -138,7 +138,7 @@ bool PrivateToLocalPass::MoveVariable(Instruction* variable,
|
||||
function->begin()->begin()->InsertBefore(move(var));
|
||||
|
||||
// Update uses where the type may have changed.
|
||||
return UpdateUses(variable->result_id());
|
||||
return UpdateUses(variable);
|
||||
}
|
||||
|
||||
uint32_t PrivateToLocalPass::GetNewType(uint32_t old_type_id) {
|
||||
@ -157,6 +157,10 @@ uint32_t PrivateToLocalPass::GetNewType(uint32_t old_type_id) {
|
||||
bool PrivateToLocalPass::IsValidUse(const Instruction* inst) const {
|
||||
// The cases in this switch have to match the cases in |UpdateUse|.
|
||||
// If we don't know how to update it, it is not valid.
|
||||
if (inst->GetOpenCL100DebugOpcode() ==
|
||||
OpenCLDebugInfo100DebugGlobalVariable) {
|
||||
return true;
|
||||
}
|
||||
switch (inst->opcode()) {
|
||||
case SpvOpLoad:
|
||||
case SpvOpStore:
|
||||
@ -175,10 +179,16 @@ bool PrivateToLocalPass::IsValidUse(const Instruction* inst) const {
|
||||
}
|
||||
}
|
||||
|
||||
bool PrivateToLocalPass::UpdateUse(Instruction* inst) {
|
||||
bool PrivateToLocalPass::UpdateUse(Instruction* inst, Instruction* user) {
|
||||
// The cases in this switch have to match the cases in |IsValidUse|. If we
|
||||
// don't think it is valid, the optimization will not view the variable as a
|
||||
// candidate, and therefore the use will not be updated.
|
||||
if (inst->GetOpenCL100DebugOpcode() ==
|
||||
OpenCLDebugInfo100DebugGlobalVariable) {
|
||||
context()->get_debug_info_mgr()->ConvertDebugGlobalToLocalVariable(inst,
|
||||
user);
|
||||
return true;
|
||||
}
|
||||
switch (inst->opcode()) {
|
||||
case SpvOpLoad:
|
||||
case SpvOpStore:
|
||||
@ -196,7 +206,7 @@ bool PrivateToLocalPass::UpdateUse(Instruction* inst) {
|
||||
context()->AnalyzeUses(inst);
|
||||
|
||||
// Update uses where the type may have changed.
|
||||
if (!UpdateUses(inst->result_id())) {
|
||||
if (!UpdateUses(inst)) {
|
||||
return false;
|
||||
}
|
||||
} break;
|
||||
@ -211,13 +221,14 @@ bool PrivateToLocalPass::UpdateUse(Instruction* inst) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PrivateToLocalPass::UpdateUses(uint32_t id) {
|
||||
bool PrivateToLocalPass::UpdateUses(Instruction* inst) {
|
||||
uint32_t id = inst->result_id();
|
||||
std::vector<Instruction*> uses;
|
||||
context()->get_def_use_mgr()->ForEachUser(
|
||||
id, [&uses](Instruction* use) { uses.push_back(use); });
|
||||
|
||||
for (Instruction* use : uses) {
|
||||
if (!UpdateUse(use)) {
|
||||
if (!UpdateUse(use, inst)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -63,8 +63,8 @@ class PrivateToLocalPass : public Pass {
|
||||
|
||||
// Updates |inst|, and any instruction dependent on |inst|, to reflect the
|
||||
// change of the base pointer now pointing to the function storage class.
|
||||
bool UpdateUse(Instruction* inst);
|
||||
bool UpdateUses(uint32_t id);
|
||||
bool UpdateUse(Instruction* inst, Instruction* user);
|
||||
bool UpdateUses(Instruction* inst);
|
||||
};
|
||||
|
||||
} // namespace opt
|
||||
|
@ -163,7 +163,7 @@ class ComputeRegisterLiveness {
|
||||
|
||||
// Propagates the register liveness information of each loop iterators.
|
||||
void DoLoopLivenessUnification() {
|
||||
for (const Loop* loop : *loop_desc_.GetDummyRootLoop()) {
|
||||
for (const Loop* loop : *loop_desc_.GetPlaceholderRootLoop()) {
|
||||
DoLoopLivenessUnification(*loop);
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,10 @@
|
||||
#include "source/opt/types.h"
|
||||
#include "source/util/make_unique.h"
|
||||
|
||||
static const uint32_t kDebugDeclareOperandLocalVariableIndex = 4;
|
||||
static const uint32_t kDebugValueOperandValueIndex = 5;
|
||||
static const uint32_t kDebugValueOperandExpressionIndex = 6;
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
@ -80,6 +84,20 @@ Pass::Status ScalarReplacementPass::ReplaceVariable(
|
||||
std::vector<Instruction*> dead;
|
||||
bool replaced_all_uses = get_def_use_mgr()->WhileEachUser(
|
||||
inst, [this, &replacements, &dead](Instruction* user) {
|
||||
if (user->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare) {
|
||||
if (ReplaceWholeDebugDeclare(user, replacements)) {
|
||||
dead.push_back(user);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (user->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugValue) {
|
||||
if (ReplaceWholeDebugValue(user, replacements)) {
|
||||
dead.push_back(user);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (!IsAnnotationInst(user->opcode())) {
|
||||
switch (user->opcode()) {
|
||||
case SpvOpLoad:
|
||||
@ -144,6 +162,58 @@ Pass::Status ScalarReplacementPass::ReplaceVariable(
|
||||
return Status::SuccessWithChange;
|
||||
}
|
||||
|
||||
bool ScalarReplacementPass::ReplaceWholeDebugDeclare(
|
||||
Instruction* dbg_decl, const std::vector<Instruction*>& replacements) {
|
||||
// Insert Deref operation to the front of the operation list of |dbg_decl|.
|
||||
Instruction* dbg_expr = context()->get_def_use_mgr()->GetDef(
|
||||
dbg_decl->GetSingleWordOperand(kDebugValueOperandExpressionIndex));
|
||||
auto* deref_expr =
|
||||
context()->get_debug_info_mgr()->DerefDebugExpression(dbg_expr);
|
||||
|
||||
// Add DebugValue instruction with Indexes operand and Deref operation.
|
||||
int32_t idx = 0;
|
||||
for (const auto* var : replacements) {
|
||||
uint32_t dbg_local_variable =
|
||||
dbg_decl->GetSingleWordOperand(kDebugDeclareOperandLocalVariableIndex);
|
||||
uint32_t index_id = context()->get_constant_mgr()->GetSIntConst(idx);
|
||||
|
||||
Instruction* added_dbg_value =
|
||||
context()->get_debug_info_mgr()->AddDebugValueWithIndex(
|
||||
dbg_local_variable,
|
||||
/*value_id=*/var->result_id(), /*expr_id=*/deref_expr->result_id(),
|
||||
index_id, /*insert_before=*/var->NextNode());
|
||||
if (added_dbg_value == nullptr) return false;
|
||||
added_dbg_value->UpdateDebugInfoFrom(dbg_decl);
|
||||
++idx;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScalarReplacementPass::ReplaceWholeDebugValue(
|
||||
Instruction* dbg_value, const std::vector<Instruction*>& replacements) {
|
||||
int32_t idx = 0;
|
||||
BasicBlock* block = context()->get_instr_block(dbg_value);
|
||||
for (auto var : replacements) {
|
||||
// Clone the DebugValue.
|
||||
std::unique_ptr<Instruction> new_dbg_value(dbg_value->Clone(context()));
|
||||
uint32_t new_id = TakeNextId();
|
||||
if (new_id == 0) return false;
|
||||
new_dbg_value->SetResultId(new_id);
|
||||
// Update 'Value' operand to the |replacements|.
|
||||
new_dbg_value->SetOperand(kDebugValueOperandValueIndex, {var->result_id()});
|
||||
// Append 'Indexes' operand.
|
||||
new_dbg_value->AddOperand(
|
||||
{SPV_OPERAND_TYPE_ID,
|
||||
{context()->get_constant_mgr()->GetSIntConst(idx)}});
|
||||
// Insert the new DebugValue to the basic block.
|
||||
auto* added_instr = dbg_value->InsertBefore(std::move(new_dbg_value));
|
||||
get_def_use_mgr()->AnalyzeInstDefUse(added_instr);
|
||||
context()->set_instr_block(added_instr, block);
|
||||
++idx;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScalarReplacementPass::ReplaceWholeLoad(
|
||||
Instruction* load, const std::vector<Instruction*>& replacements) {
|
||||
// Replaces the load of the entire composite with a load from each replacement
|
||||
@ -177,6 +247,7 @@ bool ScalarReplacementPass::ReplaceWholeLoad(
|
||||
where = where.InsertBefore(std::move(newLoad));
|
||||
get_def_use_mgr()->AnalyzeInstDefUse(&*where);
|
||||
context()->set_instr_block(&*where, block);
|
||||
where->UpdateDebugInfoFrom(load);
|
||||
loads.push_back(&*where);
|
||||
}
|
||||
|
||||
@ -195,6 +266,7 @@ bool ScalarReplacementPass::ReplaceWholeLoad(
|
||||
}
|
||||
where = where.InsertBefore(std::move(compositeConstruct));
|
||||
get_def_use_mgr()->AnalyzeInstDefUse(&*where);
|
||||
where->UpdateDebugInfoFrom(load);
|
||||
context()->set_instr_block(&*where, block);
|
||||
context()->ReplaceAllUsesWith(load->result_id(), compositeId);
|
||||
return true;
|
||||
@ -226,6 +298,7 @@ bool ScalarReplacementPass::ReplaceWholeStore(
|
||||
{SPV_OPERAND_TYPE_ID, {storeInput}},
|
||||
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {elementIndex++}}}));
|
||||
auto iter = where.InsertBefore(std::move(extract));
|
||||
iter->UpdateDebugInfoFrom(store);
|
||||
get_def_use_mgr()->AnalyzeInstDefUse(&*iter);
|
||||
context()->set_instr_block(&*iter, block);
|
||||
|
||||
@ -242,6 +315,7 @@ bool ScalarReplacementPass::ReplaceWholeStore(
|
||||
newStore->AddOperand(std::move(copy));
|
||||
}
|
||||
iter = where.InsertBefore(std::move(newStore));
|
||||
iter->UpdateDebugInfoFrom(store);
|
||||
get_def_use_mgr()->AnalyzeInstDefUse(&*iter);
|
||||
context()->set_instr_block(&*iter, block);
|
||||
}
|
||||
@ -281,6 +355,7 @@ bool ScalarReplacementPass::ReplaceAccessChain(
|
||||
Operand copy(chain->GetInOperand(i));
|
||||
replacementChain->AddOperand(std::move(copy));
|
||||
}
|
||||
replacementChain->UpdateDebugInfoFrom(chain);
|
||||
auto iter = chainIter.InsertBefore(std::move(replacementChain));
|
||||
get_def_use_mgr()->AnalyzeInstDefUse(&*iter);
|
||||
context()->set_instr_block(&*iter, context()->get_instr_block(chain));
|
||||
@ -427,6 +502,9 @@ void ScalarReplacementPass::CreateVariable(
|
||||
}
|
||||
}
|
||||
|
||||
// Update the OpenCL.DebugInfo.100 debug information.
|
||||
inst->UpdateDebugInfoFrom(varInst);
|
||||
|
||||
replacements->push_back(inst);
|
||||
}
|
||||
|
||||
@ -711,6 +789,14 @@ bool ScalarReplacementPass::CheckUses(const Instruction* inst,
|
||||
get_def_use_mgr()->ForEachUse(inst, [this, max_legal_index, stats, &ok](
|
||||
const Instruction* user,
|
||||
uint32_t index) {
|
||||
if (user->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare ||
|
||||
user->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugValue) {
|
||||
// TODO: include num_partial_accesses if it uses Fragment operation or
|
||||
// DebugValue has Indexes operand.
|
||||
stats->num_full_accesses++;
|
||||
return;
|
||||
}
|
||||
|
||||
// Annotations are check as a group separately.
|
||||
if (!IsAnnotationInst(user->opcode())) {
|
||||
switch (user->opcode()) {
|
||||
|
@ -199,6 +199,21 @@ class ScalarReplacementPass : public Pass {
|
||||
bool ReplaceWholeStore(Instruction* store,
|
||||
const std::vector<Instruction*>& replacements);
|
||||
|
||||
// Replaces the DebugDeclare to the entire composite.
|
||||
//
|
||||
// Generates a DebugValue with Deref operation for each element in the
|
||||
// scalarized variable from the original DebugDeclare. Returns true if
|
||||
// successful.
|
||||
bool ReplaceWholeDebugDeclare(Instruction* dbg_decl,
|
||||
const std::vector<Instruction*>& replacements);
|
||||
|
||||
// Replaces the DebugValue to the entire composite.
|
||||
//
|
||||
// Generates a DebugValue for each element in the scalarized variable from
|
||||
// the original DebugValue. Returns true if successful.
|
||||
bool ReplaceWholeDebugValue(Instruction* dbg_value,
|
||||
const std::vector<Instruction*>& replacements);
|
||||
|
||||
// Replaces an access chain to the composite variable with either a direct use
|
||||
// of the appropriate replacement variable or another access chain with the
|
||||
// replacement variable as the base and one fewer indexes. Returns true if
|
||||
|
@ -307,8 +307,8 @@ void SSARewriter::ProcessStore(Instruction* inst, BasicBlock* bb) {
|
||||
}
|
||||
if (pass_->IsTargetVar(var_id)) {
|
||||
WriteVariable(var_id, bb, val_id);
|
||||
pass_->context()->get_debug_info_mgr()->AddDebugValue(inst, var_id, val_id,
|
||||
inst);
|
||||
pass_->context()->get_debug_info_mgr()->AddDebugValueIfVarDeclIsVisible(
|
||||
inst, var_id, val_id, inst);
|
||||
|
||||
#if SSA_REWRITE_DEBUGGING_LEVEL > 1
|
||||
std::cerr << "\tFound store '%" << var_id << " = %" << val_id << "': "
|
||||
@ -491,7 +491,7 @@ bool SSARewriter::ApplyReplacements() {
|
||||
|
||||
// Add DebugValue for the new OpPhi instruction.
|
||||
insert_it->SetDebugScope(local_var->GetDebugScope());
|
||||
pass_->context()->get_debug_info_mgr()->AddDebugValue(
|
||||
pass_->context()->get_debug_info_mgr()->AddDebugValueIfVarDeclIsVisible(
|
||||
&*insert_it, phi_candidate->var_id(), phi_candidate->result_id(),
|
||||
&*insert_it);
|
||||
|
||||
@ -615,8 +615,6 @@ Pass::Status SSARewriter::RewriteFunctionIntoSSA(Function* fp) {
|
||||
<< fp->PrettyPrint(0) << "\n";
|
||||
#endif
|
||||
|
||||
if (modified) pass_->context()->KillDebugDeclareInsts(fp);
|
||||
|
||||
return modified ? Pass::Status::SuccessWithChange
|
||||
: Pass::Status::SuccessWithoutChange;
|
||||
}
|
||||
@ -626,6 +624,12 @@ Pass::Status SSARewritePass::Process() {
|
||||
for (auto& fn : *get_module()) {
|
||||
status =
|
||||
CombineStatus(status, SSARewriter(this).RewriteFunctionIntoSSA(&fn));
|
||||
if (status == Status::SuccessWithChange) {
|
||||
// Kill DebugDeclares for target variables.
|
||||
for (auto var_id : seen_target_vars_) {
|
||||
context()->get_debug_info_mgr()->KillDebugDeclares(var_id);
|
||||
}
|
||||
}
|
||||
if (status == Status::Failure) {
|
||||
break;
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ bool WrapOpKill::ReplaceWithFunctionCall(Instruction* inst) {
|
||||
if (call_inst == nullptr) {
|
||||
return false;
|
||||
}
|
||||
call_inst->UpdateDebugInfo(inst);
|
||||
call_inst->UpdateDebugInfoFrom(inst);
|
||||
|
||||
Instruction* return_inst = nullptr;
|
||||
uint32_t return_type_id = GetOwningFunctionsReturnType(inst);
|
||||
|
@ -77,7 +77,7 @@ endif()
|
||||
|
||||
spvtools_pch(SPIRV_TOOLS_REDUCE_SOURCES pch_source_reduce)
|
||||
|
||||
add_library(SPIRV-Tools-reduce ${SPIRV_TOOLS_REDUCE_SOURCES})
|
||||
add_library(SPIRV-Tools-reduce STATIC ${SPIRV_TOOLS_REDUCE_SOURCES})
|
||||
|
||||
spvtools_default_compile_options(SPIRV-Tools-reduce)
|
||||
target_include_directories(SPIRV-Tools-reduce
|
||||
@ -89,7 +89,7 @@ target_include_directories(SPIRV-Tools-reduce
|
||||
)
|
||||
# The reducer reuses a lot of functionality from the SPIRV-Tools library.
|
||||
target_link_libraries(SPIRV-Tools-reduce
|
||||
PUBLIC ${SPIRV_TOOLS}
|
||||
PUBLIC ${SPIRV_TOOLS}-static
|
||||
PUBLIC SPIRV-Tools-opt)
|
||||
|
||||
set_property(TARGET SPIRV-Tools-reduce PROPERTY FOLDER "SPIRV-Tools libraries")
|
||||
|
@ -91,7 +91,8 @@ void OperandToDominatingIdReductionOpportunityFinder::
|
||||
// constant. It is thus not relevant to this pass.
|
||||
continue;
|
||||
}
|
||||
// Sanity check that we don't get here if the argument is a constant.
|
||||
// Coherence check: we should not get here if the argument is a
|
||||
// constant.
|
||||
assert(!context->get_constant_mgr()->GetConstantFromInst(def));
|
||||
if (def->type_id() != candidate_dominator->type_id()) {
|
||||
// The types need to match.
|
||||
|
@ -72,8 +72,8 @@ class StructuredLoopToSelectionReductionOpportunity
|
||||
void ChangeLoopToSelection();
|
||||
|
||||
// Fixes any scenarios where, due to CFG changes, ids have uses not dominated
|
||||
// by their definitions, by changing such uses to uses of OpUndef or of dummy
|
||||
// variables.
|
||||
// by their definitions, by changing such uses to uses of OpUndef or of
|
||||
// placeholder variables.
|
||||
void FixNonDominatedIdUses();
|
||||
|
||||
// Returns true if and only if at least one of the following holds:
|
||||
|
@ -14,8 +14,6 @@
|
||||
|
||||
// Validates OpCapability instruction.
|
||||
|
||||
#include "source/val/validate.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
@ -23,6 +21,7 @@
|
||||
#include "source/diagnostic.h"
|
||||
#include "source/opcode.h"
|
||||
#include "source/val/instruction.h"
|
||||
#include "source/val/validate.h"
|
||||
#include "source/val/validation_state.h"
|
||||
|
||||
namespace spvtools {
|
||||
@ -166,7 +165,6 @@ bool IsSupportGuaranteedOpenCL_1_2(uint32_t capability, bool embedded_profile) {
|
||||
switch (capability) {
|
||||
case SpvCapabilityAddresses:
|
||||
case SpvCapabilityFloat16Buffer:
|
||||
case SpvCapabilityGroups:
|
||||
case SpvCapabilityInt16:
|
||||
case SpvCapabilityInt8:
|
||||
case SpvCapabilityKernel:
|
||||
@ -175,8 +173,6 @@ bool IsSupportGuaranteedOpenCL_1_2(uint32_t capability, bool embedded_profile) {
|
||||
return true;
|
||||
case SpvCapabilityInt64:
|
||||
return !embedded_profile;
|
||||
case SpvCapabilityPipes:
|
||||
return embedded_profile;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -187,6 +183,7 @@ bool IsSupportGuaranteedOpenCL_2_0(uint32_t capability, bool embedded_profile) {
|
||||
switch (capability) {
|
||||
case SpvCapabilityDeviceEnqueue:
|
||||
case SpvCapabilityGenericPointer:
|
||||
case SpvCapabilityGroups:
|
||||
case SpvCapabilityPipes:
|
||||
return true;
|
||||
}
|
||||
|
@ -13,11 +13,13 @@
|
||||
// limitations under the License.
|
||||
|
||||
// Validates correctness of extension SPIR-V instructions.
|
||||
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "spirv/unified1/NonSemanticClspvReflection.h"
|
||||
|
||||
#include "OpenCLDebugInfo100.h"
|
||||
#include "source/diagnostic.h"
|
||||
#include "source/enum_string_mapping.h"
|
||||
@ -180,6 +182,460 @@ spv_result_t ValidateOperandDebugType(
|
||||
<< " is not a valid debug type";
|
||||
}
|
||||
|
||||
bool IsUint32Constant(ValidationState_t& _, uint32_t id) {
|
||||
auto inst = _.FindDef(id);
|
||||
if (!inst || inst->opcode() != SpvOpConstant) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto type = _.FindDef(inst->type_id());
|
||||
if (!type || type->opcode() != SpvOpTypeInt) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (type->GetOperandAs<uint32_t>(1) != 32) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (type->GetOperandAs<uint32_t>(2) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
spv_result_t ValidateClspvReflectionKernel(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
const auto kernel_id = inst->GetOperandAs<uint32_t>(4);
|
||||
const auto kernel = _.FindDef(kernel_id);
|
||||
if (kernel->opcode() != SpvOpFunction) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Kernel does not reference a function";
|
||||
}
|
||||
|
||||
bool found_kernel = false;
|
||||
for (auto entry_point : _.entry_points()) {
|
||||
if (entry_point == kernel_id) {
|
||||
found_kernel = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found_kernel) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Kernel does not reference an entry-point";
|
||||
}
|
||||
|
||||
const auto* exec_models = _.GetExecutionModels(kernel_id);
|
||||
if (!exec_models || exec_models->empty()) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Kernel does not reference an entry-point";
|
||||
}
|
||||
for (auto exec_model : *exec_models) {
|
||||
if (exec_model != SpvExecutionModelGLCompute) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Kernel must refer only to GLCompute entry-points";
|
||||
}
|
||||
}
|
||||
|
||||
auto name = _.FindDef(inst->GetOperandAs<uint32_t>(5));
|
||||
if (!name || name->opcode() != SpvOpString) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst) << "Name must be an OpString";
|
||||
}
|
||||
|
||||
const std::string name_str = reinterpret_cast<const char*>(
|
||||
name->words().data() + name->operands()[1].offset);
|
||||
bool found = false;
|
||||
for (auto& desc : _.entry_point_descriptions(kernel_id)) {
|
||||
if (name_str == desc.name) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Name must match an entry-point for Kernel";
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateClspvReflectionArgumentInfo(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
const auto num_operands = inst->operands().size();
|
||||
if (_.GetIdOpcode(inst->GetOperandAs<uint32_t>(4)) != SpvOpString) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst) << "Name must be an OpString";
|
||||
}
|
||||
if (num_operands > 5) {
|
||||
if (_.GetIdOpcode(inst->GetOperandAs<uint32_t>(5)) != SpvOpString) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "TypeName must be an OpString";
|
||||
}
|
||||
}
|
||||
if (num_operands > 6) {
|
||||
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(6))) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "AddressQualifier must be a 32-bit unsigned integer "
|
||||
"OpConstant";
|
||||
}
|
||||
}
|
||||
if (num_operands > 7) {
|
||||
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(7))) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "AccessQualifier must be a 32-bit unsigned integer "
|
||||
"OpConstant";
|
||||
}
|
||||
}
|
||||
if (num_operands > 8) {
|
||||
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(8))) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "TypeQualifier must be a 32-bit unsigned integer "
|
||||
"OpConstant";
|
||||
}
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateKernelDecl(ValidationState_t& _, const Instruction* inst) {
|
||||
const auto decl_id = inst->GetOperandAs<uint32_t>(4);
|
||||
const auto decl = _.FindDef(decl_id);
|
||||
if (!decl || decl->opcode() != SpvOpExtInst) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Kernel must be a Kernel extended instruction";
|
||||
}
|
||||
|
||||
if (decl->GetOperandAs<uint32_t>(2) != inst->GetOperandAs<uint32_t>(2)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Kernel must be from the same extended instruction import";
|
||||
}
|
||||
|
||||
const auto ext_inst =
|
||||
decl->GetOperandAs<NonSemanticClspvReflectionInstructions>(3);
|
||||
if (ext_inst != NonSemanticClspvReflectionKernel) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Kernel must be a Kernel extended instruction";
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateArgInfo(ValidationState_t& _, const Instruction* inst,
|
||||
uint32_t info_index) {
|
||||
auto info = _.FindDef(inst->GetOperandAs<uint32_t>(info_index));
|
||||
if (!info || info->opcode() != SpvOpExtInst) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "ArgInfo must be an ArgumentInfo extended instruction";
|
||||
}
|
||||
|
||||
if (info->GetOperandAs<uint32_t>(2) != inst->GetOperandAs<uint32_t>(2)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "ArgInfo must be from the same extended instruction import";
|
||||
}
|
||||
|
||||
auto ext_inst = info->GetOperandAs<NonSemanticClspvReflectionInstructions>(3);
|
||||
if (ext_inst != NonSemanticClspvReflectionArgumentInfo) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "ArgInfo must be an ArgumentInfo extended instruction";
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateClspvReflectionArgumentBuffer(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
const auto num_operands = inst->operands().size();
|
||||
if (auto error = ValidateKernelDecl(_, inst)) {
|
||||
return error;
|
||||
}
|
||||
|
||||
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(5))) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Ordinal must be a 32-bit unsigned integer OpConstant";
|
||||
}
|
||||
|
||||
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(6))) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "DescriptorSet must be a 32-bit unsigned integer OpConstant";
|
||||
}
|
||||
|
||||
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(7))) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Binding must be a 32-bit unsigned integer OpConstant";
|
||||
}
|
||||
|
||||
if (num_operands == 9) {
|
||||
if (auto error = ValidateArgInfo(_, inst, 8)) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateClspvReflectionArgumentPodBuffer(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
const auto num_operands = inst->operands().size();
|
||||
if (auto error = ValidateKernelDecl(_, inst)) {
|
||||
return error;
|
||||
}
|
||||
|
||||
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(5))) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Ordinal must be a 32-bit unsigned integer OpConstant";
|
||||
}
|
||||
|
||||
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(6))) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "DescriptorSet must be a 32-bit unsigned integer OpConstant";
|
||||
}
|
||||
|
||||
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(7))) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Binding must be a 32-bit unsigned integer OpConstant";
|
||||
}
|
||||
|
||||
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(8))) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Offset must be a 32-bit unsigned integer OpConstant";
|
||||
}
|
||||
|
||||
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(9))) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Size must be a 32-bit unsigned integer OpConstant";
|
||||
}
|
||||
|
||||
if (num_operands == 11) {
|
||||
if (auto error = ValidateArgInfo(_, inst, 10)) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateClspvReflectionArgumentPodPushConstant(
|
||||
ValidationState_t& _, const Instruction* inst) {
|
||||
const auto num_operands = inst->operands().size();
|
||||
if (auto error = ValidateKernelDecl(_, inst)) {
|
||||
return error;
|
||||
}
|
||||
|
||||
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(5))) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Ordinal must be a 32-bit unsigned integer OpConstant";
|
||||
}
|
||||
|
||||
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(6))) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Offset must be a 32-bit unsigned integer OpConstant";
|
||||
}
|
||||
|
||||
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(7))) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Size must be a 32-bit unsigned integer OpConstant";
|
||||
}
|
||||
|
||||
if (num_operands == 9) {
|
||||
if (auto error = ValidateArgInfo(_, inst, 8)) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateClspvReflectionArgumentWorkgroup(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
const auto num_operands = inst->operands().size();
|
||||
if (auto error = ValidateKernelDecl(_, inst)) {
|
||||
return error;
|
||||
}
|
||||
|
||||
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(5))) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Ordinal must be a 32-bit unsigned integer OpConstant";
|
||||
}
|
||||
|
||||
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(6))) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "SpecId must be a 32-bit unsigned integer OpConstant";
|
||||
}
|
||||
|
||||
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(7))) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "ElemSize must be a 32-bit unsigned integer OpConstant";
|
||||
}
|
||||
|
||||
if (num_operands == 9) {
|
||||
if (auto error = ValidateArgInfo(_, inst, 8)) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateClspvReflectionSpecConstantTriple(
|
||||
ValidationState_t& _, const Instruction* inst) {
|
||||
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(4))) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "X must be a 32-bit unsigned integer OpConstant";
|
||||
}
|
||||
|
||||
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(5))) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Y must be a 32-bit unsigned integer OpConstant";
|
||||
}
|
||||
|
||||
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(6))) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Z must be a 32-bit unsigned integer OpConstant";
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateClspvReflectionSpecConstantWorkDim(
|
||||
ValidationState_t& _, const Instruction* inst) {
|
||||
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(4))) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Dim must be a 32-bit unsigned integer OpConstant";
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateClspvReflectionPushConstant(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(4))) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Offset must be a 32-bit unsigned integer OpConstant";
|
||||
}
|
||||
|
||||
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(5))) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Size must be a 32-bit unsigned integer OpConstant";
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateClspvReflectionConstantData(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(4))) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "DescriptorSet must be a 32-bit unsigned integer OpConstant";
|
||||
}
|
||||
|
||||
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(5))) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Binding must be a 32-bit unsigned integer OpConstant";
|
||||
}
|
||||
|
||||
if (_.GetIdOpcode(inst->GetOperandAs<uint32_t>(6)) != SpvOpString) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst) << "Data must be an OpString";
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateClspvReflectionSampler(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(4))) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "DescriptorSet must be a 32-bit unsigned integer OpConstant";
|
||||
}
|
||||
|
||||
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(5))) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Binding must be a 32-bit unsigned integer OpConstant";
|
||||
}
|
||||
|
||||
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(6))) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Mask must be a 32-bit unsigned integer OpConstant";
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateClspvReflectionPropertyRequiredWorkgroupSize(
|
||||
ValidationState_t& _, const Instruction* inst) {
|
||||
if (auto error = ValidateKernelDecl(_, inst)) {
|
||||
return error;
|
||||
}
|
||||
|
||||
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(5))) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "X must be a 32-bit unsigned integer OpConstant";
|
||||
}
|
||||
|
||||
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(6))) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Y must be a 32-bit unsigned integer OpConstant";
|
||||
}
|
||||
|
||||
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(7))) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Z must be a 32-bit unsigned integer OpConstant";
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateClspvReflectionInstruction(ValidationState_t& _,
|
||||
const Instruction* inst,
|
||||
uint32_t /*version*/) {
|
||||
if (!_.IsVoidType(inst->type_id())) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Return Type must be OpTypeVoid";
|
||||
}
|
||||
|
||||
auto ext_inst = inst->GetOperandAs<NonSemanticClspvReflectionInstructions>(3);
|
||||
switch (ext_inst) {
|
||||
case NonSemanticClspvReflectionKernel:
|
||||
return ValidateClspvReflectionKernel(_, inst);
|
||||
case NonSemanticClspvReflectionArgumentInfo:
|
||||
return ValidateClspvReflectionArgumentInfo(_, inst);
|
||||
case NonSemanticClspvReflectionArgumentStorageBuffer:
|
||||
case NonSemanticClspvReflectionArgumentUniform:
|
||||
case NonSemanticClspvReflectionArgumentSampledImage:
|
||||
case NonSemanticClspvReflectionArgumentStorageImage:
|
||||
case NonSemanticClspvReflectionArgumentSampler:
|
||||
return ValidateClspvReflectionArgumentBuffer(_, inst);
|
||||
case NonSemanticClspvReflectionArgumentPodStorageBuffer:
|
||||
case NonSemanticClspvReflectionArgumentPodUniform:
|
||||
return ValidateClspvReflectionArgumentPodBuffer(_, inst);
|
||||
case NonSemanticClspvReflectionArgumentPodPushConstant:
|
||||
return ValidateClspvReflectionArgumentPodPushConstant(_, inst);
|
||||
case NonSemanticClspvReflectionArgumentWorkgroup:
|
||||
return ValidateClspvReflectionArgumentWorkgroup(_, inst);
|
||||
case NonSemanticClspvReflectionSpecConstantWorkgroupSize:
|
||||
case NonSemanticClspvReflectionSpecConstantGlobalOffset:
|
||||
return ValidateClspvReflectionSpecConstantTriple(_, inst);
|
||||
case NonSemanticClspvReflectionSpecConstantWorkDim:
|
||||
return ValidateClspvReflectionSpecConstantWorkDim(_, inst);
|
||||
case NonSemanticClspvReflectionPushConstantGlobalOffset:
|
||||
case NonSemanticClspvReflectionPushConstantEnqueuedLocalSize:
|
||||
case NonSemanticClspvReflectionPushConstantGlobalSize:
|
||||
case NonSemanticClspvReflectionPushConstantRegionOffset:
|
||||
case NonSemanticClspvReflectionPushConstantNumWorkgroups:
|
||||
case NonSemanticClspvReflectionPushConstantRegionGroupOffset:
|
||||
return ValidateClspvReflectionPushConstant(_, inst);
|
||||
case NonSemanticClspvReflectionConstantDataStorageBuffer:
|
||||
case NonSemanticClspvReflectionConstantDataUniform:
|
||||
return ValidateClspvReflectionConstantData(_, inst);
|
||||
case NonSemanticClspvReflectionLiteralSampler:
|
||||
return ValidateClspvReflectionSampler(_, inst);
|
||||
case NonSemanticClspvReflectionPropertyRequiredWorkgroupSize:
|
||||
return ValidateClspvReflectionPropertyRequiredWorkgroupSize(_, inst);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
spv_result_t ValidateExtension(ValidationState_t& _, const Instruction* inst) {
|
||||
@ -2441,10 +2897,10 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
|
||||
CHECK_DEBUG_OPERAND("Local Variable",
|
||||
OpenCLDebugInfo100DebugLocalVariable, 5);
|
||||
|
||||
// TODO: We must discuss DebugDeclare.Variable of OpenCL.100.DebugInfo.
|
||||
// Currently, it says "Variable must be an id of OpVariable instruction
|
||||
// which defines the local variable.", but we want to allow
|
||||
// OpFunctionParameter as well.
|
||||
// TODO: We must discuss DebugDeclare.Variable of
|
||||
// OpenCL.100.DebugInfo. Currently, it says "Variable must be an id of
|
||||
// OpVariable instruction which defines the local variable.", but we
|
||||
// want to allow OpFunctionParameter as well.
|
||||
auto* operand = _.FindDef(inst->word(6));
|
||||
if (operand->opcode() != SpvOpVariable &&
|
||||
operand->opcode() != SpvOpFunctionParameter) {
|
||||
@ -2484,6 +2940,30 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
} else if (ext_inst_type == SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION) {
|
||||
auto import_inst = _.FindDef(inst->GetOperandAs<uint32_t>(2));
|
||||
const std::string name(reinterpret_cast<const char*>(
|
||||
import_inst->words().data() + import_inst->operands()[1].offset));
|
||||
const std::string reflection = "NonSemantic.ClspvReflection.";
|
||||
char* end_ptr;
|
||||
auto version_string = name.substr(reflection.size());
|
||||
if (version_string.empty()) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, import_inst)
|
||||
<< "Missing NonSemantic.ClspvReflection import version";
|
||||
}
|
||||
uint32_t version = static_cast<uint32_t>(
|
||||
std::strtoul(version_string.c_str(), &end_ptr, 10));
|
||||
if (end_ptr && *end_ptr != '\0') {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, import_inst)
|
||||
<< "NonSemantic.ClspvReflection import does not encode the "
|
||||
"version correctly";
|
||||
}
|
||||
if (version == 0 || version > NonSemanticClspvReflectionRevision) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, import_inst)
|
||||
<< "Unknown NonSemantic.ClspvReflection import version";
|
||||
}
|
||||
|
||||
return ValidateClspvReflectionInstruction(_, inst, version);
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
|
@ -33,7 +33,7 @@ namespace {
|
||||
// Performs compile time check that all SpvImageOperandsXXX cases are handled in
|
||||
// this module. If SpvImageOperandsXXX list changes, this function will fail the
|
||||
// build.
|
||||
// For all other purposes this is a dummy function.
|
||||
// For all other purposes this is a placeholder function.
|
||||
bool CheckAllImageOperandsHandled() {
|
||||
SpvImageOperandsMask enum_val = SpvImageOperandsBiasMask;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user