diff --git a/3rdparty/spirv-tools/CHANGES b/3rdparty/spirv-tools/CHANGES index d10f55be3..4ee7ef909 100644 --- a/3rdparty/spirv-tools/CHANGES +++ b/3rdparty/spirv-tools/CHANGES @@ -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 diff --git a/3rdparty/spirv-tools/include/generated/build-version.inc b/3rdparty/spirv-tools/include/generated/build-version.inc index 15b72c088..66955558d 100644 --- a/3rdparty/spirv-tools/include/generated/build-version.inc +++ b/3rdparty/spirv-tools/include/generated/build-version.inc @@ -1 +1 @@ -"v2020.5", "SPIRV-Tools v2020.5 aebdae4b9c5b3384041b88803e83fb244f5c8067" +"v2020.5", "SPIRV-Tools v2020.5 276598ad50d33f1d1a56311520b17390a6bed635" diff --git a/3rdparty/spirv-tools/include/generated/nonsemantic.clspvreflection.insts.inc b/3rdparty/spirv-tools/include/generated/nonsemantic.clspvreflection.insts.inc new file mode 100644 index 000000000..4f559f6ae --- /dev/null +++ b/3rdparty/spirv-tools/include/generated/nonsemantic.clspvreflection.insts.inc @@ -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}} +}; \ No newline at end of file diff --git a/3rdparty/spirv-tools/include/spirv-tools/libspirv.h b/3rdparty/spirv-tools/include/spirv-tools/libspirv.h index 84b711e49..5977f0580 100644 --- a/3rdparty/spirv-tools/include/spirv-tools/libspirv.h +++ b/3rdparty/spirv-tools/include/spirv-tools/libspirv.h @@ -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 diff --git a/3rdparty/spirv-tools/source/CMakeLists.txt b/3rdparty/spirv-tools/source/CMakeLists.txt index 708ca8483..79a81a1c0 100644 --- a/3rdparty/spirv-tools/source/CMakeLists.txt +++ b/3rdparty/spirv-tools/source/CMakeLists.txt @@ -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 $ $ 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) @@ -374,18 +378,26 @@ target_compile_definitions(${SPIRV_TOOLS}-shared PRIVATE SPIRV_TOOLS_IMPLEMENTATION PUBLIC SPIRV_TOOLS_SHAREDLIB ) -add_dependencies( ${SPIRV_TOOLS}-shared core_tables enum_string_mapping extinst_tables ) +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}) diff --git a/3rdparty/spirv-tools/source/ext_inst.cpp b/3rdparty/spirv-tools/source/ext_inst.cpp index e69c3c9b7..3471ebe0d 100644 --- a/3rdparty/spirv-tools/source/ext_inst.cpp +++ b/3rdparty/spirv-tools/source/ext_inst.cpp @@ -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; diff --git a/3rdparty/spirv-tools/source/extinst.debuginfo.grammar.json b/3rdparty/spirv-tools/source/extinst.debuginfo.grammar.json deleted file mode 100644 index 9212f6f48..000000000 --- a/3rdparty/spirv-tools/source/extinst.debuginfo.grammar.json +++ /dev/null @@ -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" } - ] - } - ] - } - ] -} diff --git a/3rdparty/spirv-tools/source/extinst.opencl.debuginfo.100.grammar.json b/3rdparty/spirv-tools/source/extinst.opencl.debuginfo.100.grammar.json deleted file mode 100644 index 08062be4f..000000000 --- a/3rdparty/spirv-tools/source/extinst.opencl.debuginfo.100.grammar.json +++ /dev/null @@ -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" - } - ] - } - ] -} diff --git a/3rdparty/spirv-tools/source/extinst.spv-amd-gcn-shader.grammar.json b/3rdparty/spirv-tools/source/extinst.spv-amd-gcn-shader.grammar.json deleted file mode 100644 index e18251bba..000000000 --- a/3rdparty/spirv-tools/source/extinst.spv-amd-gcn-shader.grammar.json +++ /dev/null @@ -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" ] - } - ] -} diff --git a/3rdparty/spirv-tools/source/extinst.spv-amd-shader-ballot.grammar.json b/3rdparty/spirv-tools/source/extinst.spv-amd-shader-ballot.grammar.json deleted file mode 100644 index 62a470eeb..000000000 --- a/3rdparty/spirv-tools/source/extinst.spv-amd-shader-ballot.grammar.json +++ /dev/null @@ -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" ] - } - ] -} diff --git a/3rdparty/spirv-tools/source/extinst.spv-amd-shader-explicit-vertex-parameter.grammar.json b/3rdparty/spirv-tools/source/extinst.spv-amd-shader-explicit-vertex-parameter.grammar.json deleted file mode 100644 index e156b1b6f..000000000 --- a/3rdparty/spirv-tools/source/extinst.spv-amd-shader-explicit-vertex-parameter.grammar.json +++ /dev/null @@ -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" ] - } - ] -} diff --git a/3rdparty/spirv-tools/source/extinst.spv-amd-shader-trinary-minmax.grammar.json b/3rdparty/spirv-tools/source/extinst.spv-amd-shader-trinary-minmax.grammar.json deleted file mode 100644 index c681976fe..000000000 --- a/3rdparty/spirv-tools/source/extinst.spv-amd-shader-trinary-minmax.grammar.json +++ /dev/null @@ -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" ] - } - ] -} diff --git a/3rdparty/spirv-tools/source/fuzz/CMakeLists.txt b/3rdparty/spirv-tools/source/fuzz/CMakeLists.txt index f1309f3e5..150ee314d 100644 --- a/3rdparty/spirv-tools/source/fuzz/CMakeLists.txt +++ b/3rdparty/spirv-tools/source/fuzz/CMakeLists.txt @@ -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) diff --git a/3rdparty/spirv-tools/source/fuzz/fact_manager.cpp b/3rdparty/spirv-tools/source/fuzz/fact_manager.cpp index 6dff669f2..a2fec768f 100644 --- a/3rdparty/spirv-tools/source/fuzz/fact_manager.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fact_manager.cpp @@ -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); } @@ -776,7 +793,7 @@ void FactManager::DataSynonymAndIdEquationFacts:: ComputeCompositeDataSynonymFacts(const protobufs::DataDescriptor& dd1, const protobufs::DataDescriptor& dd2, opt::IRContext* context) { - // Check whether this is a synonym about composite objects. If it is, + // Check whether this is a synonym about composite objects. If it is, // we can recursively add synonym facts about their associated sub-components. // Get the type of the object referred to by the first data descriptor in the @@ -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_1 = type_1->AsVector()->element_type(); - type_2 = type_2->AsVector()->element_type(); + + type_a = vector_a->element_type(); + type_b = vector_b->element_type(); } - // 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(); + + 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; + } + }; + + // 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 diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer.cpp index f54959000..ae4332bde 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer.cpp @@ -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( &final_passes, ir_context.get(), &transformation_context, &fuzzer_context, transformation_sequence_out); + MaybeAddPass( + &final_passes, ir_context.get(), &transformation_context, &fuzzer_context, + transformation_sequence_out); MaybeAddPass( &final_passes, ir_context.get(), &transformation_context, &fuzzer_context, transformation_sequence_out); diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_context.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_context.cpp index 205e1903a..dacdc58fd 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_context.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_context.cpp @@ -67,6 +67,8 @@ const std::pair kChanceOfGoingDeeperWhenMakingAccessChain = {50, 95}; const std::pair kChanceOfInterchangingZeroLikeConstants = { 10, 90}; +const std::pair + kChanceOfInterchangingSignednessOfIntegerOperands = {10, 90}; const std::pair kChanceOfInvertingComparisonOperators = { 20, 50}; const std::pair kChanceOfMakingDonorLivesafe = {40, 60}; @@ -84,6 +86,8 @@ const std::pair kChanceOfReplacingCopyObjectWithStoreLoad = const std::pair kChanceOfReplacingIdWithSynonym = {10, 90}; const std::pair kChanceOfReplacingLinearAlgebraInstructions = {10, 90}; +const std::pair kChanceOfReplacingLoadStoreWithCopyMemory = + {20, 90}; const std::pair kChanceOfReplacingParametersWithGlobals = { 30, 70}; const std::pair 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_ = diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_context.h b/3rdparty/spirv-tools/source/fuzz/fuzzer_context.h index 8fc6c15f2..24aad0ce1 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_context.h +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_context.h @@ -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_; diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp index ebd88d010..8d1b28cca 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp @@ -17,6 +17,7 @@ #include #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>* + 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 diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h index d34de5dc2..2777f24c7 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h @@ -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& 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>* + uses_to_replace); + private: opt::IRContext* ir_context_; TransformationContext* transformation_context_; diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_access_chains.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_access_chains.cpp index cc4c296f8..11155f23d 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_access_chains.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_access_chains.cpp @@ -92,6 +92,11 @@ void FuzzerPassAddAccessChains::Apply() { relevant_pointer_instructions[GetFuzzerContext()->RandomIndex( relevant_pointer_instructions)]; std::vector index_ids; + + // Each index accessing a non-struct composite will be clamped, thus + // needing a pair of fresh ids + std::vector> 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 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)); }); } diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.cpp index dc811e620..9170ada22 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.cpp @@ -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 { diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp index 2808ad561..bb676e896 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp @@ -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 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,7 +91,12 @@ void FuzzerPassApplyIdSynonyms::Apply() { // Exclude the fact that the id is synonymous with itself. continue; } - synonyms_to_try.push_back(data_descriptor); + + 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 = @@ -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 diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.h b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.h index 1a9213db1..2feb65c20 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.h +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.h @@ -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 diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_donate_modules.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_donate_modules.cpp index 74bd7ed11..aa0e24301 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_donate_modules.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_donate_modules.cpp @@ -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 = diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp new file mode 100644 index 000000000..6c3aa7bfb --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp @@ -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> 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(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> 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 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 diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h new file mode 100644 index 000000000..48242189d --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h @@ -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_ diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp index 727132e55..8bd670f3a 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp @@ -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>* - 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)); diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h index 4fcc44e0b..4ea990a40 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h @@ -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>* - uses_to_replace); }; } // namespace fuzz diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_outline_functions.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_outline_functions.cpp index e4281d107..c2c254e85 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_outline_functions.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_outline_functions.cpp @@ -17,7 +17,9 @@ #include #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); diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp index 1e5d697f7..984b7c342 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp @@ -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; } diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp new file mode 100644 index 000000000..7690ac41a --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp @@ -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> + op_load_store_pairs; + + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + // A hash map storing potential OpLoad instructions. + std::unordered_map 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 diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h new file mode 100644 index 000000000..db09500c5 --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h @@ -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 diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp index e49eacb7a..86d6d06be 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp @@ -98,7 +98,7 @@ void FuzzerPassReplaceParamsWithStruct::Apply() { parameter_id.push_back(params[index]->result_id()); } - std::unordered_map caller_id_to_fresh_id; + std::map caller_id_to_fresh_id; for (const auto* inst : fuzzerutil::GetCallers(GetIRContext(), function.result_id())) { caller_id_to_fresh_id[inst->result_id()] = diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp index 953685a51..b2d568183 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp @@ -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 binary; context->module()->ToBinary(&binary, false); @@ -735,6 +757,20 @@ std::vector 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 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 RepeatedUInt32PairToMap( + const google::protobuf::RepeatedPtrField& data) { + std::map result; + + for (const auto& entry : data) { + result[entry.first()] = entry.second(); + } + + return result; +} + +google::protobuf::RepeatedPtrField +MapToRepeatedUInt32Pair(const std::map& data) { + google::protobuf::RepeatedPtrField 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 diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_util.h b/3rdparty/spirv-tools/source/fuzz/fuzzer_util.h index 72d83f1ae..343c94e80 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_util.h +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_util.h @@ -15,6 +15,7 @@ #ifndef SOURCE_FUZZ_FUZZER_UTIL_H_ #define SOURCE_FUZZ_FUZZER_UTIL_H_ +#include #include #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& arr, uint32_t lo, std::vector 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 GetCallers(opt::IRContext* ir_context, @@ -394,6 +411,14 @@ uint32_t MaybeGetIntegerConstant( const std::vector& 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 RepeatedUInt32PairToMap( + const google::protobuf::RepeatedPtrField& data); + +// Converts a map into a repeated field of UInt32Pair. +google::protobuf::RepeatedPtrField +MapToRepeatedUInt32Pair(const std::map& data); + } // namespace fuzzerutil } // namespace fuzz diff --git a/3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto b/3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto index 968e08495..43219a1df 100644 --- a/3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto +++ b/3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto @@ -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 caller_id_to_fresh_composite_id = 4; + repeated UInt32Pair caller_id_to_fresh_composite_id = 4; } diff --git a/3rdparty/spirv-tools/source/fuzz/shrinker.cpp b/3rdparty/spirv-tools/source/fuzz/shrinker.cpp index 85a06fd83..829df63ee 100644 --- a/3rdparty/spirv-tools/source/fuzz/shrinker.cpp +++ b/3rdparty/spirv-tools/source/fuzz/shrinker.cpp @@ -121,11 +121,13 @@ 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(), - ¤t_best_binary, ¤t_best_transformations) != + 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( + 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(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,11 +197,11 @@ Shrinker::ShrinkerResultStatus Shrinker::Run( // transformations inapplicable. std::vector 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(), - &next_binary, &next_transformation_sequence) != + if (replayer.Run( + binary_in, initial_facts, transformations_with_chunk_removed, + static_cast( + 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. return ShrinkerResultStatus::kReplayFailed; diff --git a/3rdparty/spirv-tools/source/fuzz/transformation.cpp b/3rdparty/spirv-tools/source/fuzz/transformation.cpp index a9fa61130..8e03279ca 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation.cpp @@ -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::FromMessage( case protobufs::Transformation::TransformationCase::kAddLocalVariable: return MakeUnique( message.add_local_variable()); + case protobufs::Transformation::TransformationCase::kAddLoopPreheader: + return MakeUnique( + message.add_loop_preheader()); case protobufs::Transformation::TransformationCase:: kAddNoContractionDecoration: return MakeUnique( @@ -233,6 +238,10 @@ std::unique_ptr Transformation::FromMessage( kReplaceLinearAlgebraInstruction: return MakeUnique( message.replace_linear_algebra_instruction()); + case protobufs::Transformation::TransformationCase:: + kReplaceLoadStoreWithCopyMemory: + return MakeUnique( + message.replace_load_store_with_copy_memory()); case protobufs::Transformation::TransformationCase:: kReplaceParamsWithStruct: return MakeUnique( diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_access_chain.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_access_chain.cpp index f805bab71..33668694f 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_access_chain.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_access_chain.cpp @@ -29,7 +29,8 @@ TransformationAccessChain::TransformationAccessChain( TransformationAccessChain::TransformationAccessChain( uint32_t fresh_id, uint32_t pointer_id, const std::vector& index_id, - const protobufs::InstructionDescriptor& instruction_to_insert_before) { + const protobufs::InstructionDescriptor& instruction_to_insert_before, + const std::vector>& 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 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 maybe_index_value = - GetIndexValue(ir_context, index_id); - if (!maybe_index_value.first) { - // There was no integer: this index is no good. - return false; + // 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( + 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( + 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(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( @@ -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 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 diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_access_chain.h b/3rdparty/spirv-tools/source/fuzz/transformation_access_chain.h index 9306a596e..db5b8e675 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_access_chain.h +++ b/3rdparty/spirv-tools/source/fuzz/transformation_access_chain.h @@ -33,20 +33,28 @@ class TransformationAccessChain : public Transformation { TransformationAccessChain( uint32_t fresh_id, uint32_t pointer_id, const std::vector& index_id, - const protobufs::InstructionDescriptor& instruction_to_insert_before); + const protobufs::InstructionDescriptor& instruction_to_insert_before, + const std::vector>& 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 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_; }; diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_add_function.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_add_function.cpp index 90276ed0b..66b379ef6 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_add_function.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_add_function.cpp @@ -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) { diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_add_function.h b/3rdparty/spirv-tools/source/fuzz/transformation_add_function.h index 5af197b61..4a84c705b 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_add_function.h +++ b/3rdparty/spirv-tools/source/fuzz/transformation_add_function.h @@ -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. diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_add_loop_preheader.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_add_loop_preheader.cpp new file mode 100644 index 000000000..eec71da49 --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/transformation_add_loop_preheader.cpp @@ -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 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 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 preheader = MakeUnique( + std::unique_ptr(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 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( + 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( + new opt::Instruction(ir_context, SpvOpBranch, 0, 0, + std::initializer_list{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 diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_add_loop_preheader.h b/3rdparty/spirv-tools/source/fuzz/transformation_add_loop_preheader.h new file mode 100644 index 000000000..2143e3f7f --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/transformation_add_loop_preheader.h @@ -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 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 diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_outline_function.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_outline_function.cpp index 05fd923cd..9257209b5 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_outline_function.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_outline_function.cpp @@ -21,20 +21,6 @@ namespace spvtools { namespace fuzz { -namespace { - -std::map PairSequenceToMap( - const google::protobuf::RepeatedPtrField& - pair_sequence) { - std::map 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 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 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 input_id_to_fresh_id_map = - PairSequenceToMap(message_.input_id_to_fresh_id()); - std::map 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); diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_outline_function.h b/3rdparty/spirv-tools/source/fuzz/transformation_outline_function.h index ba439c842..dac434641 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_outline_function.h +++ b/3rdparty/spirv-tools/source/fuzz/transformation_outline_function.h @@ -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. diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_record_synonymous_constants.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_record_synonymous_constants.cpp index b1568bffb..a93e1d429 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_record_synonymous_constants.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_record_synonymous_constants.cpp @@ -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(); diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_record_synonymous_constants.h b/3rdparty/spirv-tools/source/fuzz/transformation_record_synonymous_constants.h index b28eeb3b7..8cff0cdab 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_record_synonymous_constants.h +++ b/3rdparty/spirv-tools/source/fuzz/transformation_record_synonymous_constants.h @@ -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; diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.cpp index e427f3c36..fbbeab2e6 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.cpp @@ -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 diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.h b/3rdparty/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.h index a5a9dfda1..fe3fac052 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.h +++ b/3rdparty/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.h @@ -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_; }; diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.cpp index 76f083bea..e78573c7b 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.cpp @@ -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 result_column_ids(matrix_row_count); + for (uint32_t i = 0; i < matrix_row_count; i++) { + std::vector 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( + 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( + 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( + 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 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( + 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 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( + 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( + 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( + 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 { diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.h b/3rdparty/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.h index 530c1f203..05ebdd7fc 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.h +++ b/3rdparty/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.h @@ -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; diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_replace_load_store_with_copy_memory.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_replace_load_store_with_copy_memory.cpp new file mode 100644 index 000000000..52964bfa6 --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/transformation_replace_load_store_with_copy_memory.cpp @@ -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( + 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 diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_replace_load_store_with_copy_memory.h b/3rdparty/spirv-tools/source/fuzz/transformation_replace_load_store_with_copy_memory.h new file mode 100644 index 000000000..0fb9a0991 --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/transformation_replace_load_store_with_copy_memory.h @@ -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 diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_replace_parameter_with_global.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_replace_parameter_with_global.cpp index f680b63bc..9db2434b8 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_replace_parameter_with_global.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_replace_parameter_with_global.cpp @@ -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. { diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_replace_params_with_struct.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_replace_params_with_struct.cpp index a2aaa3aa8..7e7641541 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_replace_params_with_struct.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_replace_params_with_struct.cpp @@ -28,8 +28,7 @@ TransformationReplaceParamsWithStruct::TransformationReplaceParamsWithStruct( TransformationReplaceParamsWithStruct::TransformationReplaceParamsWithStruct( const std::vector& parameter_id, uint32_t fresh_function_type_id, uint32_t fresh_parameter_id, - const std::unordered_map& - caller_id_to_fresh_composite_id) { + const std::map& 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 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( 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(i)}}})); - function->RemoveParameter(param_inst->result_id()); + fuzzerutil::RemoveParameter(ir_context, param_inst->result_id()); } // Update function's type. diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_replace_params_with_struct.h b/3rdparty/spirv-tools/source/fuzz/transformation_replace_params_with_struct.h index 0ff73405e..d2ce20452 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_replace_params_with_struct.h +++ b/3rdparty/spirv-tools/source/fuzz/transformation_replace_params_with_struct.h @@ -15,7 +15,7 @@ #ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMS_WITH_STRUCT_H_ #define SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMS_WITH_STRUCT_H_ -#include +#include #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/transformation.h" @@ -33,8 +33,7 @@ class TransformationReplaceParamsWithStruct : public Transformation { TransformationReplaceParamsWithStruct( const std::vector& parameter_id, uint32_t fresh_function_type_id, uint32_t fresh_parameter_id, - const std::unordered_map& - caller_id_to_fresh_composite_id); + const std::map& 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 diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_set_loop_control.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_set_loop_control.cpp index 845ac69e3..6cf2104d4 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_set_loop_control.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_set_loop_control.cpp @@ -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 | diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_split_block.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_split_block.cpp index b020d98a0..3c437e432 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_split_block.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_split_block.cpp @@ -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()}); }); diff --git a/3rdparty/spirv-tools/source/link/CMakeLists.txt b/3rdparty/spirv-tools/source/link/CMakeLists.txt index d3083192c..bb058ea22 100644 --- a/3rdparty/spirv-tools/source/link/CMakeLists.txt +++ b/3rdparty/spirv-tools/source/link/CMakeLists.txt @@ -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 ) diff --git a/3rdparty/spirv-tools/source/name_mapper.cpp b/3rdparty/spirv-tools/source/name_mapper.cpp index 5525bbc5f..eb08f8fed 100644 --- a/3rdparty/spirv-tools/source/name_mapper.cpp +++ b/3rdparty/spirv-tools/source/name_mapper.cpp @@ -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); } } diff --git a/3rdparty/spirv-tools/source/operand.cpp b/3rdparty/spirv-tools/source/operand.cpp index 09105958e..7b2b98f2d 100644 --- a/3rdparty/spirv-tools/source/operand.cpp +++ b/3rdparty/spirv-tools/source/operand.cpp @@ -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, diff --git a/3rdparty/spirv-tools/source/opt/CMakeLists.txt b/3rdparty/spirv-tools/source/opt/CMakeLists.txt index 0047c3467..090aeac56 100644 --- a/3rdparty/spirv-tools/source/opt/CMakeLists.txt +++ b/3rdparty/spirv-tools/source/opt/CMakeLists.txt @@ -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) diff --git a/3rdparty/spirv-tools/source/opt/aggressive_dead_code_elim_pass.cpp b/3rdparty/spirv-tools/source/opt/aggressive_dead_code_elim_pass.cpp index b75578744..71bbed1d5 100644 --- a/3rdparty/spirv-tools/source/opt/aggressive_dead_code_elim_pass.cpp +++ b/3rdparty/spirv-tools/source/opt/aggressive_dead_code_elim_pass.cpp @@ -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_) { diff --git a/3rdparty/spirv-tools/source/opt/constants.cpp b/3rdparty/spirv-tools/source/opt/constants.cpp index 6057356cb..cf24295d1 100644 --- a/3rdparty/spirv-tools/source/opt/constants.cpp +++ b/3rdparty/spirv-tools/source/opt/constants.cpp @@ -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(val)}); + return GetDefiningInstruction(c)->result_id(); +} + std::vector Constant::GetVectorComponents( analysis::ConstantManager* const_mgr) const { std::vector components; diff --git a/3rdparty/spirv-tools/source/opt/constants.h b/3rdparty/spirv-tools/source/opt/constants.h index 680e69eac..c33bb9737 100644 --- a/3rdparty/spirv-tools/source/opt/constants.h +++ b/3rdparty/spirv-tools/source/opt/constants.h @@ -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 diff --git a/3rdparty/spirv-tools/source/opt/debug_info_manager.cpp b/3rdparty/spirv-tools/source/opt/debug_info_manager.cpp index cc631fc10..b91ef43cc 100644 --- a/3rdparty/spirv-tools/source/opt/debug_info_manager.cpp +++ b/3rdparty/spirv-tools/source/opt/debug_info_manager.cpp @@ -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,9 +449,47 @@ 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) { +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 new_dbg_value(new Instruction( + context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(), + result_id, + { + {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(OpenCLDebugInfo100DebugValue)}}, + {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, + {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}}); + } + + 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; @@ -456,36 +497,6 @@ void DebugInfoManager::AddDebugValue(Instruction* scope_and_line, for (auto* dbg_decl_or_val : dbg_decl_itr->second) { if (!IsDeclareVisibleToInstr(dbg_decl_or_val, instr_scope_id)) continue; - uint32_t result_id = context()->TakeNextId(); - std::unique_ptr new_dbg_value(new Instruction( - context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(), - result_id, - { - {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(OpenCLDebugInfo100DebugValue)}}, - {spv_operand_type_t::SPV_OPERAND_TYPE_ID, - {dbg_decl_or_val->GetSingleWordOperand( - kDebugValueOperandLocalVariableIndex)}}, - {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {value_id}}, - {spv_operand_type_t::SPV_OPERAND_TYPE_ID, - {GetEmptyDebugExpression()->result_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)}}); - } - } - // 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 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(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; } diff --git a/3rdparty/spirv-tools/source/opt/debug_info_manager.h b/3rdparty/spirv-tools/source/opt/debug_info_manager.h index ff91b5c54..cdafc658c 100644 --- a/3rdparty/spirv-tools/source/opt/debug_info_manager.h +++ b/3rdparty/spirv-tools/source/opt/debug_info_manager.h @@ -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_; } diff --git a/3rdparty/spirv-tools/source/opt/dominator_tree.cpp b/3rdparty/spirv-tools/source/opt/dominator_tree.cpp index 7e61506b9..55287f444 100644 --- a/3rdparty/spirv-tools/source/opt/dominator_tree.cpp +++ b/3rdparty/spirv-tools/source/opt/dominator_tree.cpp @@ -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. @@ -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 BasicBlockSuccessorHelper::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 void BasicBlockSuccessorHelper::CreateSuccessorMap( - Function& f, const BasicBlock* dummy_start_node) { + Function& f, const BasicBlock* placeholder_start_node) { std::map 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::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::CreateSuccessorMap( pred_list.push_back(succ); }); } else { - successors_[dummy_start_node].push_back(&bb); - predecessors_[&bb].push_back(const_cast(dummy_start_node)); + successors_[placeholder_start_node].push_back(&bb); + predecessors_[&bb].push_back( + const_cast(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(dummy_start_node)); + const_cast(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>* 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 helper{ - *const_cast(f), dummy_start_node, postdominator_}; + *const_cast(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::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> edges; - GetDominatorEdges(f, dummy_start_node, &edges); + GetDominatorEdges(f, placeholder_start_node, &edges); // Transform the vector 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"; diff --git a/3rdparty/spirv-tools/source/opt/instruction.h b/3rdparty/spirv-tools/source/opt/instruction.h index e99a5ae8f..509c42fa3 100644 --- a/3rdparty/spirv-tools/source/opt/instruction.h +++ b/3rdparty/spirv-tools/source/opt/instruction.h @@ -246,6 +246,11 @@ class Instruction : public utils::IntrusiveNodeBase { // 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& 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 { 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()) diff --git a/3rdparty/spirv-tools/source/opt/ir_builder.h b/3rdparty/spirv-tools/source/opt/ir_builder.h index b0c1d2ec9..fe5feff56 100644 --- a/3rdparty/spirv-tools/source/opt/ir_builder.h +++ b/3rdparty/spirv-tools/source/opt/ir_builder.h @@ -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_) diff --git a/3rdparty/spirv-tools/source/opt/ir_context.cpp b/3rdparty/spirv-tools/source/opt/ir_context.cpp index 0791097c5..a56ff06c5 100644 --- a/3rdparty/spirv-tools/source/opt/ir_context.cpp +++ b/3rdparty/spirv-tools/source/opt/ir_context.cpp @@ -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; }); diff --git a/3rdparty/spirv-tools/source/opt/ir_context.h b/3rdparty/spirv-tools/source/opt/ir_context.h index 37be83653..b19365741 100644 --- a/3rdparty/spirv-tools/source/opt/ir_context.h +++ b/3rdparty/spirv-tools/source/opt/ir_context.h @@ -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; } diff --git a/3rdparty/spirv-tools/source/opt/local_single_block_elim_pass.cpp b/3rdparty/spirv-tools/source/opt/local_single_block_elim_pass.cpp index bd5d75101..511123697 100644 --- a/3rdparty/spirv-tools/source/opt/local_single_block_elim_pass.cpp +++ b/3rdparty/spirv-tools/source/opt/local_single_block_elim_pass.cpp @@ -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; } diff --git a/3rdparty/spirv-tools/source/opt/local_single_store_elim_pass.cpp b/3rdparty/spirv-tools/source/opt/local_single_store_elim_pass.cpp index 238410755..eb325dd3b 100644 --- a/3rdparty/spirv-tools/source/opt/local_single_store_elim_pass.cpp +++ b/3rdparty/spirv-tools/source/opt/local_single_store_elim_pass.cpp @@ -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); diff --git a/3rdparty/spirv-tools/source/opt/loop_descriptor.cpp b/3rdparty/spirv-tools/source/opt/loop_descriptor.cpp index ed0dd28f4..72f405c2a 100644 --- a/3rdparty/spirv-tools/source/opt/loop_descriptor.cpp +++ b/3rdparty/spirv-tools/source/opt/loop_descriptor.cpp @@ -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 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 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( diff --git a/3rdparty/spirv-tools/source/opt/loop_descriptor.h b/3rdparty/spirv-tools/source/opt/loop_descriptor.h index 6e2b82896..4b4f8bc75 100644 --- a/3rdparty/spirv-tools/source/opt/loop_descriptor.h +++ b/3rdparty/spirv-tools/source/opt/loop_descriptor.h @@ -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 basic_block_to_loop_; diff --git a/3rdparty/spirv-tools/source/opt/loop_peeling.cpp b/3rdparty/spirv-tools/source/opt/loop_peeling.cpp index b640542d3..556442dcd 100644 --- a/3rdparty/spirv-tools/source/opt/loop_peeling.cpp +++ b/3rdparty/spirv-tools/source/opt/loop_peeling.cpp @@ -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(iteration) < std::numeric_limits::max()) { cast_iteration = static_cast(iteration); } diff --git a/3rdparty/spirv-tools/source/opt/loop_unroller.cpp b/3rdparty/spirv-tools/source/opt/loop_unroller.cpp index 40cf6bc2a..6cdced464 100644 --- a/3rdparty/spirv-tools/source/opt/loop_unroller.cpp +++ b/3rdparty/spirv-tools/source/opt/loop_unroller.cpp @@ -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 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 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 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) { diff --git a/3rdparty/spirv-tools/source/opt/loop_unswitch_pass.cpp b/3rdparty/spirv-tools/source/opt/loop_unswitch_pass.cpp index 502fc6b68..d805ecf3c 100644 --- a/3rdparty/spirv-tools/source/opt/loop_unswitch_pass.cpp +++ b/3rdparty/spirv-tools/source/opt/loop_unswitch_pass.cpp @@ -594,9 +594,9 @@ bool LoopUnswitchPass::ProcessFunction(Function* f) { bool loop_changed = true; while (loop_changed) { loop_changed = false; - for (Loop& loop : - make_range(++TreeDFIterator(loop_descriptor.GetDummyRootLoop()), - TreeDFIterator())) { + for (Loop& loop : make_range( + ++TreeDFIterator(loop_descriptor.GetPlaceholderRootLoop()), + TreeDFIterator())) { if (processed_loop.count(&loop)) continue; processed_loop.insert(&loop); diff --git a/3rdparty/spirv-tools/source/opt/merge_return_pass.cpp b/3rdparty/spirv-tools/source/opt/merge_return_pass.cpp index 2421c2cad..b43eb3178 100644 --- a/3rdparty/spirv-tools/source/opt/merge_return_pass.cpp +++ b/3rdparty/spirv-tools/source/opt/merge_return_pass.cpp @@ -111,7 +111,7 @@ bool MergeReturnPass::ProcessStructured( } RecordImmediateDominators(function); - AddDummySwitchAroundFunction(); + AddSingleCaseSwitchAroundFunction(); std::list 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(); diff --git a/3rdparty/spirv-tools/source/opt/merge_return_pass.h b/3rdparty/spirv-tools/source/opt/merge_return_pass.h index fe85557af..06a3e7b55 100644 --- a/3rdparty/spirv-tools/source/opt/merge_return_pass.h +++ b/3rdparty/spirv-tools/source/opt/merge_return_pass.h @@ -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 diff --git a/3rdparty/spirv-tools/source/opt/optimizer.cpp b/3rdparty/spirv-tools/source/opt/optimizer.cpp index 25adee976..403231ab7 100644 --- a/3rdparty/spirv-tools/source/opt/optimizer.cpp +++ b/3rdparty/spirv-tools/source/opt/optimizer.cpp @@ -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()) { diff --git a/3rdparty/spirv-tools/source/opt/private_to_local_pass.cpp b/3rdparty/spirv-tools/source/opt/private_to_local_pass.cpp index 6df690dc0..dd6cbbdef 100644 --- a/3rdparty/spirv-tools/source/opt/private_to_local_pass.cpp +++ b/3rdparty/spirv-tools/source/opt/private_to_local_pass.cpp @@ -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 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; } } diff --git a/3rdparty/spirv-tools/source/opt/private_to_local_pass.h b/3rdparty/spirv-tools/source/opt/private_to_local_pass.h index 3f9135c09..c6127d67f 100644 --- a/3rdparty/spirv-tools/source/opt/private_to_local_pass.h +++ b/3rdparty/spirv-tools/source/opt/private_to_local_pass.h @@ -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 diff --git a/3rdparty/spirv-tools/source/opt/register_pressure.cpp b/3rdparty/spirv-tools/source/opt/register_pressure.cpp index cb2467441..5750c6d40 100644 --- a/3rdparty/spirv-tools/source/opt/register_pressure.cpp +++ b/3rdparty/spirv-tools/source/opt/register_pressure.cpp @@ -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); } } diff --git a/3rdparty/spirv-tools/source/opt/scalar_replacement_pass.cpp b/3rdparty/spirv-tools/source/opt/scalar_replacement_pass.cpp index 36c0c0d72..d71d605bf 100644 --- a/3rdparty/spirv-tools/source/opt/scalar_replacement_pass.cpp +++ b/3rdparty/spirv-tools/source/opt/scalar_replacement_pass.cpp @@ -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 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& 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& replacements) { + int32_t idx = 0; + BasicBlock* block = context()->get_instr_block(dbg_value); + for (auto var : replacements) { + // Clone the DebugValue. + std::unique_ptr 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& 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()) { diff --git a/3rdparty/spirv-tools/source/opt/scalar_replacement_pass.h b/3rdparty/spirv-tools/source/opt/scalar_replacement_pass.h index e20f1f1a5..1f6c92813 100644 --- a/3rdparty/spirv-tools/source/opt/scalar_replacement_pass.h +++ b/3rdparty/spirv-tools/source/opt/scalar_replacement_pass.h @@ -199,6 +199,21 @@ class ScalarReplacementPass : public Pass { bool ReplaceWholeStore(Instruction* store, const std::vector& 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& 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& 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 diff --git a/3rdparty/spirv-tools/source/opt/ssa_rewrite_pass.cpp b/3rdparty/spirv-tools/source/opt/ssa_rewrite_pass.cpp index 1477db44e..d5bba5e21 100644 --- a/3rdparty/spirv-tools/source/opt/ssa_rewrite_pass.cpp +++ b/3rdparty/spirv-tools/source/opt/ssa_rewrite_pass.cpp @@ -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; } diff --git a/3rdparty/spirv-tools/source/opt/wrap_opkill.cpp b/3rdparty/spirv-tools/source/opt/wrap_opkill.cpp index 4d708405c..ae1000c7f 100644 --- a/3rdparty/spirv-tools/source/opt/wrap_opkill.cpp +++ b/3rdparty/spirv-tools/source/opt/wrap_opkill.cpp @@ -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); diff --git a/3rdparty/spirv-tools/source/reduce/CMakeLists.txt b/3rdparty/spirv-tools/source/reduce/CMakeLists.txt index d945bd20a..865510bb7 100644 --- a/3rdparty/spirv-tools/source/reduce/CMakeLists.txt +++ b/3rdparty/spirv-tools/source/reduce/CMakeLists.txt @@ -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") diff --git a/3rdparty/spirv-tools/source/reduce/operand_to_dominating_id_reduction_opportunity_finder.cpp b/3rdparty/spirv-tools/source/reduce/operand_to_dominating_id_reduction_opportunity_finder.cpp index 13beb8902..1bd9d9d7d 100644 --- a/3rdparty/spirv-tools/source/reduce/operand_to_dominating_id_reduction_opportunity_finder.cpp +++ b/3rdparty/spirv-tools/source/reduce/operand_to_dominating_id_reduction_opportunity_finder.cpp @@ -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. diff --git a/3rdparty/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity.h b/3rdparty/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity.h index 564811f10..962cf77a3 100644 --- a/3rdparty/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity.h +++ b/3rdparty/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity.h @@ -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: diff --git a/3rdparty/spirv-tools/source/val/validate_capability.cpp b/3rdparty/spirv-tools/source/val/validate_capability.cpp index 8a356bf7f..4b98bc19c 100644 --- a/3rdparty/spirv-tools/source/val/validate_capability.cpp +++ b/3rdparty/spirv-tools/source/val/validate_capability.cpp @@ -14,8 +14,6 @@ // Validates OpCapability instruction. -#include "source/val/validate.h" - #include #include #include @@ -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; } diff --git a/3rdparty/spirv-tools/source/val/validate_extensions.cpp b/3rdparty/spirv-tools/source/val/validate_extensions.cpp index 7ce681c4f..e22e12142 100644 --- a/3rdparty/spirv-tools/source/val/validate_extensions.cpp +++ b/3rdparty/spirv-tools/source/val/validate_extensions.cpp @@ -13,11 +13,13 @@ // limitations under the License. // Validates correctness of extension SPIR-V instructions. - +#include #include #include #include +#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(1) != 32) { + return false; + } + + if (type->GetOperandAs(2) != 0) { + return false; + } + + return true; +} + +spv_result_t ValidateClspvReflectionKernel(ValidationState_t& _, + const Instruction* inst) { + const auto kernel_id = inst->GetOperandAs(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(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( + 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(4)) != SpvOpString) { + return _.diag(SPV_ERROR_INVALID_ID, inst) << "Name must be an OpString"; + } + if (num_operands > 5) { + if (_.GetIdOpcode(inst->GetOperandAs(5)) != SpvOpString) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "TypeName must be an OpString"; + } + } + if (num_operands > 6) { + if (!IsUint32Constant(_, inst->GetOperandAs(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(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(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(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(2) != inst->GetOperandAs(2)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Kernel must be from the same extended instruction import"; + } + + const auto ext_inst = + decl->GetOperandAs(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(info_index)); + if (!info || info->opcode() != SpvOpExtInst) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "ArgInfo must be an ArgumentInfo extended instruction"; + } + + if (info->GetOperandAs(2) != inst->GetOperandAs(2)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "ArgInfo must be from the same extended instruction import"; + } + + auto ext_inst = info->GetOperandAs(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(5))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Ordinal must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(6))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "DescriptorSet must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(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(5))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Ordinal must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(6))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "DescriptorSet must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(7))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Binding must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(8))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Offset must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(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(5))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Ordinal must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(6))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Offset must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(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(5))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Ordinal must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(6))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "SpecId must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(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(4))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "X must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(5))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Y must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(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(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(4))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Offset must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(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(4))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "DescriptorSet must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(5))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Binding must be a 32-bit unsigned integer OpConstant"; + } + + if (_.GetIdOpcode(inst->GetOperandAs(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(4))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "DescriptorSet must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(5))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Binding must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(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(5))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "X must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(6))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Y must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(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(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(2)); + const std::string name(reinterpret_cast( + 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( + 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; diff --git a/3rdparty/spirv-tools/source/val/validate_image.cpp b/3rdparty/spirv-tools/source/val/validate_image.cpp index 9ce74a3d0..8a2bdf137 100644 --- a/3rdparty/spirv-tools/source/val/validate_image.cpp +++ b/3rdparty/spirv-tools/source/val/validate_image.cpp @@ -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;