Updated spirv-tools.

This commit is contained in:
Бранимир Караџић 2020-08-02 19:33:12 -07:00
parent 7cf5eb0eba
commit ecd5959987
96 changed files with 3025 additions and 1875 deletions

View File

@ -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

View File

@ -1 +1 @@
"v2020.5", "SPIRV-Tools v2020.5 aebdae4b9c5b3384041b88803e83fb244f5c8067"
"v2020.5", "SPIRV-Tools v2020.5 276598ad50d33f1d1a56311520b17390a6bed635"

View File

@ -0,0 +1,28 @@
static const spv_ext_inst_desc_t nonsemantic_clspvreflection_entries[] = {
{"Kernel", 1, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
{"ArgumentInfo", 2, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}},
{"ArgumentStorageBuffer", 3, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}},
{"ArgumentUniform", 4, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}},
{"ArgumentPodStorageBuffer", 5, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}},
{"ArgumentPodUniform", 6, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}},
{"ArgumentPodPushConstant", 7, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}},
{"ArgumentSampledImage", 8, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}},
{"ArgumentStorageImage", 9, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}},
{"ArgumentSampler", 10, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}},
{"ArgumentWorkgroup", 11, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_OPTIONAL_ID, SPV_OPERAND_TYPE_NONE}},
{"SpecConstantWorkgroupSize", 12, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
{"SpecConstantGlobalOffset", 13, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
{"SpecConstantWorkDim", 14, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
{"PushConstantGlobalOffset", 15, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
{"PushConstantEnqueuedLocalSize", 16, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
{"PushConstantGlobalSize", 17, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
{"PushConstantRegionOffset", 18, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
{"PushConstantNumWorkgroups", 19, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
{"PushConstantRegionGroupOffset", 20, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
{"ConstantDataStorageBuffer", 21, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
{"ConstantDataUniform", 22, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
{"LiteralSampler", 23, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}},
{"PropertyRequiredWorkgroupSize", 24, 0, nullptr, {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE}}
};

View File

@ -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

View File

@ -19,8 +19,8 @@ set(LANG_HEADER_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_lang
# For now, assume the DebugInfo grammar file is in the current directory.
# It might migrate to SPIRV-Headers.
set(DEBUGINFO_GRAMMAR_JSON_FILE "${CMAKE_CURRENT_SOURCE_DIR}/extinst.debuginfo.grammar.json")
set(CLDEBUGINFO100_GRAMMAR_JSON_FILE "${CMAKE_CURRENT_SOURCE_DIR}/extinst.opencl.debuginfo.100.grammar.json")
set(DEBUGINFO_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1/extinst.debuginfo.grammar.json")
set(CLDEBUGINFO100_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json")
# macro() definitions are used in the following because we need to append .inc
# file paths into some global lists (*_CPP_DEPENDS). And those global lists are
@ -112,7 +112,7 @@ endmacro(spvtools_opencl_tables)
macro(spvtools_vendor_tables VENDOR_TABLE SHORT_NAME OPERAND_KIND_PREFIX)
set(INSTS_FILE "${spirv-tools_BINARY_DIR}/${VENDOR_TABLE}.insts.inc")
set(GRAMMAR_FILE "${spirv-tools_SOURCE_DIR}/source/extinst.${VENDOR_TABLE}.grammar.json")
set(GRAMMAR_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1/extinst.${VENDOR_TABLE}.grammar.json")
add_custom_command(OUTPUT ${INSTS_FILE}
COMMAND ${PYTHON_EXECUTABLE} ${GRAMMAR_PROCESSING_SCRIPT}
--extinst-vendor-grammar=${GRAMMAR_FILE}
@ -148,6 +148,7 @@ spvtools_vendor_tables("spv-amd-gcn-shader" "spv-amd-gs" "")
spvtools_vendor_tables("spv-amd-shader-ballot" "spv-amd-sb" "")
spvtools_vendor_tables("debuginfo" "debuginfo" "")
spvtools_vendor_tables("opencl.debuginfo.100" "cldi100" "CLDEBUG100_")
spvtools_vendor_tables("nonsemantic.clspvreflection" "clspvreflection" "")
spvtools_extinst_lang_headers("DebugInfo" ${DEBUGINFO_GRAMMAR_JSON_FILE})
spvtools_extinst_lang_headers("OpenCLDebugInfo100" ${CLDEBUGINFO100_GRAMMAR_JSON_FILE})
@ -345,18 +346,21 @@ set_source_files_properties(
spvtools_pch(SPIRV_SOURCES pch_source)
add_library(${SPIRV_TOOLS} ${SPIRV_SOURCES})
spvtools_default_compile_options(${SPIRV_TOOLS})
target_include_directories(${SPIRV_TOOLS}
add_library(${SPIRV_TOOLS}-static STATIC ${SPIRV_SOURCES})
spvtools_default_compile_options(${SPIRV_TOOLS}-static)
target_include_directories(${SPIRV_TOOLS}-static
PUBLIC
$<BUILD_INTERFACE:${spirv-tools_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
PRIVATE ${spirv-tools_BINARY_DIR}
PRIVATE ${SPIRV_HEADER_INCLUDE_DIR}
)
set_property(TARGET ${SPIRV_TOOLS} PROPERTY FOLDER "SPIRV-Tools libraries")
spvtools_check_symbol_exports(${SPIRV_TOOLS})
add_dependencies( ${SPIRV_TOOLS} core_tables enum_string_mapping extinst_tables )
set_property(TARGET ${SPIRV_TOOLS}-static PROPERTY FOLDER "SPIRV-Tools libraries")
spvtools_check_symbol_exports(${SPIRV_TOOLS}-static)
add_dependencies(${SPIRV_TOOLS}-static core_tables enum_string_mapping extinst_tables)
# The static target does not have the '-static' suffix.
set_target_properties(${SPIRV_TOOLS}-static PROPERTIES OUTPUT_NAME "${SPIRV_TOOLS}")
add_library(${SPIRV_TOOLS}-shared SHARED ${SPIRV_SOURCES})
spvtools_default_compile_options(${SPIRV_TOOLS}-shared)
@ -376,16 +380,24 @@ target_compile_definitions(${SPIRV_TOOLS}-shared
)
add_dependencies(${SPIRV_TOOLS}-shared core_tables enum_string_mapping extinst_tables)
# Create the "${SPIRV_TOOLS}" target as an alias to either "${SPIRV_TOOLS}-static"
# or "${SPIRV_TOOLS}-shared" depending on the value of BUILD_SHARED_LIBS.
if(BUILD_SHARED_LIBS)
add_library(${SPIRV_TOOLS} ALIAS ${SPIRV_TOOLS}-shared)
else()
add_library(${SPIRV_TOOLS} ALIAS ${SPIRV_TOOLS}-static)
endif()
if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
find_library(LIBRT rt)
if(LIBRT)
target_link_libraries(${SPIRV_TOOLS} ${LIBRT})
target_link_libraries(${SPIRV_TOOLS}-static ${LIBRT})
target_link_libraries(${SPIRV_TOOLS}-shared ${LIBRT})
endif()
endif()
if(ENABLE_SPIRV_TOOLS_INSTALL)
install(TARGETS ${SPIRV_TOOLS} ${SPIRV_TOOLS}-shared EXPORT ${SPIRV_TOOLS}Targets
install(TARGETS ${SPIRV_TOOLS}-static ${SPIRV_TOOLS}-shared EXPORT ${SPIRV_TOOLS}Targets
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})

View File

@ -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;

View File

@ -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" }
]
}
]
}
]
}

View File

@ -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"
}
]
}
]
}

View File

@ -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" ]
}
]
}

View File

@ -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" ]
}
]
}

View File

@ -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" ]
}
]
}

View File

@ -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" ]
}
]
}

View File

@ -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)

View File

@ -474,11 +474,19 @@ class FactManager::DataSynonymAndIdEquationFacts {
void MakeEquivalent(const protobufs::DataDescriptor& dd1,
const protobufs::DataDescriptor& dd2);
// Registers a data descriptor in the equivalence relation if it hasn't been
// registered yet, and returns its representative.
const protobufs::DataDescriptor* RegisterDataDescriptor(
const protobufs::DataDescriptor& dd);
// Returns true if and only if |dd1| and |dd2| are valid data descriptors
// whose associated data have the same type (modulo integer signedness).
bool DataDescriptorsAreWellFormedAndComparable(
// whose associated data have compatible types. Two types are compatible if:
// - they are the same
// - they both are numerical or vectors of numerical components with the same
// number of components and the same bit count per component
static bool DataDescriptorsAreWellFormedAndComparable(
opt::IRContext* context, const protobufs::DataDescriptor& dd1,
const protobufs::DataDescriptor& dd2) const;
const protobufs::DataDescriptor& dd2);
OperationSet GetEquations(const protobufs::DataDescriptor* lhs) const;
@ -530,9 +538,7 @@ void FactManager::DataSynonymAndIdEquationFacts::AddFact(
protobufs::DataDescriptor lhs_dd = MakeDataDescriptor(fact.lhs_id(), {});
// Register the LHS in the equivalence relation if needed.
if (!synonymous_.Exists(lhs_dd)) {
synonymous_.Register(lhs_dd);
}
RegisterDataDescriptor(lhs_dd);
// Get equivalence class representatives for all ids used on the RHS of the
// equation.
@ -540,11 +546,8 @@ void FactManager::DataSynonymAndIdEquationFacts::AddFact(
for (auto rhs_id : fact.rhs_id()) {
// Register a data descriptor based on this id in the equivalence relation
// if needed, and then record the equivalence class representative.
protobufs::DataDescriptor rhs_dd = MakeDataDescriptor(rhs_id, {});
if (!synonymous_.Exists(rhs_dd)) {
synonymous_.Register(rhs_dd);
}
rhs_dd_ptrs.push_back(synonymous_.Find(&rhs_dd));
rhs_dd_ptrs.push_back(
RegisterDataDescriptor(MakeDataDescriptor(rhs_id, {})));
}
// Now add the fact.
@ -604,6 +607,14 @@ void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive(
case SpvOpConvertUToF:
ComputeConversionDataSynonymFacts(*rhs_dds[0], context);
break;
case SpvOpBitcast: {
assert(DataDescriptorsAreWellFormedAndComparable(context, lhs_dd,
*rhs_dds[0]) &&
"Operands of OpBitcast equation fact must have compatible types");
if (!synonymous_.IsEquivalent(lhs_dd, *rhs_dds[0])) {
AddDataSynonymFactRecursive(lhs_dd, *rhs_dds[0], context);
}
} break;
case SpvOpIAdd: {
// Equation form: "a = b + c"
for (const auto& equation : GetEquations(rhs_dds[0])) {
@ -713,9 +724,15 @@ void FactManager::DataSynonymAndIdEquationFacts::AddDataSynonymFactRecursive(
// Record that the data descriptors provided in the fact are equivalent.
MakeEquivalent(dd1, dd2);
assert(synonymous_.Find(&dd1) == synonymous_.Find(&dd2) &&
"|dd1| and |dd2| must have a single representative");
// Compute various corollary facts.
// |dd1| and |dd2| belong to the same equivalence class so it doesn't matter
// which one we use here.
ComputeConversionDataSynonymFacts(dd1, context);
ComputeCompositeDataSynonymFacts(dd1, dd2, context);
}
@ -1132,11 +1149,8 @@ void FactManager::DataSynonymAndIdEquationFacts::MakeEquivalent(
const protobufs::DataDescriptor& dd2) {
// Register the data descriptors if they are not already known to the
// equivalence relation.
for (const auto& dd : {dd1, dd2}) {
if (!synonymous_.Exists(dd)) {
synonymous_.Register(dd);
}
}
RegisterDataDescriptor(dd1);
RegisterDataDescriptor(dd2);
if (synonymous_.IsEquivalent(dd1, dd2)) {
// The data descriptors are already known to be equivalent, so there is
@ -1203,10 +1217,17 @@ void FactManager::DataSynonymAndIdEquationFacts::MakeEquivalent(
id_equations_.erase(no_longer_representative);
}
const protobufs::DataDescriptor*
FactManager::DataSynonymAndIdEquationFacts::RegisterDataDescriptor(
const protobufs::DataDescriptor& dd) {
return synonymous_.Exists(dd) ? synonymous_.Find(&dd)
: synonymous_.Register(dd);
}
bool FactManager::DataSynonymAndIdEquationFacts::
DataDescriptorsAreWellFormedAndComparable(
opt::IRContext* context, const protobufs::DataDescriptor& dd1,
const protobufs::DataDescriptor& dd2) const {
const protobufs::DataDescriptor& dd2) {
auto end_type_id_1 = fuzzerutil::WalkCompositeTypeIndices(
context, context->get_def_use_mgr()->GetDef(dd1.object())->type_id(),
dd1.index());
@ -1225,30 +1246,49 @@ bool FactManager::DataSynonymAndIdEquationFacts::
// vectors that differ only in signedness.
// Get both types.
const opt::analysis::Type* type_1 =
context->get_type_mgr()->GetType(end_type_id_1);
const opt::analysis::Type* type_2 =
context->get_type_mgr()->GetType(end_type_id_2);
const auto* type_a = context->get_type_mgr()->GetType(end_type_id_1);
const auto* type_b = context->get_type_mgr()->GetType(end_type_id_2);
assert(type_a && type_b && "Data descriptors have invalid type(s)");
// If the first type is a vector, check that the second type is a vector of
// the same width, and drill down to the vector element types.
if (type_1->AsVector()) {
if (!type_2->AsVector()) {
// If both types are numerical or vectors of numerical components, then they
// are compatible if they have the same number of components and the same bit
// count per component.
if (type_a->AsVector() && type_b->AsVector()) {
const auto* vector_a = type_a->AsVector();
const auto* vector_b = type_b->AsVector();
if (vector_a->element_count() != vector_b->element_count() ||
vector_a->element_type()->AsBool() ||
vector_b->element_type()->AsBool()) {
// The case where both vectors have boolean elements and the same number
// of components is handled by the direct equality check earlier.
// You can't have multiple identical boolean vector types.
return false;
}
if (type_1->AsVector()->element_count() !=
type_2->AsVector()->element_count()) {
return false;
type_a = vector_a->element_type();
type_b = vector_b->element_type();
}
type_1 = type_1->AsVector()->element_type();
type_2 = type_2->AsVector()->element_type();
auto get_bit_count_for_numeric_type =
[](const opt::analysis::Type& type) -> uint32_t {
if (const auto* integer = type.AsInteger()) {
return integer->width();
} else if (const auto* floating = type.AsFloat()) {
return floating->width();
} else {
assert(false && "|type| must be a numerical type");
return 0;
}
// Check that type_1 and type_2 are both integer types of the same bit-width
// (but with potentially different signedness).
auto integer_type_1 = type_1->AsInteger();
auto integer_type_2 = type_2->AsInteger();
return integer_type_1 && integer_type_2 &&
integer_type_1->width() == integer_type_2->width();
};
// Checks that both |type_a| and |type_b| are either numerical or vectors of
// numerical components and have the same number of bits.
return (type_a->AsInteger() || type_a->AsFloat()) &&
(type_b->AsInteger() || type_b->AsFloat()) &&
(get_bit_count_for_numeric_type(*type_a) ==
get_bit_count_for_numeric_type(*type_b));
}
std::vector<const protobufs::DataDescriptor*>

View File

@ -46,6 +46,7 @@
#include "source/fuzz/fuzzer_pass_construct_composites.h"
#include "source/fuzz/fuzzer_pass_copy_objects.h"
#include "source/fuzz/fuzzer_pass_donate_modules.h"
#include "source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h"
#include "source/fuzz/fuzzer_pass_interchange_zero_like_constants.h"
#include "source/fuzz/fuzzer_pass_invert_comparison_operators.h"
#include "source/fuzz/fuzzer_pass_merge_blocks.h"
@ -335,6 +336,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
MaybeAddPass<FuzzerPassAddNoContractionDecorations>(
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassInterchangeSignednessOfIntegerOperands>(
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassInterchangeZeroLikeConstants>(
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);

View File

@ -67,6 +67,8 @@ const std::pair<uint32_t, uint32_t> kChanceOfGoingDeeperWhenMakingAccessChain =
{50, 95};
const std::pair<uint32_t, uint32_t> kChanceOfInterchangingZeroLikeConstants = {
10, 90};
const std::pair<uint32_t, uint32_t>
kChanceOfInterchangingSignednessOfIntegerOperands = {10, 90};
const std::pair<uint32_t, uint32_t> kChanceOfInvertingComparisonOperators = {
20, 50};
const std::pair<uint32_t, uint32_t> kChanceOfMakingDonorLivesafe = {40, 60};
@ -84,6 +86,8 @@ const std::pair<uint32_t, uint32_t> kChanceOfReplacingCopyObjectWithStoreLoad =
const std::pair<uint32_t, uint32_t> kChanceOfReplacingIdWithSynonym = {10, 90};
const std::pair<uint32_t, uint32_t>
kChanceOfReplacingLinearAlgebraInstructions = {10, 90};
const std::pair<uint32_t, uint32_t> kChanceOfReplacingLoadStoreWithCopyMemory =
{20, 90};
const std::pair<uint32_t, uint32_t> kChanceOfReplacingParametersWithGlobals = {
30, 70};
const std::pair<uint32_t, uint32_t> kChanceOfReplacingParametersWithStruct = {
@ -195,6 +199,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
ChooseBetweenMinAndMax(kChanceOfDonatingAdditionalModule);
chance_of_going_deeper_when_making_access_chain_ =
ChooseBetweenMinAndMax(kChanceOfGoingDeeperWhenMakingAccessChain);
chance_of_interchanging_signedness_of_integer_operands_ =
ChooseBetweenMinAndMax(kChanceOfInterchangingSignednessOfIntegerOperands);
chance_of_interchanging_zero_like_constants_ =
ChooseBetweenMinAndMax(kChanceOfInterchangingZeroLikeConstants);
chance_of_inverting_comparison_operators_ =
@ -222,6 +228,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
ChooseBetweenMinAndMax(kChanceOfReplacingIdWithSynonym);
chance_of_replacing_linear_algebra_instructions_ =
ChooseBetweenMinAndMax(kChanceOfReplacingLinearAlgebraInstructions);
chance_of_replacing_load_store_with_copy_memory_ =
ChooseBetweenMinAndMax(kChanceOfReplacingLoadStoreWithCopyMemory);
chance_of_replacing_parameters_with_globals_ =
ChooseBetweenMinAndMax(kChanceOfReplacingParametersWithGlobals);
chance_of_replacing_parameters_with_struct_ =

View File

@ -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_;

View File

@ -17,6 +17,7 @@
#include <set>
#include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/id_use_descriptor.h"
#include "source/fuzz/instruction_descriptor.h"
#include "source/fuzz/transformation_add_constant_boolean.h"
#include "source/fuzz/transformation_add_constant_composite.h"
@ -514,5 +515,23 @@ uint32_t FuzzerPass::FindOrCreateZeroConstant(
}
}
void FuzzerPass::MaybeAddUseToReplace(
opt::Instruction* use_inst, uint32_t use_index, uint32_t replacement_id,
std::vector<std::pair<protobufs::IdUseDescriptor, uint32_t>>*
uses_to_replace) {
// Only consider this use if it is in a block
if (!GetIRContext()->get_instr_block(use_inst)) {
return;
}
// Get the index of the operand restricted to input operands.
uint32_t in_operand_index =
fuzzerutil::InOperandIndexFromOperandIndex(*use_inst, use_index);
auto id_use_descriptor =
MakeIdUseDescriptorFromUse(GetIRContext(), use_inst, in_operand_index);
uses_to_replace->emplace_back(
std::make_pair(id_use_descriptor, replacement_id));
}
} // namespace fuzz
} // namespace spvtools

View File

@ -175,8 +175,7 @@ class FuzzerPass {
// with |words| as its value. If either the required integer type or the
// constant do not exist, transformations are applied to add them.
// The returned id either participates in IdIsIrrelevant fact or not,
// depending
// on the |is_irrelevant| parameter.
// depending on the |is_irrelevant| parameter.
uint32_t FindOrCreateIntegerConstant(const std::vector<uint32_t>& words,
uint32_t width, bool is_signed,
bool is_irrelevant);
@ -274,6 +273,16 @@ class FuzzerPass {
uint32_t FindOrCreateZeroConstant(uint32_t scalar_or_composite_type_id,
bool is_irrelevant);
// Adds a pair (id_use_descriptor, |replacement_id|) to the vector
// |uses_to_replace|, where id_use_descriptor is the id use descriptor
// representing the usage of an id in the |use_inst| instruction, at operand
// index |use_index|, only if the instruction is in a basic block.
// If the instruction is not in a basic block, it does nothing.
void MaybeAddUseToReplace(
opt::Instruction* use_inst, uint32_t use_index, uint32_t replacement_id,
std::vector<std::pair<protobufs::IdUseDescriptor, uint32_t>>*
uses_to_replace);
private:
opt::IRContext* ir_context_;
TransformationContext* transformation_context_;

View File

@ -92,6 +92,11 @@ void FuzzerPassAddAccessChains::Apply() {
relevant_pointer_instructions[GetFuzzerContext()->RandomIndex(
relevant_pointer_instructions)];
std::vector<uint32_t> index_ids;
// Each index accessing a non-struct composite will be clamped, thus
// needing a pair of fresh ids
std::vector<std::pair<uint32_t, uint32_t>> fresh_ids_for_clamping;
auto pointer_type = GetIRContext()->get_def_use_mgr()->GetDef(
chosen_pointer->type_id());
uint32_t subobject_type_id = pointer_type->GetSingleWordInOperand(1);
@ -132,20 +137,37 @@ void FuzzerPassAddAccessChains::Apply() {
break;
}
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3179) We
// could allow non-constant indices when looking up non-structs,
// using clamping to ensure they are in-bounds.
uint32_t index_value =
GetFuzzerContext()->GetRandomIndexForAccessChain(bound);
index_ids.push_back(FindOrCreateIntegerConstant(
{index_value}, 32, GetFuzzerContext()->ChooseEven(), false));
switch (subobject_type->opcode()) {
case SpvOpTypeArray:
case SpvOpTypeMatrix:
case SpvOpTypeVector:
case SpvOpTypeVector: {
// The index will be clamped
bool is_signed = GetFuzzerContext()->ChooseEven();
// Make the constant ready for clamping. We need:
// - an OpTypeBool to be present in the module
// - an OpConstant with the same type as the index and value
// the maximum value for an index
// - a new pair of fresh ids for the clamping instructions
FindOrCreateBoolType();
FindOrCreateIntegerConstant({bound - 1}, 32, is_signed, false);
std::pair<uint32_t, uint32_t> fresh_pair_of_ids = {
GetFuzzerContext()->GetFreshId(),
GetFuzzerContext()->GetFreshId()};
fresh_ids_for_clamping.emplace_back(fresh_pair_of_ids);
index_ids.push_back(FindOrCreateIntegerConstant(
{index_value}, 32, is_signed, false));
subobject_type_id = subobject_type->GetSingleWordInOperand(0);
break;
} break;
case SpvOpTypeStruct:
index_ids.push_back(FindOrCreateIntegerConstant(
{index_value}, 32, GetFuzzerContext()->ChooseEven(), false));
subobject_type_id =
subobject_type->GetSingleWordInOperand(index_value);
break;
@ -162,7 +184,7 @@ void FuzzerPassAddAccessChains::Apply() {
// Apply the transformation to add an access chain.
ApplyTransformation(TransformationAccessChain(
GetFuzzerContext()->GetFreshId(), chosen_pointer->result_id(),
index_ids, instruction_descriptor));
index_ids, instruction_descriptor, fresh_ids_for_clamping));
});
}

View File

@ -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 {

View File

@ -60,7 +60,7 @@ void FuzzerPassApplyIdSynonyms::Apply() {
}
});
for (auto& use : uses) {
for (const auto& use : uses) {
auto use_inst = use.first;
auto use_index = use.second;
auto block_containing_use = GetIRContext()->get_instr_block(use_inst);
@ -82,7 +82,7 @@ void FuzzerPassApplyIdSynonyms::Apply() {
}
std::vector<const protobufs::DataDescriptor*> synonyms_to_try;
for (auto& data_descriptor :
for (const auto* data_descriptor :
GetTransformationContext()->GetFactManager()->GetSynonymsForId(
id_with_known_synonyms)) {
protobufs::DataDescriptor descriptor_for_this_id =
@ -91,8 +91,13 @@ void FuzzerPassApplyIdSynonyms::Apply() {
// Exclude the fact that the id is synonymous with itself.
continue;
}
if (DataDescriptorsHaveCompatibleTypes(
use_inst->opcode(), use_in_operand_index,
descriptor_for_this_id, *data_descriptor)) {
synonyms_to_try.push_back(data_descriptor);
}
}
while (!synonyms_to_try.empty()) {
auto synonym_to_try =
GetFuzzerContext()->RemoveAtRandomIndex(&synonyms_to_try);
@ -162,5 +167,26 @@ void FuzzerPassApplyIdSynonyms::Apply() {
}
}
bool FuzzerPassApplyIdSynonyms::DataDescriptorsHaveCompatibleTypes(
SpvOp opcode, uint32_t use_in_operand_index,
const protobufs::DataDescriptor& dd1,
const protobufs::DataDescriptor& dd2) {
auto base_object_type_id_1 =
fuzzerutil::GetTypeId(GetIRContext(), dd1.object());
auto base_object_type_id_2 =
fuzzerutil::GetTypeId(GetIRContext(), dd2.object());
assert(base_object_type_id_1 && base_object_type_id_2 &&
"Data descriptors are invalid");
auto type_id_1 = fuzzerutil::WalkCompositeTypeIndices(
GetIRContext(), base_object_type_id_1, dd1.index());
auto type_id_2 = fuzzerutil::WalkCompositeTypeIndices(
GetIRContext(), base_object_type_id_2, dd2.index());
assert(type_id_1 && type_id_2 && "Data descriptors have invalid types");
return TransformationReplaceIdWithSynonym::TypesAreCompatible(
GetIRContext(), opcode, use_in_operand_index, type_id_1, type_id_2);
}
} // namespace fuzz
} // namespace spvtools

View File

@ -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

View File

@ -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 =

View File

@ -0,0 +1,149 @@
// Copyright (c) 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "fuzzer_pass_interchange_signedness_of_integer_operands.h"
#include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/id_use_descriptor.h"
#include "source/fuzz/transformation_record_synonymous_constants.h"
#include "source/fuzz/transformation_replace_id_with_synonym.h"
namespace spvtools {
namespace fuzz {
FuzzerPassInterchangeSignednessOfIntegerOperands::
FuzzerPassInterchangeSignednessOfIntegerOperands(
opt::IRContext* ir_context,
TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
transformations) {}
FuzzerPassInterchangeSignednessOfIntegerOperands::
~FuzzerPassInterchangeSignednessOfIntegerOperands() = default;
void FuzzerPassInterchangeSignednessOfIntegerOperands::Apply() {
// Make vector keeping track of all the uses we want to replace.
// This is a vector of pairs, where the first element is an id use descriptor
// identifying the use of a constant id and the second is the id that should
// be used to replace it.
std::vector<std::pair<protobufs::IdUseDescriptor, uint32_t>> uses_to_replace;
for (auto constant : GetIRContext()->GetConstants()) {
uint32_t constant_id = constant->result_id();
// We want to record the synonymity of an integer constant with another
// constant with opposite signedness, and this can only be done if they are
// not irrelevant.
if (GetTransformationContext()->GetFactManager()->IdIsIrrelevant(
constant_id)) {
continue;
}
uint32_t toggled_id =
FindOrCreateToggledIntegerConstant(constant->result_id());
if (!toggled_id) {
// Not an integer constant
continue;
}
assert(!GetTransformationContext()->GetFactManager()->IdIsIrrelevant(
toggled_id) &&
"FindOrCreateToggledConstant can't produce an irrelevant id");
// Record synonymous constants
ApplyTransformation(
TransformationRecordSynonymousConstants(constant_id, toggled_id));
// Find all the uses of the constant and, for each, probabilistically
// decide whether to replace it.
GetIRContext()->get_def_use_mgr()->ForEachUse(
constant_id,
[this, toggled_id, &uses_to_replace](opt::Instruction* use_inst,
uint32_t use_index) -> void {
if (GetFuzzerContext()->ChoosePercentage(
GetFuzzerContext()
->GetChanceOfInterchangingSignednessOfIntegerOperands())) {
MaybeAddUseToReplace(use_inst, use_index, toggled_id,
&uses_to_replace);
}
});
}
// Replace the ids if it is allowed.
for (auto use_to_replace : uses_to_replace) {
MaybeApplyTransformation(TransformationReplaceIdWithSynonym(
use_to_replace.first, use_to_replace.second));
}
}
uint32_t FuzzerPassInterchangeSignednessOfIntegerOperands::
FindOrCreateToggledIntegerConstant(uint32_t id) {
auto constant = GetIRContext()->get_constant_mgr()->FindDeclaredConstant(id);
// This pass only toggles integer constants.
if (!constant->AsIntConstant() &&
(!constant->AsVectorConstant() ||
!constant->AsVectorConstant()->component_type()->AsInteger())) {
return 0;
}
if (auto integer = constant->AsIntConstant()) {
auto type = integer->type()->AsInteger();
// Find or create and return the toggled constant.
return FindOrCreateIntegerConstant(std::vector<uint32_t>(integer->words()),
type->width(), !type->IsSigned(), false);
}
// The constant is an integer vector.
// Find the component type.
auto component_type =
constant->AsVectorConstant()->component_type()->AsInteger();
// Find or create the toggled component type.
uint32_t toggled_component_type = FindOrCreateIntegerType(
component_type->width(), !component_type->IsSigned());
// Get the information about the toggled components. We need to extract this
// information now because the analyses might be invalidated, which would make
// the constant and component_type variables invalid.
std::vector<std::vector<uint32_t>> component_words;
for (auto component : constant->AsVectorConstant()->GetComponents()) {
component_words.push_back(component->AsIntConstant()->words());
}
uint32_t width = component_type->width();
bool is_signed = !component_type->IsSigned();
std::vector<uint32_t> toggled_components;
// Find or create the toggled components.
for (auto words : component_words) {
toggled_components.push_back(
FindOrCreateIntegerConstant(words, width, is_signed, false));
}
// Find or create the required toggled vector type.
uint32_t toggled_type = FindOrCreateVectorType(
toggled_component_type, (uint32_t)toggled_components.size());
// Find or create and return the toggled vector constant.
return FindOrCreateCompositeConstant(toggled_components, toggled_type, false);
}
} // namespace fuzz
} // namespace spvtools

View File

@ -0,0 +1,51 @@
// Copyright (c) 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SOURCE_FUZZ_FUZZER_PASS_INTERCHANGE_SIGNEDNESS_OF_INTEGER_OPERANDS_
#define SOURCE_FUZZ_FUZZER_PASS_INTERCHANGE_SIGNEDNESS_OF_INTEGER_OPERANDS_
#include "source/fuzz/fuzzer_pass.h"
namespace spvtools {
namespace fuzz {
// A pass that:
// - Finds all the integer constant (scalar and vector) definitions in the
// module and adds the definitions of the integer with the same data words but
// opposite signedness. If the synonym is already in the module, it does not
// add a new one.
// - For each use of an integer constant where its signedness does not matter,
// decides whether to change it to the id of the toggled constant.
class FuzzerPassInterchangeSignednessOfIntegerOperands : public FuzzerPass {
public:
FuzzerPassInterchangeSignednessOfIntegerOperands(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations);
~FuzzerPassInterchangeSignednessOfIntegerOperands() override;
void Apply() override;
private:
// Given the id of an integer constant (scalar or vector), it finds or creates
// the corresponding toggled constant (the integer with the same data words
// but opposite signedness). Returns the id of the toggled instruction if the
// constant is an integer scalar or vector, 0 otherwise.
uint32_t FindOrCreateToggledIntegerConstant(uint32_t id);
};
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_FUZZER_PASS_INTERCHANGE_SIGNEDNESS_OF_INTEGER_OPERANDS_

View File

@ -57,24 +57,6 @@ uint32_t FuzzerPassInterchangeZeroLikeConstants::FindOrCreateToggledConstant(
return 0;
}
void FuzzerPassInterchangeZeroLikeConstants::MaybeAddUseToReplace(
opt::Instruction* use_inst, uint32_t use_index, uint32_t replacement_id,
std::vector<std::pair<protobufs::IdUseDescriptor, uint32_t>>*
uses_to_replace) {
// Only consider this use if it is in a block
if (!GetIRContext()->get_instr_block(use_inst)) {
return;
}
// Get the index of the operand restricted to input operands.
uint32_t in_operand_index =
fuzzerutil::InOperandIndexFromOperandIndex(*use_inst, use_index);
auto id_use_descriptor =
MakeIdUseDescriptorFromUse(GetIRContext(), use_inst, in_operand_index);
uses_to_replace->emplace_back(
std::make_pair(id_use_descriptor, replacement_id));
}
void FuzzerPassInterchangeZeroLikeConstants::Apply() {
// Make vector keeping track of all the uses we want to replace.
// This is a vector of pairs, where the first element is an id use descriptor
@ -118,7 +100,7 @@ void FuzzerPassInterchangeZeroLikeConstants::Apply() {
});
}
// Replace the ids
// Replace the ids if it is allowed.
for (auto use_to_replace : uses_to_replace) {
MaybeApplyTransformation(TransformationReplaceIdWithSynonym(
use_to_replace.first, use_to_replace.second));

View File

@ -46,16 +46,6 @@ class FuzzerPassInterchangeZeroLikeConstants : public FuzzerPass {
// Returns the id of the toggled instruction if the constant is zero-like,
// 0 otherwise.
uint32_t FindOrCreateToggledConstant(opt::Instruction* declaration);
// Given an id use (described by an instruction and an index) and an id with
// which the original one should be replaced, adds a pair (with the elements
// being the corresponding id use descriptor and the replacement id) to
// |uses_to_replace| if the use is in an instruction block, otherwise does
// nothing.
void MaybeAddUseToReplace(
opt::Instruction* use_inst, uint32_t use_index, uint32_t replacement_id,
std::vector<std::pair<protobufs::IdUseDescriptor, uint32_t>>*
uses_to_replace);
};
} // namespace fuzz

View File

@ -17,7 +17,9 @@
#include <vector>
#include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/instruction_descriptor.h"
#include "source/fuzz/transformation_outline_function.h"
#include "source/fuzz/transformation_split_block.h"
namespace spvtools {
namespace fuzz {
@ -46,6 +48,33 @@ void FuzzerPassOutlineFunctions::Apply() {
blocks.push_back(&block);
}
auto entry_block = blocks[GetFuzzerContext()->RandomIndex(blocks)];
// If the entry block starts with OpPhi, try to split it.
if (entry_block->begin()->opcode() == SpvOpPhi) {
// Find the first non-OpPhi instruction.
opt::Instruction* non_phi_inst = nullptr;
for (auto& instruction : *entry_block) {
if (instruction.opcode() != SpvOpPhi) {
non_phi_inst = &instruction;
break;
}
}
assert(non_phi_inst && "|non_phi_inst| must've been initialized");
// If the split was not applicable, the transformation will not work.
uint32_t new_block_id = GetFuzzerContext()->GetFreshId();
if (!MaybeApplyTransformation(TransformationSplitBlock(
MakeInstructionDescriptor(non_phi_inst->result_id(),
non_phi_inst->opcode(), 0),
new_block_id))) {
return;
}
// The new entry block is the newly-created block.
entry_block = &*function->FindBlock(new_block_id);
}
auto dominator_analysis = GetIRContext()->GetDominatorAnalysis(function);
auto postdominator_analysis =
GetIRContext()->GetPostDominatorAnalysis(function);

View File

@ -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;
}

View File

@ -0,0 +1,105 @@
// Copyright (c) 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h"
#include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/instruction_descriptor.h"
#include "source/fuzz/transformation_replace_load_store_with_copy_memory.h"
#include "source/opt/instruction.h"
namespace spvtools {
namespace fuzz {
FuzzerPassReplaceLoadsStoresWithCopyMemories::
FuzzerPassReplaceLoadsStoresWithCopyMemories(
opt::IRContext* ir_context,
TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
transformations) {}
FuzzerPassReplaceLoadsStoresWithCopyMemories::
~FuzzerPassReplaceLoadsStoresWithCopyMemories() = default;
void FuzzerPassReplaceLoadsStoresWithCopyMemories::Apply() {
// We look for matching pairs of instructions OpLoad and
// OpStore within the same block. Potential instructions OpLoad to be matched
// are stored in a hash map. If we encounter instructions that write to memory
// or instructions of memory barriers that could operate on variables within
// unsafe storage classes we need to erase the hash map to avoid unsafe
// operations.
// A vector of matching OpLoad and OpStore instructions.
std::vector<std::pair<opt::Instruction*, opt::Instruction*>>
op_load_store_pairs;
for (auto& function : *GetIRContext()->module()) {
for (auto& block : function) {
// A hash map storing potential OpLoad instructions.
std::unordered_map<uint32_t, opt::Instruction*> current_op_loads;
for (auto& instruction : block) {
// Add a potential OpLoad instruction.
if (instruction.opcode() == SpvOpLoad) {
current_op_loads[instruction.result_id()] = &instruction;
} else if (instruction.opcode() == SpvOpStore) {
if (current_op_loads.find(instruction.GetSingleWordOperand(1)) !=
current_op_loads.end()) {
// We have found the matching OpLoad instruction to the current
// OpStore instruction.
op_load_store_pairs.push_back(std::make_pair(
current_op_loads[instruction.GetSingleWordOperand(1)],
&instruction));
}
}
if (TransformationReplaceLoadStoreWithCopyMemory::IsMemoryWritingOpCode(
instruction.opcode())) {
current_op_loads.clear();
} else if (TransformationReplaceLoadStoreWithCopyMemory::
IsMemoryBarrierOpCode(instruction.opcode())) {
for (auto it = current_op_loads.begin();
it != current_op_loads.end();) {
// Get the storage class.
opt::Instruction* source_id =
GetIRContext()->get_def_use_mgr()->GetDef(
it->second->GetSingleWordOperand(2));
SpvStorageClass storage_class =
fuzzerutil::GetStorageClassFromPointerType(
GetIRContext(), source_id->type_id());
if (!TransformationReplaceLoadStoreWithCopyMemory::
IsStorageClassSafeAcrossMemoryBarriers(storage_class)) {
it = current_op_loads.erase(it);
} else {
it++;
}
}
}
}
}
}
for (auto instr_pair : op_load_store_pairs) {
// Randomly decide to apply the transformation for the
// potential pairs.
if (!GetFuzzerContext()->ChoosePercentage(
GetFuzzerContext()
->GetChanceOfReplacingLoadStoreWithCopyMemory())) {
ApplyTransformation(TransformationReplaceLoadStoreWithCopyMemory(
MakeInstructionDescriptor(GetIRContext(), instr_pair.first),
MakeInstructionDescriptor(GetIRContext(), instr_pair.second)));
}
}
} // namespace fuzz
} // namespace fuzz
} // namespace spvtools

View File

@ -0,0 +1,41 @@
// Copyright (c) 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SPIRV_TOOLS_FUZZER_PASS_REPLACE_LOADS_STORES_WITH_COPY_MEMORIES_H
#define SPIRV_TOOLS_FUZZER_PASS_REPLACE_LOADS_STORES_WITH_COPY_MEMORIES_H
#include "source/fuzz/fuzzer_pass.h"
namespace spvtools {
namespace fuzz {
// A fuzzer pass that takes pairs of instruction descriptors to OpLoad and
// OpStore that have the same intermediate value and in each pair replaces the
// OpStore with an equivalent OpCopyMemory.
class FuzzerPassReplaceLoadsStoresWithCopyMemories : public FuzzerPass {
public:
FuzzerPassReplaceLoadsStoresWithCopyMemories(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations);
~FuzzerPassReplaceLoadsStoresWithCopyMemories() override;
void Apply() override;
};
} // namespace fuzz
} // namespace spvtools
#endif // SPIRV_TOOLS_FUZZER_PASS_REPLACE_LOADS_STORES_WITH_COPY_MEMORIES_H

View File

@ -98,7 +98,7 @@ void FuzzerPassReplaceParamsWithStruct::Apply() {
parameter_id.push_back(params[index]->result_id());
}
std::unordered_map<uint32_t, uint32_t> caller_id_to_fresh_id;
std::map<uint32_t, uint32_t> caller_id_to_fresh_id;
for (const auto* inst :
fuzzerutil::GetCallers(GetIRContext(), function.result_id())) {
caller_id_to_fresh_id[inst->result_id()] =

View File

@ -373,6 +373,28 @@ uint32_t GetArraySize(const opt::Instruction& array_type_instruction,
return array_length_constant->GetU32();
}
uint32_t GetBoundForCompositeIndex(const opt::Instruction& composite_type_inst,
opt::IRContext* ir_context) {
switch (composite_type_inst.opcode()) {
case SpvOpTypeArray:
return fuzzerutil::GetArraySize(composite_type_inst, ir_context);
case SpvOpTypeMatrix:
case SpvOpTypeVector:
return composite_type_inst.GetSingleWordInOperand(1);
case SpvOpTypeStruct: {
return fuzzerutil::GetNumberOfStructMembers(composite_type_inst);
}
case SpvOpTypeRuntimeArray:
assert(false &&
"GetBoundForCompositeIndex should not be invoked with an "
"OpTypeRuntimeArray, which does not have a static bound.");
return 0;
default:
assert(false && "Unknown composite type.");
return 0;
}
}
bool IsValid(opt::IRContext* context, spv_validator_options validator_options) {
std::vector<uint32_t> binary;
context->module()->ToBinary(&binary, false);
@ -735,6 +757,20 @@ std::vector<opt::Instruction*> GetParameters(opt::IRContext* ir_context,
return result;
}
void RemoveParameter(opt::IRContext* ir_context, uint32_t parameter_id) {
auto* function = GetFunctionFromParameterId(ir_context, parameter_id);
assert(function && "|parameter_id| is invalid");
assert(!FunctionIsEntryPoint(ir_context, function->result_id()) &&
"Can't remove parameter from an entry point function");
function->RemoveParameter(parameter_id);
// We've just removed parameters from the function and cleared their memory.
// Make sure analyses have no dangling pointers.
ir_context->InvalidateAnalysesExceptFor(
opt::IRContext::Analysis::kAnalysisNone);
}
std::vector<opt::Instruction*> GetCallers(opt::IRContext* ir_context,
uint32_t function_id) {
assert(FindFunction(ir_context, function_id) &&
@ -1104,6 +1140,32 @@ uint32_t MaybeGetIntegerConstant(
return 0;
}
uint32_t MaybeGetIntegerConstantFromValueAndType(opt::IRContext* ir_context,
uint32_t value,
uint32_t int_type_id) {
auto int_type_inst = ir_context->get_def_use_mgr()->GetDef(int_type_id);
assert(int_type_inst && "The given type id must exist.");
auto int_type = ir_context->get_type_mgr()
->GetType(int_type_inst->result_id())
->AsInteger();
assert(int_type && int_type->width() == 32 &&
"The given type id must correspond to an 32-bit integer type.");
opt::analysis::IntConstant constant(int_type, {value});
// Check that the constant exists in the module.
if (!ir_context->get_constant_mgr()->FindConstant(&constant)) {
return 0;
}
return ir_context->get_constant_mgr()
->GetDefiningInstruction(&constant)
->result_id();
}
uint32_t MaybeGetFloatConstant(
opt::IRContext* ir_context,
const TransformationContext& transformation_context,
@ -1193,6 +1255,63 @@ void AddStructType(opt::IRContext* ir_context, uint32_t result_id,
UpdateModuleIdBound(ir_context, result_id);
}
bool TypesAreEqualUpToSign(opt::IRContext* ir_context, uint32_t type1_id,
uint32_t type2_id) {
if (type1_id == type2_id) {
return true;
}
auto type1 = ir_context->get_type_mgr()->GetType(type1_id);
auto type2 = ir_context->get_type_mgr()->GetType(type2_id);
// Integer scalar types must have the same width
if (type1->AsInteger() && type2->AsInteger()) {
return type1->AsInteger()->width() == type2->AsInteger()->width();
}
// Integer vector types must have the same number of components and their
// component types must be integers with the same width.
if (type1->AsVector() && type2->AsVector()) {
auto component_type1 = type1->AsVector()->element_type()->AsInteger();
auto component_type2 = type2->AsVector()->element_type()->AsInteger();
// Only check the component count and width if they are integer.
if (component_type1 && component_type2) {
return type1->AsVector()->element_count() ==
type2->AsVector()->element_count() &&
component_type1->width() == component_type2->width();
}
}
// In all other cases, the types cannot be considered equal.
return false;
}
std::map<uint32_t, uint32_t> RepeatedUInt32PairToMap(
const google::protobuf::RepeatedPtrField<protobufs::UInt32Pair>& data) {
std::map<uint32_t, uint32_t> result;
for (const auto& entry : data) {
result[entry.first()] = entry.second();
}
return result;
}
google::protobuf::RepeatedPtrField<protobufs::UInt32Pair>
MapToRepeatedUInt32Pair(const std::map<uint32_t, uint32_t>& data) {
google::protobuf::RepeatedPtrField<protobufs::UInt32Pair> result;
for (const auto& entry : data) {
protobufs::UInt32Pair pair;
pair.set_first(entry.first);
pair.set_second(entry.second);
*result.Add() = std::move(pair);
}
return result;
}
} // namespace fuzzerutil
} // namespace fuzz

View File

@ -15,6 +15,7 @@
#ifndef SOURCE_FUZZ_FUZZER_UTIL_H_
#define SOURCE_FUZZ_FUZZER_UTIL_H_
#include <map>
#include <vector>
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
@ -141,6 +142,13 @@ uint32_t GetNumberOfStructMembers(
uint32_t GetArraySize(const opt::Instruction& array_type_instruction,
opt::IRContext* context);
// Returns the bound for indexing into a composite of type
// |composite_type_inst|, i.e. the number of fields of a struct, the size of an
// array, the number of components of a vector, or the number of columns of a
// matrix. |composite_type_inst| must be the type of a composite.
uint32_t GetBoundForCompositeIndex(const opt::Instruction& composite_type_inst,
opt::IRContext* ir_context);
// Returns true if and only if |context| is valid, according to the validator
// instantiated with |validator_options|.
bool IsValid(opt::IRContext* context, spv_validator_options validator_options);
@ -281,6 +289,15 @@ bool IsPermutationOfRange(const std::vector<uint32_t>& arr, uint32_t lo,
std::vector<opt::Instruction*> GetParameters(opt::IRContext* ir_context,
uint32_t function_id);
// Removes an OpFunctionParameter instruction with result id |parameter_id|
// from the its function. Parameter's function must not be an entry-point
// function. The function must have a parameter with result id |parameter_id|.
//
// Prefer using this function to opt::Function::RemoveParameter since
// this function also guarantees that |ir_context| has no invalid pointers
// to the removed parameter.
void RemoveParameter(opt::IRContext* ir_context, uint32_t parameter_id);
// Returns all OpFunctionCall instructions that call a function with result id
// |function_id|.
std::vector<opt::Instruction*> GetCallers(opt::IRContext* ir_context,
@ -394,6 +411,14 @@ uint32_t MaybeGetIntegerConstant(
const std::vector<uint32_t>& words, uint32_t width, bool is_signed,
bool is_irrelevant);
// Returns the id of a 32-bit integer constant in the module with type
// |int_type_id| and value |value|, or 0 if no such constant exists in the
// module. |int_type_id| must exist in the module and it must correspond to a
// 32-bit integer type.
uint32_t MaybeGetIntegerConstantFromValueAndType(opt::IRContext* ir_context,
uint32_t value,
uint32_t int_type_id);
// Returns the result id of an OpConstant instruction of floating-point type.
// Returns 0 if no such instruction or type is present in the module.
// The returned id either participates in IdIsIrrelevant fact or not, depending
@ -441,6 +466,23 @@ inline uint32_t FloatToWord(float value) {
return result;
}
// Returns true if any of the following is true:
// - |type1_id| and |type2_id| are the same id
// - |type1_id| and |type2_id| refer to integer scalar or vector types, only
// differing by their signedness.
bool TypesAreEqualUpToSign(opt::IRContext* ir_context, uint32_t type1_id,
uint32_t type2_id);
// Converts repeated field of UInt32Pair to a map. If two or more equal values
// of |UInt32Pair::first()| are available in |data|, the last value of
// |UInt32Pair::second()| is used.
std::map<uint32_t, uint32_t> RepeatedUInt32PairToMap(
const google::protobuf::RepeatedPtrField<protobufs::UInt32Pair>& data);
// Converts a map into a repeated field of UInt32Pair.
google::protobuf::RepeatedPtrField<protobufs::UInt32Pair>
MapToRepeatedUInt32Pair(const std::map<uint32_t, uint32_t>& data);
} // namespace fuzzerutil
} // namespace fuzz

View File

@ -406,6 +406,8 @@ message Transformation {
TransformationReplaceParamsWithStruct replace_params_with_struct = 59;
TransformationReplaceCopyObjectWithStoreLoad replace_copy_object_with_store_load = 60;
TransformationReplaceCopyMemoryWithLoadStore replace_copy_memory_with_load_store = 61;
TransformationReplaceLoadStoreWithCopyMemory replace_load_store_with_copy_memory = 62;
TransformationAddLoopPreheader add_loop_preheader = 63;
// Add additional option using the next available number.
}
}
@ -416,6 +418,13 @@ message TransformationAccessChain {
// Adds an access chain instruction based on a given pointer and indices.
// When accessing a struct, the corresponding indices must be 32-bit integer constants.
// For any other composite, the indices can be any 32-bit integer, and the transformation
// adds two instructions for each such index to clamp it to the bound, as follows:
//
// %t1 = OpULessThanEqual %bool %index %bound_minus_one
// %t2 = OpSelect %int_type %t1 %index %bound_minus_one
// Result id for the access chain
uint32 fresh_id = 1;
@ -429,6 +438,10 @@ message TransformationAccessChain {
// OpAccessChain instruction should be inserted
InstructionDescriptor instruction_to_insert_before = 4;
// Additional fresh ids, required to clamp index variables. A pair is needed
// for each access to a non-struct composite.
repeated UInt32Pair fresh_ids_for_clamping = 5;
}
message TransformationAddConstantBoolean {
@ -690,6 +703,34 @@ message TransformationAddLocalVariable {
}
message TransformationAddLoopPreheader {
// A transformation that adds a loop preheader block before the given loop header.
// The id of the loop header block
uint32 loop_header_block = 1;
// A fresh id for the preheader block
uint32 fresh_id = 2;
// Fresh ids for splitting the OpPhi instructions in the header.
// A new OpPhi instruction in the preheader is needed for each OpPhi instruction in the header,
// if the header has more than one predecessor outside of the loop.
// This allows turning instructions of the form:
//
// %loop_header_block = OpLabel
// %id1 = OpPhi %type %val1 %pred1_id %val2 %pred2_id %val3 %backedge_block_id
//
// into:
// %fresh_id = OpLabel
// %phi_id1 = OpPhi %type %val1 %pred1_id %val2 %pred2_id
// OpBranch %header_id
// %loop_header_block = OpLabel
// %id1 = OpPhi %type %phi_id1 %fresh_id %val3 %backedge_block_id
repeated uint32 phi_id = 3;
}
message TransformationAddNoContractionDecoration {
// Applies OpDecorate NoContraction to the given result id
@ -1331,24 +1372,22 @@ message TransformationReplaceLinearAlgebraInstruction {
repeated uint32 fresh_ids = 1;
// A descriptor for a linear algebra instruction.
// This transformation is only applicable if the described instruction has one of the following opcodes.
// Supported:
// OpVectorTimesScalar
// OpMatrixTimesScalar
// OpVectorTimesMatrix
// OpMatrixTimesVector
// OpMatrixTimesMatrix
// OpDot
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3354):
// Right now we only support certain operations. When this issue is addressed
// the supporting comments can be removed.
// To be supported in the future:
// OpTranspose
// OpOuterProduct
InstructionDescriptor instruction_descriptor = 2;
}
message TransformationReplaceLoadStoreWithCopyMemory {
// A transformation that takes a pair of instruction descriptors
// to OpLoad and OpStore that have the same intermediate value
// and replaces the OpStore with an equivalent OpCopyMemory.
// The instruction descriptor to OpLoad
InstructionDescriptor load_instruction_descriptor = 1;
// The instruction descriptor to OpStore
InstructionDescriptor store_instruction_descriptor = 2;
}
message TransformationReplaceParamsWithStruct {
// Replaces parameters of the function with a struct containing
@ -1365,12 +1404,9 @@ message TransformationReplaceParamsWithStruct {
uint32 fresh_parameter_id = 3;
// Fresh ids for struct objects containing values of replaced parameters.
// This map contains a fresh id for at least every result id of a relevant
// This field contains a fresh id for at least every result id of a relevant
// OpFunctionCall instruction.
//
// While maps are not fully deterministic, the way this map is used does not
// exhibit nondeterminism. Change to repeated Uint32Pair if this changes.
map<uint32, uint32> caller_id_to_fresh_composite_id = 4;
repeated UInt32Pair caller_id_to_fresh_composite_id = 4;
}

View File

@ -121,10 +121,12 @@ Shrinker::ShrinkerResultStatus Shrinker::Run(
// succeeds, (b) get the binary that results from running these
// transformations, and (c) get the subsequence of the initial transformations
// that actually apply (in principle this could be a strict subsequence).
if (Replayer(impl_->target_env, impl_->validate_during_replay,
impl_->validator_options)
.Run(binary_in, initial_facts, transformation_sequence_in,
transformation_sequence_in.transformation_size(),
Replayer replayer(impl_->target_env, impl_->validate_during_replay,
impl_->validator_options);
replayer.SetMessageConsumer(impl_->consumer);
if (replayer.Run(binary_in, initial_facts, transformation_sequence_in,
static_cast<uint32_t>(
transformation_sequence_in.transformation_size()),
&current_best_binary, &current_best_transformations) !=
Replayer::ReplayerResultStatus::kComplete) {
return ShrinkerResultStatus::kReplayFailed;
@ -185,7 +187,8 @@ Shrinker::ShrinkerResultStatus Shrinker::Run(
// Remove a chunk of transformations according to the current index and
// chunk size.
auto transformations_with_chunk_removed =
RemoveChunk(current_best_transformations, chunk_index, chunk_size);
RemoveChunk(current_best_transformations,
static_cast<uint32_t>(chunk_index), chunk_size);
// Replay the smaller sequence of transformations to get a next binary and
// transformation sequence. Note that the transformations arising from
@ -194,10 +197,10 @@ Shrinker::ShrinkerResultStatus Shrinker::Run(
// transformations inapplicable.
std::vector<uint32_t> next_binary;
protobufs::TransformationSequence next_transformation_sequence;
if (Replayer(impl_->target_env, impl_->validate_during_replay,
impl_->validator_options)
.Run(binary_in, initial_facts, transformations_with_chunk_removed,
transformations_with_chunk_removed.transformation_size(),
if (replayer.Run(
binary_in, initial_facts, transformations_with_chunk_removed,
static_cast<uint32_t>(
transformations_with_chunk_removed.transformation_size()),
&next_binary, &next_transformation_sequence) !=
Replayer::ReplayerResultStatus::kComplete) {
// Replay should not fail; if it does, we need to abort shrinking.

View File

@ -31,6 +31,7 @@
#include "source/fuzz/transformation_add_global_variable.h"
#include "source/fuzz/transformation_add_image_sample_unused_components.h"
#include "source/fuzz/transformation_add_local_variable.h"
#include "source/fuzz/transformation_add_loop_preheader.h"
#include "source/fuzz/transformation_add_no_contraction_decoration.h"
#include "source/fuzz/transformation_add_parameter.h"
#include "source/fuzz/transformation_add_relaxed_decoration.h"
@ -66,6 +67,7 @@
#include "source/fuzz/transformation_replace_copy_object_with_store_load.h"
#include "source/fuzz/transformation_replace_id_with_synonym.h"
#include "source/fuzz/transformation_replace_linear_algebra_instruction.h"
#include "source/fuzz/transformation_replace_load_store_with_copy_memory.h"
#include "source/fuzz/transformation_replace_parameter_with_global.h"
#include "source/fuzz/transformation_replace_params_with_struct.h"
#include "source/fuzz/transformation_set_function_control.h"
@ -126,6 +128,9 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
case protobufs::Transformation::TransformationCase::kAddLocalVariable:
return MakeUnique<TransformationAddLocalVariable>(
message.add_local_variable());
case protobufs::Transformation::TransformationCase::kAddLoopPreheader:
return MakeUnique<TransformationAddLoopPreheader>(
message.add_loop_preheader());
case protobufs::Transformation::TransformationCase::
kAddNoContractionDecoration:
return MakeUnique<TransformationAddNoContractionDecoration>(
@ -233,6 +238,10 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
kReplaceLinearAlgebraInstruction:
return MakeUnique<TransformationReplaceLinearAlgebraInstruction>(
message.replace_linear_algebra_instruction());
case protobufs::Transformation::TransformationCase::
kReplaceLoadStoreWithCopyMemory:
return MakeUnique<TransformationReplaceLoadStoreWithCopyMemory>(
message.replace_load_store_with_copy_memory());
case protobufs::Transformation::TransformationCase::
kReplaceParamsWithStruct:
return MakeUnique<TransformationReplaceParamsWithStruct>(

View File

@ -29,7 +29,8 @@ TransformationAccessChain::TransformationAccessChain(
TransformationAccessChain::TransformationAccessChain(
uint32_t fresh_id, uint32_t pointer_id,
const std::vector<uint32_t>& index_id,
const protobufs::InstructionDescriptor& instruction_to_insert_before) {
const protobufs::InstructionDescriptor& instruction_to_insert_before,
const std::vector<std::pair<uint32_t, uint32_t>>& fresh_ids_for_clamping) {
message_.set_fresh_id(fresh_id);
message_.set_pointer_id(pointer_id);
for (auto id : index_id) {
@ -37,12 +38,22 @@ TransformationAccessChain::TransformationAccessChain(
}
*message_.mutable_instruction_to_insert_before() =
instruction_to_insert_before;
for (auto clamping_ids_pair : fresh_ids_for_clamping) {
protobufs::UInt32Pair pair;
pair.set_first(clamping_ids_pair.first);
pair.set_second(clamping_ids_pair.second);
*message_.add_fresh_ids_for_clamping() = pair;
}
}
bool TransformationAccessChain::IsApplicable(
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
// The result id must be fresh
if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
// Keep track of the fresh ids used to make sure that they are distinct.
std::set<uint32_t> fresh_ids_used;
// The result id must be fresh.
if (!CheckIdIsFreshAndNotUsedByThisTransformation(
message_.fresh_id(), ir_context, &fresh_ids_used)) {
return false;
}
// The pointer id must exist and have a type.
@ -50,7 +61,7 @@ bool TransformationAccessChain::IsApplicable(
if (!pointer || !pointer->type_id()) {
return false;
}
// The type must indeed be a pointer
// The type must indeed be a pointer.
auto pointer_type = ir_context->get_def_use_mgr()->GetDef(pointer->type_id());
if (pointer_type->opcode() != SpvOpTypePointer) {
return false;
@ -96,23 +107,86 @@ bool TransformationAccessChain::IsApplicable(
// Start from the base type of the pointer.
uint32_t subobject_type_id = pointer_type->GetSingleWordInOperand(1);
int id_pairs_used = 0;
// Consider the given index ids in turn.
for (auto index_id : message_.index_id()) {
// Try to get the integer value associated with this index is. The first
// component of the result will be false if the id did not correspond to an
// integer. Otherwise, the integer with which the id is associated is the
// second component.
std::pair<bool, uint32_t> maybe_index_value =
GetIndexValue(ir_context, index_id);
if (!maybe_index_value.first) {
// There was no integer: this index is no good.
// The index value will correspond to the value of the index if the object
// is a struct, otherwise the value 0 will be used.
uint32_t index_value;
// Check whether the object is a struct.
if (ir_context->get_def_use_mgr()->GetDef(subobject_type_id)->opcode() ==
SpvOpTypeStruct) {
// It is a struct: we need to retrieve the integer value.
bool successful;
std::tie(successful, index_value) =
GetIndexValue(ir_context, index_id, subobject_type_id);
if (!successful) {
return false;
}
} else {
// It is not a struct: the index will need clamping.
if (message_.fresh_ids_for_clamping().size() <= id_pairs_used) {
// We don't have enough ids
return false;
}
// Get two new ids to use and update the amount used.
protobufs::UInt32Pair fresh_ids =
message_.fresh_ids_for_clamping()[id_pairs_used++];
// Valid ids need to have been given
if (fresh_ids.first() == 0 || fresh_ids.second() == 0) {
return false;
}
// Check that the ids are actually fresh and not already used by this
// transformation.
if (!CheckIdIsFreshAndNotUsedByThisTransformation(
fresh_ids.first(), ir_context, &fresh_ids_used) ||
!CheckIdIsFreshAndNotUsedByThisTransformation(
fresh_ids.second(), ir_context, &fresh_ids_used)) {
return false;
}
if (!ValidIndexToComposite(ir_context, index_id, subobject_type_id)) {
return false;
}
// Perform the clamping using the fresh ids at our disposal.
auto index_instruction = ir_context->get_def_use_mgr()->GetDef(index_id);
uint32_t bound = fuzzerutil::GetBoundForCompositeIndex(
*ir_context->get_def_use_mgr()->GetDef(subobject_type_id),
ir_context);
// The module must have an integer constant of value bound-1 of the same
// type as the index.
if (!fuzzerutil::MaybeGetIntegerConstantFromValueAndType(
ir_context, bound - 1, index_instruction->type_id())) {
return false;
}
// The module must have the definition of bool type to make a comparison.
if (!fuzzerutil::MaybeGetBoolType(ir_context)) {
return false;
}
// The index is not necessarily a constant, so we may not know its value.
// We can use index 0 because the components of a non-struct composite
// all have the same type, and index 0 is always in bounds.
index_value = 0;
}
// Try to walk down the type using this index. This will yield 0 if the
// type is not a composite or the index is out of bounds, and the id of
// the next type otherwise.
subobject_type_id = fuzzerutil::WalkOneCompositeTypeIndex(
ir_context, subobject_type_id, maybe_index_value.second);
ir_context, subobject_type_id, index_value);
if (!subobject_type_id) {
// Either the type was not a composite (so that too many indices were
// provided), or the index was out of bounds.
@ -152,25 +226,105 @@ void TransformationAccessChain::Apply(
ir_context->get_def_use_mgr()->GetDef(message_.pointer_id())->type_id());
uint32_t subobject_type_id = pointer_type->GetSingleWordInOperand(1);
uint32_t id_pairs_used = 0;
// Go through the index ids in turn.
for (auto index_id : message_.index_id()) {
// Add the index id to the operands.
operands.push_back({SPV_OPERAND_TYPE_ID, {index_id}});
// Get the integer value associated with the index id.
uint32_t index_value = GetIndexValue(ir_context, index_id).second;
uint32_t index_value;
// Actual id to be used in the instruction: the original id
// or the clamped one.
uint32_t new_index_id;
// Check whether the object is a struct.
if (ir_context->get_def_use_mgr()->GetDef(subobject_type_id)->opcode() ==
SpvOpTypeStruct) {
// It is a struct: we need to retrieve the integer value.
index_value =
GetIndexValue(ir_context, index_id, subobject_type_id).second;
new_index_id = index_id;
} else {
// It is not a struct: the index will need clamping.
// Get two new ids to use and update the amount used.
protobufs::UInt32Pair fresh_ids =
message_.fresh_ids_for_clamping()[id_pairs_used++];
// Perform the clamping using the fresh ids at our disposal.
// The module will not be changed if |add_clamping_instructions| is not
// set.
auto index_instruction = ir_context->get_def_use_mgr()->GetDef(index_id);
uint32_t bound = fuzzerutil::GetBoundForCompositeIndex(
*ir_context->get_def_use_mgr()->GetDef(subobject_type_id),
ir_context);
auto bound_minus_one_id =
fuzzerutil::MaybeGetIntegerConstantFromValueAndType(
ir_context, bound - 1, index_instruction->type_id());
assert(bound_minus_one_id &&
"A constant of value bound - 1 and the same type as the index "
"must exist as a precondition.");
uint32_t bool_type_id = fuzzerutil::MaybeGetBoolType(ir_context);
assert(bool_type_id &&
"An OpTypeBool instruction must exist as a precondition.");
auto int_type_inst =
ir_context->get_def_use_mgr()->GetDef(index_instruction->type_id());
// Clamp the integer and add the corresponding instructions in the module
// if |add_clamping_instructions| is set.
auto instruction_to_insert_before =
FindInstruction(message_.instruction_to_insert_before(), ir_context);
// Compare the index with the bound via an instruction of the form:
// %fresh_ids.first = OpULessThanEqual %bool %int_id %bound_minus_one.
fuzzerutil::UpdateModuleIdBound(ir_context, fresh_ids.first());
instruction_to_insert_before->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, SpvOpULessThanEqual, bool_type_id, fresh_ids.first(),
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {index_instruction->result_id()}},
{SPV_OPERAND_TYPE_ID, {bound_minus_one_id}}})));
// Select the index if in-bounds, otherwise one less than the bound:
// %fresh_ids.second = OpSelect %int_type %fresh_ids.first %int_id
// %bound_minus_one
fuzzerutil::UpdateModuleIdBound(ir_context, fresh_ids.second());
instruction_to_insert_before->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, SpvOpSelect, int_type_inst->result_id(),
fresh_ids.second(),
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {fresh_ids.first()}},
{SPV_OPERAND_TYPE_ID, {index_instruction->result_id()}},
{SPV_OPERAND_TYPE_ID, {bound_minus_one_id}}})));
new_index_id = fresh_ids.second();
index_value = 0;
}
// Add the correct index id to the operands.
operands.push_back({SPV_OPERAND_TYPE_ID, {new_index_id}});
// Walk to the next type in the composite object using this index.
subobject_type_id = fuzzerutil::WalkOneCompositeTypeIndex(
ir_context, subobject_type_id, index_value);
}
// The access chain's result type is a pointer to the composite component that
// was reached after following all indices. The storage class is that of the
// original pointer.
// The access chain's result type is a pointer to the composite component
// that was reached after following all indices. The storage class is that
// of the original pointer.
uint32_t result_type = fuzzerutil::MaybeGetPointerType(
ir_context, subobject_type_id,
static_cast<SpvStorageClass>(pointer_type->GetSingleWordInOperand(0)));
// Add the access chain instruction to the module, and update the module's id
// bound.
// Add the access chain instruction to the module, and update the module's
// id bound.
fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
FindInstruction(message_.instruction_to_insert_before(), ir_context)
->InsertBefore(MakeUnique<opt::Instruction>(
@ -180,8 +334,8 @@ void TransformationAccessChain::Apply(
// Conservatively invalidate all analyses.
ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
// If the base pointer's pointee value was irrelevant, the same is true of the
// pointee value of the result of this access chain.
// If the base pointer's pointee value was irrelevant, the same is true of
// the pointee value of the result of this access chain.
if (transformation_context->GetFactManager()->PointeeValueIsIrrelevant(
message_.pointer_id())) {
transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
@ -196,21 +350,61 @@ protobufs::Transformation TransformationAccessChain::ToMessage() const {
}
std::pair<bool, uint32_t> TransformationAccessChain::GetIndexValue(
opt::IRContext* ir_context, uint32_t index_id) const {
auto index_instruction = ir_context->get_def_use_mgr()->GetDef(index_id);
if (!index_instruction || !spvOpcodeIsConstant(index_instruction->opcode())) {
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3179) We could
// allow non-constant indices when looking up non-structs, using clamping
// to ensure they are in-bounds.
opt::IRContext* ir_context, uint32_t index_id,
uint32_t object_type_id) const {
if (!ValidIndexToComposite(ir_context, index_id, object_type_id)) {
return {false, 0};
}
auto index_instruction = ir_context->get_def_use_mgr()->GetDef(index_id);
uint32_t bound = fuzzerutil::GetBoundForCompositeIndex(
*ir_context->get_def_use_mgr()->GetDef(object_type_id), ir_context);
// The index must be a constant
if (!spvOpcodeIsConstant(index_instruction->opcode())) {
return {false, 0};
}
// The index must be in bounds.
uint32_t value = index_instruction->GetSingleWordInOperand(0);
if (value >= bound) {
return {false, 0};
}
return {true, value};
}
bool TransformationAccessChain::ValidIndexToComposite(
opt::IRContext* ir_context, uint32_t index_id, uint32_t object_type_id) {
auto object_type_def = ir_context->get_def_use_mgr()->GetDef(object_type_id);
// The object being indexed must be a composite.
if (!spvOpcodeIsComposite(object_type_def->opcode())) {
return false;
}
// Get the defining instruction of the index.
auto index_instruction = ir_context->get_def_use_mgr()->GetDef(index_id);
if (!index_instruction) {
return false;
}
// The index type must be 32-bit integer.
auto index_type =
ir_context->get_def_use_mgr()->GetDef(index_instruction->type_id());
if (index_type->opcode() != SpvOpTypeInt ||
index_type->GetSingleWordInOperand(0) != 32) {
return {false, 0};
return false;
}
return {true, index_instruction->GetSingleWordInOperand(0)};
// If the object being traversed is a struct, the id must correspond to an
// in-bound constant.
if (object_type_def->opcode() == SpvOpTypeStruct) {
if (!spvOpcodeIsConstant(index_instruction->opcode())) {
return false;
}
}
return true;
}
} // namespace fuzz

View File

@ -33,20 +33,28 @@ class TransformationAccessChain : public Transformation {
TransformationAccessChain(
uint32_t fresh_id, uint32_t pointer_id,
const std::vector<uint32_t>& index_id,
const protobufs::InstructionDescriptor& instruction_to_insert_before);
const protobufs::InstructionDescriptor& instruction_to_insert_before,
const std::vector<std::pair<uint32_t, uint32_t>>& fresh_ids_for_clamping =
{});
// - |message_.fresh_id| must be fresh
// - |message_.fresh_id| must be fresh.
// - |message_.instruction_to_insert_before| must identify an instruction
// before which it is legitimate to insert an OpAccessChain instruction
// before which it is legitimate to insert an OpAccessChain instruction.
// - |message_.pointer_id| must be a result id with pointer type that is
// available (according to dominance rules) at the insertion point.
// - The pointer must not be OpConstantNull or OpUndef
// - |message_.index_id| must be a sequence of ids of 32-bit integer constants
// - The pointer must not be OpConstantNull or OpUndef.
// - |message_.index_id| must be a sequence of ids of 32-bit integers
// such that it is possible to walk the pointee type of
// |message_.pointer_id| using these indices, remaining in-bounds.
// |message_.pointer_id| using these indices.
// - All indices used to access a struct must be OpConstant.
// - The indices used to index non-struct composites will be clamped to be
// in bound. Enough fresh ids must be given in
// |message_.fresh_id_for_clamping| to perform clamping (2 for
// each index accessing a non-struct). This requires the bool type and
// a constant of value (bound - 1) to be declared in the module.
// - If type t is the final type reached by walking these indices, the module
// must include an instruction "OpTypePointer SC %t" where SC is the storage
// class associated with |message_.pointer_id|
// class associated with |message_.pointer_id|.
bool IsApplicable(
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const override;
@ -58,6 +66,9 @@ class TransformationAccessChain : public Transformation {
// the indices in |message_.index_id|, and with the same storage class as
// |message_.pointer_id|.
//
// For each of the indices traversing non-struct composites, two clamping
// instructions are added using ids in |message_.fresh_id_for_clamping|.
//
// If the fact manager in |transformation_context| reports that
// |message_.pointer_id| has an irrelevant pointee value, then the fact that
// |message_.fresh_id| (the result of the access chain) also has an irrelevant
@ -68,11 +79,21 @@ class TransformationAccessChain : public Transformation {
protobufs::Transformation ToMessage() const override;
private:
// Returns {false, 0} if |index_id| does not correspond to a 32-bit integer
// constant. Otherwise, returns {true, value}, where value is the value of
// the 32-bit integer constant to which |index_id| corresponds.
// Returns {false, 0} in each of the following cases:
// - |index_id| does not correspond to a 32-bit integer constant
// - the object being indexed is not a composite type
// - the constant at |index_id| is out of bounds.
// Otherwise, returns {true, value}, where value is the value of the constant
// at |index_id|.
std::pair<bool, uint32_t> GetIndexValue(opt::IRContext* ir_context,
uint32_t index_id) const;
uint32_t index_id,
uint32_t object_type_id) const;
// Returns true if |index_id| corresponds, in the given context, to a 32-bit
// integer which can be used to index an object of the type specified by
// |object_type_id|. Returns false otherwise.
static bool ValidIndexToComposite(opt::IRContext* ir_context,
uint32_t index_id, uint32_t object_type_id);
protobufs::TransformationAccessChain message_;
};

View File

@ -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) {

View File

@ -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.

View File

@ -0,0 +1,227 @@
// Copyright (c) 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "transformation_add_loop_preheader.h"
#include "source/fuzz/fuzzer_util.h"
#include "source/opt/instruction.h"
namespace spvtools {
namespace fuzz {
TransformationAddLoopPreheader::TransformationAddLoopPreheader(
const protobufs::TransformationAddLoopPreheader& message)
: message_(message) {}
TransformationAddLoopPreheader::TransformationAddLoopPreheader(
uint32_t loop_header_block, uint32_t fresh_id,
std::vector<uint32_t> phi_id) {
message_.set_loop_header_block(loop_header_block);
message_.set_fresh_id(fresh_id);
for (auto id : phi_id) {
message_.add_phi_id(id);
}
}
bool TransformationAddLoopPreheader::IsApplicable(
opt::IRContext* ir_context,
const TransformationContext& /* unused */) const {
// |message_.loop_header_block()| must be the id of a loop header block.
opt::BasicBlock* loop_header_block =
fuzzerutil::MaybeFindBlock(ir_context, message_.loop_header_block());
if (!loop_header_block || !loop_header_block->IsLoopHeader()) {
return false;
}
// The id for the preheader must actually be fresh.
std::set<uint32_t> used_ids;
if (!CheckIdIsFreshAndNotUsedByThisTransformation(message_.fresh_id(),
ir_context, &used_ids)) {
return false;
}
size_t num_predecessors =
ir_context->cfg()->preds(message_.loop_header_block()).size();
// The block must have at least 2 predecessors (the back-edge block and
// another predecessor outside of the loop)
if (num_predecessors < 2) {
return false;
}
// If the block only has one predecessor outside of the loop (and thus 2 in
// total), then no additional fresh ids are necessary.
if (num_predecessors == 2) {
return true;
}
// Count the number of OpPhi instructions.
int32_t num_phi_insts = 0;
loop_header_block->ForEachPhiInst(
[&num_phi_insts](opt::Instruction* /* unused */) { num_phi_insts++; });
// There must be enough fresh ids for the OpPhi instructions.
if (num_phi_insts > message_.phi_id_size()) {
return false;
}
// Check that the needed ids are fresh and distinct.
for (int32_t i = 0; i < num_phi_insts; i++) {
if (!CheckIdIsFreshAndNotUsedByThisTransformation(message_.phi_id(i),
ir_context, &used_ids)) {
return false;
}
}
return true;
}
void TransformationAddLoopPreheader::Apply(
opt::IRContext* ir_context,
TransformationContext* /* transformation_context */) const {
// Find the loop header.
opt::BasicBlock* loop_header =
fuzzerutil::MaybeFindBlock(ir_context, message_.loop_header_block());
auto dominator_analysis =
ir_context->GetDominatorAnalysis(loop_header->GetParent());
uint32_t back_edge_block_id = 0;
// Update the branching instructions of the out-of-loop predecessors of the
// header. Set |back_edge_block_id| to be the id of the back-edge block.
ir_context->get_def_use_mgr()->ForEachUse(
loop_header->id(),
[this, &ir_context, &dominator_analysis, &loop_header,
&back_edge_block_id](opt::Instruction* use_inst, uint32_t use_index) {
if (dominator_analysis->Dominates(loop_header->GetLabelInst(),
use_inst)) {
// If |use_inst| is a branch instruction dominated by the header, the
// block containing it is the back-edge block.
if (use_inst->IsBranch()) {
assert(back_edge_block_id == 0 &&
"There should only be one back-edge block");
back_edge_block_id = ir_context->get_instr_block(use_inst)->id();
}
// References to the header inside the loop should not be updated
return;
}
// If |use_inst| is not a branch or merge instruction, it should not be
// changed.
if (!use_inst->IsBranch() &&
use_inst->opcode() != SpvOpSelectionMerge &&
use_inst->opcode() != SpvOpLoopMerge) {
return;
}
// Update the reference.
use_inst->SetOperand(use_index, {message_.fresh_id()});
});
assert(back_edge_block_id && "The back-edge block should have been found");
// Make a new block for the preheader.
std::unique_ptr<opt::BasicBlock> preheader = MakeUnique<opt::BasicBlock>(
std::unique_ptr<opt::Instruction>(new opt::Instruction(
ir_context, SpvOpLabel, 0, message_.fresh_id(), {})));
uint32_t phi_ids_used = 0;
// Update the OpPhi instructions and, if there is more than one out-of-loop
// predecessor, add necessary OpPhi instructions so the preheader.
loop_header->ForEachPhiInst([this, &ir_context, &preheader,
&back_edge_block_id,
&phi_ids_used](opt::Instruction* phi_inst) {
// The loop header must have at least 2 incoming edges (the back edge, and
// at least one from outside the loop).
assert(phi_inst->NumInOperands() >= 4);
if (phi_inst->NumInOperands() == 4) {
// There is just one out-of-loop predecessor, so no additional
// instructions in the preheader are necessary. The reference to the
// original out-of-loop predecessor needs to be updated so that it refers
// to the preheader.
uint32_t index_of_out_of_loop_pred_id =
phi_inst->GetInOperand(1).words[0] == back_edge_block_id ? 3 : 1;
phi_inst->SetInOperand(index_of_out_of_loop_pred_id, {preheader->id()});
} else {
// There is more than one out-of-loop predecessor, so an OpPhi instruction
// needs to be added to the preheader, and its value will depend on all
// the current out-of-loop predecessors of the header.
// Get the operand list and the value corresponding to the back-edge
// block.
std::vector<opt::Operand> preheader_in_operands;
uint32_t back_edge_val = 0;
for (uint32_t i = 0; i < phi_inst->NumInOperands(); i += 2) {
// Only add operands if they don't refer to the back-edge block.
if (phi_inst->GetInOperand(i + 1).words[0] == back_edge_block_id) {
back_edge_val = phi_inst->GetInOperand(i).words[0];
} else {
preheader_in_operands.push_back(std::move(phi_inst->GetInOperand(i)));
preheader_in_operands.push_back(
std::move(phi_inst->GetInOperand(i + 1)));
}
}
// Add the new instruction to the preheader.
uint32_t fresh_phi_id = message_.phi_id(phi_ids_used++);
// Update id bound.
fuzzerutil::UpdateModuleIdBound(ir_context, fresh_phi_id);
preheader->AddInstruction(std::unique_ptr<opt::Instruction>(
new opt::Instruction(ir_context, SpvOpPhi, phi_inst->type_id(),
fresh_phi_id, preheader_in_operands)));
// Update the OpPhi instruction in the header so that it refers to the
// back edge block and the preheader as the predecessors, and it uses the
// newly-defined OpPhi in the preheader for the corresponding value.
phi_inst->SetInOperands(
{{SPV_OPERAND_TYPE_RESULT_ID, {fresh_phi_id}},
{SPV_OPERAND_TYPE_RESULT_ID, {preheader->id()}},
{SPV_OPERAND_TYPE_RESULT_ID, {back_edge_val}},
{SPV_OPERAND_TYPE_RESULT_ID, {back_edge_block_id}}});
}
});
// Update id bound.
fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
// Add an unconditional branch from the preheader to the header.
preheader->AddInstruction(std::unique_ptr<opt::Instruction>(
new opt::Instruction(ir_context, SpvOpBranch, 0, 0,
std::initializer_list<opt::Operand>{opt::Operand(
spv_operand_type_t::SPV_OPERAND_TYPE_RESULT_ID,
{loop_header->id()})})));
// Insert the preheader in the module.
loop_header->GetParent()->InsertBasicBlockBefore(std::move(preheader),
loop_header);
// Invalidate analyses because the structure of the program changed.
ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
}
protobufs::Transformation TransformationAddLoopPreheader::ToMessage() const {
protobufs::Transformation result;
*result.mutable_add_loop_preheader() = message_;
return result;
}
} // namespace fuzz
} // namespace spvtools

View File

@ -0,0 +1,57 @@
// Copyright (c) 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_LOOP_PREHEADER_H
#define SOURCE_FUZZ_TRANSFORMATION_ADD_LOOP_PREHEADER_H
#include "source/fuzz/transformation.h"
namespace spvtools {
namespace fuzz {
class TransformationAddLoopPreheader : public Transformation {
public:
explicit TransformationAddLoopPreheader(
const protobufs::TransformationAddLoopPreheader& message);
TransformationAddLoopPreheader(uint32_t loop_header_block, uint32_t fresh_id,
std::vector<uint32_t> phi_id);
// - |message_.loop_header_block| must be the id of a loop header block in
// the given module.
// - |message_.fresh_id| must be an available id.
// - |message_.phi_ids| must be a list of available ids.
// It can be empty if the loop header only has one predecessor outside of
// the loop. Otherwise, it must contain at least as many ids as OpPhi
// instructions in the loop header block.
bool IsApplicable(
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const override;
// Adds a preheader block as the unique out-of-loop predecessor of the given
// loop header block. All of the existing out-of-loop predecessors of the
// header are changed so that they branch to the preheader instead.
void Apply(opt::IRContext* ir_context,
TransformationContext* transformation_context) const override;
protobufs::Transformation ToMessage() const override;
private:
protobufs::TransformationAddLoopPreheader message_;
};
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_LOOP_PREHEADER_H

View File

@ -21,20 +21,6 @@
namespace spvtools {
namespace fuzz {
namespace {
std::map<uint32_t, uint32_t> PairSequenceToMap(
const google::protobuf::RepeatedPtrField<protobufs::UInt32Pair>&
pair_sequence) {
std::map<uint32_t, uint32_t> result;
for (auto& pair : pair_sequence) {
result[pair.first()] = pair.second();
}
return result;
}
} // namespace
TransformationOutlineFunction::TransformationOutlineFunction(
const spvtools::fuzz::protobufs::TransformationOutlineFunction& message)
: message_(message) {}
@ -55,18 +41,10 @@ TransformationOutlineFunction::TransformationOutlineFunction(
message_.set_new_function_region_entry_block(new_function_region_entry_block);
message_.set_new_caller_result_id(new_caller_result_id);
message_.set_new_callee_result_id(new_callee_result_id);
for (auto& entry : input_id_to_fresh_id) {
protobufs::UInt32Pair pair;
pair.set_first(entry.first);
pair.set_second(entry.second);
*message_.add_input_id_to_fresh_id() = pair;
}
for (auto& entry : output_id_to_fresh_id) {
protobufs::UInt32Pair pair;
pair.set_first(entry.first);
pair.set_second(entry.second);
*message_.add_output_id_to_fresh_id() = pair;
}
*message_.mutable_input_id_to_fresh_id() =
fuzzerutil::MapToRepeatedUInt32Pair(input_id_to_fresh_id);
*message_.mutable_output_id_to_fresh_id() =
fuzzerutil::MapToRepeatedUInt32Pair(output_id_to_fresh_id);
}
bool TransformationOutlineFunction::IsApplicable(
@ -252,8 +230,8 @@ bool TransformationOutlineFunction::IsApplicable(
// For each region input id, i.e. every id defined outside the region but
// used inside the region, ...
std::map<uint32_t, uint32_t> input_id_to_fresh_id_map =
PairSequenceToMap(message_.input_id_to_fresh_id());
auto input_id_to_fresh_id_map =
fuzzerutil::RepeatedUInt32PairToMap(message_.input_id_to_fresh_id());
for (auto id : GetRegionInputIds(ir_context, region_set, exit_block)) {
// There needs to be a corresponding fresh id to be used as a function
// parameter.
@ -280,8 +258,8 @@ bool TransformationOutlineFunction::IsApplicable(
// For each region output id -- i.e. every id defined inside the region but
// used outside the region, ...
std::map<uint32_t, uint32_t> output_id_to_fresh_id_map =
PairSequenceToMap(message_.output_id_to_fresh_id());
auto output_id_to_fresh_id_map =
fuzzerutil::RepeatedUInt32PairToMap(message_.output_id_to_fresh_id());
for (auto id : GetRegionOutputIds(ir_context, region_set, exit_block)) {
if (
// ... there needs to be a corresponding fresh id that can hold the
@ -323,10 +301,10 @@ void TransformationOutlineFunction::Apply(
GetRegionOutputIds(ir_context, region_blocks, original_region_exit_block);
// Maps from input and output ids to fresh ids.
std::map<uint32_t, uint32_t> input_id_to_fresh_id_map =
PairSequenceToMap(message_.input_id_to_fresh_id());
std::map<uint32_t, uint32_t> output_id_to_fresh_id_map =
PairSequenceToMap(message_.output_id_to_fresh_id());
auto input_id_to_fresh_id_map =
fuzzerutil::RepeatedUInt32PairToMap(message_.input_id_to_fresh_id());
auto output_id_to_fresh_id_map =
fuzzerutil::RepeatedUInt32PairToMap(message_.output_id_to_fresh_id());
UpdateModuleIdBoundForFreshIds(ir_context, input_id_to_fresh_id_map,
output_id_to_fresh_id_map);

View File

@ -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.

View File

@ -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();

View File

@ -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;

View File

@ -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

View File

@ -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_;
};

View File

@ -41,16 +41,8 @@ bool TransformationReplaceLinearAlgebraInstruction::IsApplicable(
auto instruction =
FindInstruction(message_.instruction_descriptor(), ir_context);
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3354):
// Right now we only support certain operations. When this issue is addressed
// the following conditional can use the function |spvOpcodeIsLinearAlgebra|.
// It must be a supported linear algebra instruction.
if (instruction->opcode() != SpvOpVectorTimesScalar &&
instruction->opcode() != SpvOpMatrixTimesScalar &&
instruction->opcode() != SpvOpVectorTimesMatrix &&
instruction->opcode() != SpvOpMatrixTimesVector &&
instruction->opcode() != SpvOpMatrixTimesMatrix &&
instruction->opcode() != SpvOpDot) {
// It must be a linear algebra instruction.
if (!spvOpcodeIsLinearAlgebra(instruction->opcode())) {
return false;
}
@ -77,6 +69,9 @@ void TransformationReplaceLinearAlgebraInstruction::Apply(
FindInstruction(message_.instruction_descriptor(), ir_context);
switch (linear_algebra_instruction->opcode()) {
case SpvOpTranspose:
ReplaceOpTranspose(ir_context, linear_algebra_instruction);
break;
case SpvOpVectorTimesScalar:
ReplaceOpVectorTimesScalar(ir_context, linear_algebra_instruction);
break;
@ -92,6 +87,9 @@ void TransformationReplaceLinearAlgebraInstruction::Apply(
case SpvOpMatrixTimesMatrix:
ReplaceOpMatrixTimesMatrix(ir_context, linear_algebra_instruction);
break;
case SpvOpOuterProduct:
ReplaceOpOuterProduct(ir_context, linear_algebra_instruction);
break;
case SpvOpDot:
ReplaceOpDot(ir_context, linear_algebra_instruction);
break;
@ -115,6 +113,24 @@ uint32_t TransformationReplaceLinearAlgebraInstruction::GetRequiredFreshIdCount(
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3354):
// Right now we only support certain operations.
switch (instruction->opcode()) {
case SpvOpTranspose: {
// For each matrix row, |2 * matrix_column_count| OpCompositeExtract and 1
// OpCompositeConstruct will be inserted.
auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef(
instruction->GetSingleWordInOperand(0));
uint32_t matrix_column_count =
ir_context->get_type_mgr()
->GetType(matrix_instruction->type_id())
->AsMatrix()
->element_count();
uint32_t matrix_row_count = ir_context->get_type_mgr()
->GetType(matrix_instruction->type_id())
->AsMatrix()
->element_type()
->AsVector()
->element_count();
return matrix_row_count * (2 * matrix_column_count + 1);
}
case SpvOpVectorTimesScalar:
// For each vector component, 1 OpCompositeExtract and 1 OpFMul will be
// inserted.
@ -213,6 +229,26 @@ uint32_t TransformationReplaceLinearAlgebraInstruction::GetRequiredFreshIdCount(
return matrix_2_column_count *
(2 + matrix_1_row_count * (5 * matrix_1_column_count - 1));
}
case SpvOpOuterProduct: {
// For each |vector_2| component, |vector_1_component_count + 1|
// OpCompositeExtract, |vector_1_component_count| OpFMul and 1
// OpCompositeConstruct instructions will be inserted.
auto vector_1_instruction = ir_context->get_def_use_mgr()->GetDef(
instruction->GetSingleWordInOperand(0));
auto vector_2_instruction = ir_context->get_def_use_mgr()->GetDef(
instruction->GetSingleWordInOperand(1));
uint32_t vector_1_component_count =
ir_context->get_type_mgr()
->GetType(vector_1_instruction->type_id())
->AsVector()
->element_count();
uint32_t vector_2_component_count =
ir_context->get_type_mgr()
->GetType(vector_2_instruction->type_id())
->AsVector()
->element_count();
return 2 * vector_2_component_count * (vector_1_component_count + 1);
}
case SpvOpDot:
// For each pair of vector components, 2 OpCompositeExtract and 1 OpFMul
// will be inserted. The first two OpFMul instructions will result the
@ -233,6 +269,80 @@ uint32_t TransformationReplaceLinearAlgebraInstruction::GetRequiredFreshIdCount(
}
}
void TransformationReplaceLinearAlgebraInstruction::ReplaceOpTranspose(
opt::IRContext* ir_context,
opt::Instruction* linear_algebra_instruction) const {
// Gets OpTranspose instruction information.
auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef(
linear_algebra_instruction->GetSingleWordInOperand(0));
uint32_t matrix_column_count = ir_context->get_type_mgr()
->GetType(matrix_instruction->type_id())
->AsMatrix()
->element_count();
auto matrix_column_type = ir_context->get_type_mgr()
->GetType(matrix_instruction->type_id())
->AsMatrix()
->element_type();
auto matrix_column_component_type =
matrix_column_type->AsVector()->element_type();
uint32_t matrix_row_count = matrix_column_type->AsVector()->element_count();
auto resulting_matrix_column_type =
ir_context->get_type_mgr()
->GetType(linear_algebra_instruction->type_id())
->AsMatrix()
->element_type();
uint32_t fresh_id_index = 0;
std::vector<uint32_t> result_column_ids(matrix_row_count);
for (uint32_t i = 0; i < matrix_row_count; i++) {
std::vector<uint32_t> column_component_ids(matrix_column_count);
for (uint32_t j = 0; j < matrix_column_count; j++) {
// Extracts the matrix column.
uint32_t matrix_column_id = message_.fresh_ids(fresh_id_index++);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, SpvOpCompositeExtract,
ir_context->get_type_mgr()->GetId(matrix_column_type),
matrix_column_id,
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {matrix_instruction->result_id()}},
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {j}}})));
// Extracts the matrix column component.
column_component_ids[j] = message_.fresh_ids(fresh_id_index++);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, SpvOpCompositeExtract,
ir_context->get_type_mgr()->GetId(matrix_column_component_type),
column_component_ids[j],
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {matrix_column_id}},
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
}
// Inserts the resulting matrix column.
opt::Instruction::OperandList in_operands;
for (auto& column_component_id : column_component_ids) {
in_operands.push_back({SPV_OPERAND_TYPE_ID, {column_component_id}});
}
result_column_ids[i] = message_.fresh_ids(fresh_id_index++);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, SpvOpCompositeConstruct,
ir_context->get_type_mgr()->GetId(resulting_matrix_column_type),
result_column_ids[i], opt::Instruction::OperandList(in_operands)));
}
// The OpTranspose instruction is changed to an OpCompositeConstruct
// instruction.
linear_algebra_instruction->SetOpcode(SpvOpCompositeConstruct);
linear_algebra_instruction->SetInOperand(0, {result_column_ids[0]});
for (uint32_t i = 1; i < result_column_ids.size(); i++) {
linear_algebra_instruction->AddOperand(
{SPV_OPERAND_TYPE_ID, {result_column_ids[i]}});
}
fuzzerutil::UpdateModuleIdBound(
ir_context, message_.fresh_ids(message_.fresh_ids().size() - 1));
}
void TransformationReplaceLinearAlgebraInstruction::ReplaceOpVectorTimesScalar(
opt::IRContext* ir_context,
opt::Instruction* linear_algebra_instruction) const {
@ -740,6 +850,92 @@ void TransformationReplaceLinearAlgebraInstruction::ReplaceOpMatrixTimesMatrix(
ir_context, message_.fresh_ids(message_.fresh_ids().size() - 1));
}
void TransformationReplaceLinearAlgebraInstruction::ReplaceOpOuterProduct(
opt::IRContext* ir_context,
opt::Instruction* linear_algebra_instruction) const {
// Gets vector 1 information.
auto vector_1_instruction = ir_context->get_def_use_mgr()->GetDef(
linear_algebra_instruction->GetSingleWordInOperand(0));
uint32_t vector_1_component_count =
ir_context->get_type_mgr()
->GetType(vector_1_instruction->type_id())
->AsVector()
->element_count();
auto vector_1_component_type = ir_context->get_type_mgr()
->GetType(vector_1_instruction->type_id())
->AsVector()
->element_type();
// Gets vector 2 information.
auto vector_2_instruction = ir_context->get_def_use_mgr()->GetDef(
linear_algebra_instruction->GetSingleWordInOperand(1));
uint32_t vector_2_component_count =
ir_context->get_type_mgr()
->GetType(vector_2_instruction->type_id())
->AsVector()
->element_count();
uint32_t fresh_id_index = 0;
std::vector<uint32_t> result_column_ids(vector_2_component_count);
for (uint32_t i = 0; i < vector_2_component_count; i++) {
// Extracts |vector_2| component.
uint32_t vector_2_component_id = message_.fresh_ids(fresh_id_index++);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, SpvOpCompositeExtract,
ir_context->get_type_mgr()->GetId(vector_1_component_type),
vector_2_component_id,
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {vector_2_instruction->result_id()}},
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
std::vector<uint32_t> column_component_ids(vector_1_component_count);
for (uint32_t j = 0; j < vector_1_component_count; j++) {
// Extracts |vector_1| component.
uint32_t vector_1_component_id = message_.fresh_ids(fresh_id_index++);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, SpvOpCompositeExtract,
ir_context->get_type_mgr()->GetId(vector_1_component_type),
vector_1_component_id,
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {vector_1_instruction->result_id()}},
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {j}}})));
// Multiplies |vector_1| and |vector_2| components.
column_component_ids[j] = message_.fresh_ids(fresh_id_index++);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, SpvOpFMul,
ir_context->get_type_mgr()->GetId(vector_1_component_type),
column_component_ids[j],
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {vector_2_component_id}},
{SPV_OPERAND_TYPE_ID, {vector_1_component_id}}})));
}
// Inserts the resulting matrix column.
opt::Instruction::OperandList in_operands;
for (auto& column_component_id : column_component_ids) {
in_operands.push_back({SPV_OPERAND_TYPE_ID, {column_component_id}});
}
result_column_ids[i] = message_.fresh_ids(fresh_id_index++);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, SpvOpCompositeConstruct, vector_1_instruction->type_id(),
result_column_ids[i], in_operands));
}
// The OpOuterProduct instruction is changed to an OpCompositeConstruct
// instruction.
linear_algebra_instruction->SetOpcode(SpvOpCompositeConstruct);
linear_algebra_instruction->SetInOperand(0, {result_column_ids[0]});
linear_algebra_instruction->SetInOperand(1, {result_column_ids[1]});
for (uint32_t i = 2; i < result_column_ids.size(); i++) {
linear_algebra_instruction->AddOperand(
{SPV_OPERAND_TYPE_ID, {result_column_ids[i]}});
}
fuzzerutil::UpdateModuleIdBound(
ir_context, message_.fresh_ids(message_.fresh_ids().size() - 1));
}
void TransformationReplaceLinearAlgebraInstruction::ReplaceOpDot(
opt::IRContext* ir_context,
opt::Instruction* linear_algebra_instruction) const {

View File

@ -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;

View File

@ -0,0 +1,192 @@
// Copyright (c) 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "transformation_replace_load_store_with_copy_memory.h"
#include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/instruction_descriptor.h"
#include "source/opcode.h"
namespace spvtools {
namespace fuzz {
namespace {
const uint32_t kOpStoreOperandIndexTargetVariable = 0;
const uint32_t kOpStoreOperandIndexIntermediateIdToWrite = 1;
const uint32_t kOpLoadOperandIndexSourceVariable = 2;
} // namespace
TransformationReplaceLoadStoreWithCopyMemory::
TransformationReplaceLoadStoreWithCopyMemory(
const spvtools::fuzz::protobufs::
TransformationReplaceLoadStoreWithCopyMemory& message)
: message_(message) {}
TransformationReplaceLoadStoreWithCopyMemory::
TransformationReplaceLoadStoreWithCopyMemory(
const protobufs::InstructionDescriptor& load_instruction_descriptor,
const protobufs::InstructionDescriptor& store_instruction_descriptor) {
*message_.mutable_load_instruction_descriptor() = load_instruction_descriptor;
*message_.mutable_store_instruction_descriptor() =
store_instruction_descriptor;
}
bool TransformationReplaceLoadStoreWithCopyMemory::IsApplicable(
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
// This transformation is only applicable to the pair of OpLoad and OpStore
// instructions.
if (message_.load_instruction_descriptor().target_instruction_opcode() !=
SpvOpLoad) {
return false;
}
if (message_.store_instruction_descriptor().target_instruction_opcode() !=
SpvOpStore) {
return false;
}
// The OpLoad instruction must be defined.
auto load_instruction =
FindInstruction(message_.load_instruction_descriptor(), ir_context);
if (!load_instruction) {
return false;
}
// The OpStore instruction must be defined.
auto store_instruction =
FindInstruction(message_.store_instruction_descriptor(), ir_context);
if (!store_instruction) {
return false;
}
// Intermediate values of the OpLoad and the OpStore must match.
if (load_instruction->result_id() !=
store_instruction->GetSingleWordOperand(
kOpStoreOperandIndexIntermediateIdToWrite)) {
return false;
}
// Get storage class of the variable pointed by the source operand in OpLoad.
opt::Instruction* source_id = ir_context->get_def_use_mgr()->GetDef(
load_instruction->GetSingleWordOperand(2));
SpvStorageClass storage_class = fuzzerutil::GetStorageClassFromPointerType(
ir_context, source_id->type_id());
// Iterate over all instructions between |load_instruction| and
// |store_instruction|.
for (auto it = load_instruction; it != store_instruction;
it = it->NextNode()) {
//|load_instruction| and |store_instruction| are not in the same block.
if (it == nullptr) {
return false;
}
// We need to make sure that the value pointed to by the source of the
// OpLoad hasn't changed by the time we see the matching OpStore
// instruction.
if (IsMemoryWritingOpCode(it->opcode())) {
return false;
} else if (IsMemoryBarrierOpCode(it->opcode()) &&
!IsStorageClassSafeAcrossMemoryBarriers(storage_class)) {
return false;
}
}
return true;
}
void TransformationReplaceLoadStoreWithCopyMemory::Apply(
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
// OpLoad and OpStore instructions must be defined.
auto load_instruction =
FindInstruction(message_.load_instruction_descriptor(), ir_context);
assert(load_instruction && load_instruction->opcode() == SpvOpLoad &&
"The required OpLoad instruction must be defined.");
auto store_instruction =
FindInstruction(message_.store_instruction_descriptor(), ir_context);
assert(store_instruction && store_instruction->opcode() == SpvOpStore &&
"The required OpStore instruction must be defined.");
// Intermediate values of the OpLoad and the OpStore must match.
assert(load_instruction->result_id() ==
store_instruction->GetSingleWordOperand(
kOpStoreOperandIndexIntermediateIdToWrite) &&
"OpLoad and OpStore must refer to the same value.");
// Get the ids of the source operand of the OpLoad and the target operand of
// the OpStore.
uint32_t source_variable_id =
load_instruction->GetSingleWordOperand(kOpLoadOperandIndexSourceVariable);
uint32_t target_variable_id = store_instruction->GetSingleWordOperand(
kOpStoreOperandIndexTargetVariable);
// Insert the OpCopyMemory instruction before the OpStore instruction.
store_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, SpvOpCopyMemory, 0, 0,
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {target_variable_id}},
{SPV_OPERAND_TYPE_ID, {source_variable_id}}})));
// Remove the OpStore instruction.
ir_context->KillInst(store_instruction);
ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
}
bool TransformationReplaceLoadStoreWithCopyMemory::IsMemoryWritingOpCode(
SpvOp op_code) {
if (spvOpcodeIsAtomicOp(op_code)) {
return op_code != SpvOpAtomicLoad;
}
switch (op_code) {
case SpvOpStore:
case SpvOpCopyMemory:
case SpvOpCopyMemorySized:
return true;
default:
return false;
}
}
bool TransformationReplaceLoadStoreWithCopyMemory::IsMemoryBarrierOpCode(
SpvOp op_code) {
switch (op_code) {
case SpvOpMemoryBarrier:
case SpvOpMemoryNamedBarrier:
return true;
default:
return false;
}
}
bool TransformationReplaceLoadStoreWithCopyMemory::
IsStorageClassSafeAcrossMemoryBarriers(SpvStorageClass storage_class) {
switch (storage_class) {
case SpvStorageClassUniformConstant:
case SpvStorageClassInput:
case SpvStorageClassUniform:
case SpvStorageClassPrivate:
case SpvStorageClassFunction:
return true;
default:
return false;
}
}
protobufs::Transformation
TransformationReplaceLoadStoreWithCopyMemory::ToMessage() const {
protobufs::Transformation result;
*result.mutable_replace_load_store_with_copy_memory() = message_;
return result;
}
} // namespace fuzz
} // namespace spvtools

View File

@ -0,0 +1,76 @@
// Copyright (c) 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SPIRV_TOOLS_TRANSFORMATION_REPLACE_LOAD_STORE_WITH_COPY_MEMORY_H
#define SPIRV_TOOLS_TRANSFORMATION_REPLACE_LOAD_STORE_WITH_COPY_MEMORY_H
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
#include "source/fuzz/transformation.h"
#include "source/fuzz/transformation_context.h"
#include "source/opt/ir_context.h"
namespace spvtools {
namespace fuzz {
class TransformationReplaceLoadStoreWithCopyMemory : public Transformation {
public:
explicit TransformationReplaceLoadStoreWithCopyMemory(
const protobufs::TransformationReplaceLoadStoreWithCopyMemory& message);
TransformationReplaceLoadStoreWithCopyMemory(
const protobufs::InstructionDescriptor& load_instruction_descriptor,
const protobufs::InstructionDescriptor& store_instruction_descriptor);
// - |message_.load_instruction_descriptor| must identify an OpLoad
// instruction.
// - |message_.store_instruction_descriptor| must identify an OpStore
// instruction.
// - The OpStore must write the intermediate value loaded by the OpLoad.
// - The OpLoad and the OpStore must not have certain instruction in between
// (checked by IsMemoryWritingOpCode(), IsMemoryBarrierOpCode(),
// IsStorageClassSafeAcrossMemoryBarriers()).
bool IsApplicable(
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const override;
// Takes a pair of instruction descriptors to OpLoad and OpStore that have the
// same intermediate value and replaces the OpStore with an equivalent
// OpCopyMemory.
void Apply(opt::IRContext* ir_context,
TransformationContext* transformation_context) const override;
// Checks if the instruction that has an |op_code| might write to
// the source operand of the OpLoad instruction.
static bool IsMemoryWritingOpCode(SpvOp op_code);
// Checks if the instruction that has an |op_code| is a memory barrier that
// could interfere with the source operand of the OpLoad instruction
static bool IsMemoryBarrierOpCode(SpvOp op_code);
// Checks if the |storage_class| of the source operand of the OpLoad
// instruction implies that this variable cannot change (due to other threads)
// across memory barriers.
static bool IsStorageClassSafeAcrossMemoryBarriers(
SpvStorageClass storage_class);
protobufs::Transformation ToMessage() const override;
private:
protobufs::TransformationReplaceLoadStoreWithCopyMemory message_;
};
} // namespace fuzz
} // namespace spvtools
#endif // SPIRV_TOOLS_TRANSFORMATION_REPLACE_LOAD_STORE_WITH_COPY_MEMORY_H

View File

@ -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.
{

View File

@ -28,8 +28,7 @@ TransformationReplaceParamsWithStruct::TransformationReplaceParamsWithStruct(
TransformationReplaceParamsWithStruct::TransformationReplaceParamsWithStruct(
const std::vector<uint32_t>& parameter_id, uint32_t fresh_function_type_id,
uint32_t fresh_parameter_id,
const std::unordered_map<uint32_t, uint32_t>&
caller_id_to_fresh_composite_id) {
const std::map<uint32_t, uint32_t>& caller_id_to_fresh_composite_id) {
message_.set_fresh_function_type_id(fresh_function_type_id);
message_.set_fresh_parameter_id(fresh_parameter_id);
@ -37,9 +36,8 @@ TransformationReplaceParamsWithStruct::TransformationReplaceParamsWithStruct(
message_.add_parameter_id(id);
}
message_.mutable_caller_id_to_fresh_composite_id()->insert(
caller_id_to_fresh_composite_id.begin(),
caller_id_to_fresh_composite_id.end());
*message_.mutable_caller_id_to_fresh_composite_id() =
fuzzerutil::MapToRepeatedUInt32Pair(caller_id_to_fresh_composite_id);
}
bool TransformationReplaceParamsWithStruct::IsApplicable(
@ -103,13 +101,15 @@ bool TransformationReplaceParamsWithStruct::IsApplicable(
return false;
}
auto caller_id_to_fresh_composite_id = fuzzerutil::RepeatedUInt32PairToMap(
message_.caller_id_to_fresh_composite_id());
// Check that |callee_id_to_fresh_composite_id| is valid.
for (const auto* inst :
fuzzerutil::GetCallers(ir_context, function->result_id())) {
// Check that the callee is present in the map. It's ok if the map contains
// more ids that there are callees (those ids will not be used).
if (!message_.caller_id_to_fresh_composite_id().contains(
inst->result_id())) {
if (!caller_id_to_fresh_composite_id.count(inst->result_id())) {
return false;
}
}
@ -118,7 +118,7 @@ bool TransformationReplaceParamsWithStruct::IsApplicable(
std::vector<uint32_t> fresh_ids = {message_.fresh_function_type_id(),
message_.fresh_parameter_id()};
for (const auto& entry : message_.caller_id_to_fresh_composite_id()) {
for (const auto& entry : caller_id_to_fresh_composite_id) {
fresh_ids.push_back(entry.second);
}
@ -167,6 +167,9 @@ void TransformationReplaceParamsWithStruct::Apply(
}
}
auto caller_id_to_fresh_composite_id = fuzzerutil::RepeatedUInt32PairToMap(
message_.caller_id_to_fresh_composite_id());
// Update all function calls.
for (auto* inst : fuzzerutil::GetCallers(ir_context, function->result_id())) {
// Create a list of operands for the OpCompositeConstruct instruction.
@ -189,7 +192,7 @@ void TransformationReplaceParamsWithStruct::Apply(
// Insert OpCompositeConstruct before the function call.
auto fresh_composite_id =
message_.caller_id_to_fresh_composite_id().at(inst->result_id());
caller_id_to_fresh_composite_id.at(inst->result_id());
inst->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, SpvOpCompositeConstruct, struct_type_id, fresh_composite_id,
std::move(composite_components)));
@ -227,7 +230,7 @@ void TransformationReplaceParamsWithStruct::Apply(
{SPV_OPERAND_TYPE_ID, {message_.fresh_parameter_id()}},
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {static_cast<uint32_t>(i)}}}));
function->RemoveParameter(param_inst->result_id());
fuzzerutil::RemoveParameter(ir_context, param_inst->result_id());
}
// Update function's type.

View File

@ -15,7 +15,7 @@
#ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMS_WITH_STRUCT_H_
#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMS_WITH_STRUCT_H_
#include <unordered_map>
#include <map>
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
#include "source/fuzz/transformation.h"
@ -33,8 +33,7 @@ class TransformationReplaceParamsWithStruct : public Transformation {
TransformationReplaceParamsWithStruct(
const std::vector<uint32_t>& parameter_id,
uint32_t fresh_function_type_id, uint32_t fresh_parameter_id,
const std::unordered_map<uint32_t, uint32_t>&
caller_id_to_fresh_composite_id);
const std::map<uint32_t, uint32_t>& caller_id_to_fresh_composite_id);
// - Each element of |parameter_id| is a valid result id of some
// OpFunctionParameter instruction. All parameter ids must correspond to

View File

@ -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 |

View File

@ -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()});
});

View File

@ -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
)

View File

@ -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);
}
}

View File

@ -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,

View File

@ -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)

View File

@ -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_) {

View File

@ -396,6 +396,12 @@ uint32_t ConstantManager::GetFloatConst(float val) {
return GetDefiningInstruction(c)->result_id();
}
uint32_t ConstantManager::GetSIntConst(int32_t val) {
Type* sint_type = context()->get_type_mgr()->GetSIntType();
const Constant* c = GetConstant(sint_type, {static_cast<uint32_t>(val)});
return GetDefiningInstruction(c)->result_id();
}
std::vector<const analysis::Constant*> Constant::GetVectorComponents(
analysis::ConstantManager* const_mgr) const {
std::vector<const analysis::Constant*> components;

View File

@ -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

View File

@ -33,10 +33,13 @@ static const uint32_t kDebugDeclareOperandLocalVariableIndex = 4;
static const uint32_t kDebugDeclareOperandVariableIndex = 5;
static const uint32_t kDebugValueOperandLocalVariableIndex = 4;
static const uint32_t kDebugValueOperandExpressionIndex = 6;
static const uint32_t kDebugValueOperandIndexesIndex = 7;
static const uint32_t kDebugOperationOperandOperationIndex = 4;
static const uint32_t kOpVariableOperandStorageClassIndex = 2;
static const uint32_t kDebugLocalVariableOperandParentIndex = 9;
static const uint32_t kDebugOperationOperandOpCodeIndex = 4;
static const uint32_t kExtInstInstructionInIdx = 1;
static const uint32_t kDebugGlobalVariableOperandFlagsIndex = 12;
static const uint32_t kDebugLocalVariableOperandFlagsIndex = 10;
namespace spvtools {
namespace opt {
@ -369,7 +372,7 @@ Instruction* DebugInfoManager::CloneDebugInlinedAt(uint32_t clone_inlined_at_id,
std::move(new_inlined_at));
}
bool DebugInfoManager::IsDebugDeclared(uint32_t variable_id) {
bool DebugInfoManager::IsVariableDebugDeclared(uint32_t variable_id) {
auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id);
return dbg_decl_itr != var_id_to_dbg_decl_.end();
}
@ -446,17 +449,11 @@ bool DebugInfoManager::IsDeclareVisibleToInstr(Instruction* dbg_declare,
return IsAncestorOfScope(instr_scope_id, decl_scope_id);
}
void DebugInfoManager::AddDebugValue(Instruction* scope_and_line,
uint32_t variable_id, uint32_t value_id,
Instruction* insert_pos) {
auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id);
if (dbg_decl_itr == var_id_to_dbg_decl_.end()) return;
uint32_t instr_scope_id = scope_and_line->GetDebugScope().GetLexicalScope();
for (auto* dbg_decl_or_val : dbg_decl_itr->second) {
if (!IsDeclareVisibleToInstr(dbg_decl_or_val, instr_scope_id)) continue;
Instruction* DebugInfoManager::AddDebugValueWithIndex(
uint32_t dbg_local_var_id, uint32_t value_id, uint32_t expr_id,
uint32_t index_id, Instruction* insert_before) {
uint32_t result_id = context()->TakeNextId();
if (!result_id) return nullptr;
std::unique_ptr<Instruction> new_dbg_value(new Instruction(
context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
result_id,
@ -467,25 +464,39 @@ void DebugInfoManager::AddDebugValue(Instruction* scope_and_line,
->GetExtInstImportId_OpenCL100DebugInfo()}},
{spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
{static_cast<uint32_t>(OpenCLDebugInfo100DebugValue)}},
{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
{dbg_decl_or_val->GetSingleWordOperand(
kDebugValueOperandLocalVariableIndex)}},
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {dbg_local_var_id}},
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {value_id}},
{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
{GetEmptyDebugExpression()->result_id()}},
{expr_id == 0 ? GetEmptyDebugExpression()->result_id() : expr_id}},
}));
if (index_id) {
new_dbg_value->AddOperand(
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {index_id}});
}
if (dbg_decl_or_val->NumOperands() >
kDebugValueOperandExpressionIndex + 1) {
assert(dbg_decl_or_val->GetOpenCL100DebugOpcode() ==
OpenCLDebugInfo100DebugValue);
for (uint32_t i = kDebugValueOperandExpressionIndex + 1;
i < dbg_decl_or_val->NumOperands(); ++i) {
new_dbg_value->AddOperand({spv_operand_type_t::SPV_OPERAND_TYPE_ID,
{dbg_decl_or_val->GetSingleWordOperand(i)}});
Instruction* added_dbg_value =
insert_before->InsertBefore(std::move(new_dbg_value));
AnalyzeDebugInst(added_dbg_value);
if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
context()->get_def_use_mgr()->AnalyzeInstDefUse(added_dbg_value);
if (context()->AreAnalysesValid(
IRContext::Analysis::kAnalysisInstrToBlockMapping)) {
auto insert_blk = context()->get_instr_block(insert_before);
context()->set_instr_block(added_dbg_value, insert_blk);
}
return added_dbg_value;
}
void DebugInfoManager::AddDebugValueIfVarDeclIsVisible(
Instruction* scope_and_line, uint32_t variable_id, uint32_t value_id,
Instruction* insert_pos) {
auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id);
if (dbg_decl_itr == var_id_to_dbg_decl_.end()) return;
uint32_t instr_scope_id = scope_and_line->GetDebugScope().GetLexicalScope();
for (auto* dbg_decl_or_val : dbg_decl_itr->second) {
if (!IsDeclareVisibleToInstr(dbg_decl_or_val, instr_scope_id)) continue;
// Avoid inserting the new DebugValue between OpPhi or OpVariable
// instructions.
Instruction* insert_before = insert_pos->NextNode();
@ -494,17 +505,18 @@ void DebugInfoManager::AddDebugValue(Instruction* scope_and_line,
insert_before = insert_before->NextNode();
}
Instruction* added_dbg_value =
insert_before->InsertBefore(std::move(new_dbg_value));
added_dbg_value->UpdateDebugInfo(scope_and_line);
AnalyzeDebugInst(added_dbg_value);
if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
context()->get_def_use_mgr()->AnalyzeInstDefUse(added_dbg_value);
if (context()->AreAnalysesValid(
IRContext::Analysis::kAnalysisInstrToBlockMapping)) {
auto insert_blk = context()->get_instr_block(insert_before);
context()->set_instr_block(added_dbg_value, insert_blk);
uint32_t index_id = 0;
if (dbg_decl_or_val->NumOperands() > kDebugValueOperandIndexesIndex) {
index_id =
dbg_decl_or_val->GetSingleWordOperand(kDebugValueOperandIndexesIndex);
}
Instruction* added_dbg_value =
AddDebugValueWithIndex(dbg_decl_or_val->GetSingleWordOperand(
kDebugValueOperandLocalVariableIndex),
value_id, 0, index_id, insert_before);
assert(added_dbg_value != nullptr);
added_dbg_value->UpdateDebugInfoFrom(scope_and_line);
}
}
@ -542,6 +554,12 @@ uint32_t DebugInfoManager::GetVariableIdOfDebugValueUsedForDeclare(
return 0;
}
bool DebugInfoManager::IsDebugDeclare(Instruction* instr) {
if (!instr->IsOpenCL100DebugInstr()) return false;
return instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare ||
GetVariableIdOfDebugValueUsedForDeclare(instr) != 0;
}
void DebugInfoManager::AnalyzeDebugInst(Instruction* dbg_inst) {
if (!dbg_inst->IsOpenCL100DebugInstr()) return;
@ -556,7 +574,7 @@ void DebugInfoManager::AnalyzeDebugInst(Instruction* dbg_inst) {
if (deref_operation_ == nullptr &&
dbg_inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugOperation &&
dbg_inst->GetSingleWordOperand(kDebugOperationOperandOpCodeIndex) ==
dbg_inst->GetSingleWordOperand(kDebugOperationOperandOperationIndex) ==
OpenCLDebugInfo100Deref) {
deref_operation_ = dbg_inst;
}
@ -581,6 +599,56 @@ void DebugInfoManager::AnalyzeDebugInst(Instruction* dbg_inst) {
}
}
void DebugInfoManager::ConvertDebugGlobalToLocalVariable(
Instruction* dbg_global_var, Instruction* local_var) {
if (dbg_global_var->GetOpenCL100DebugOpcode() !=
OpenCLDebugInfo100DebugGlobalVariable) {
return;
}
assert(local_var->opcode() == SpvOpVariable ||
local_var->opcode() == SpvOpFunctionParameter);
// Convert |dbg_global_var| to DebugLocalVariable
dbg_global_var->SetInOperand(kExtInstInstructionInIdx,
{OpenCLDebugInfo100DebugLocalVariable});
auto flags = dbg_global_var->GetSingleWordOperand(
kDebugGlobalVariableOperandFlagsIndex);
for (uint32_t i = dbg_global_var->NumInOperands() - 1;
i >= kDebugLocalVariableOperandFlagsIndex; --i) {
dbg_global_var->RemoveOperand(i);
}
dbg_global_var->SetOperand(kDebugLocalVariableOperandFlagsIndex, {flags});
context()->ForgetUses(dbg_global_var);
context()->AnalyzeUses(dbg_global_var);
// Create a DebugDeclare
std::unique_ptr<Instruction> new_dbg_decl(new Instruction(
context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
context()->TakeNextId(),
{
{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
{context()
->get_feature_mgr()
->GetExtInstImportId_OpenCL100DebugInfo()}},
{spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
{static_cast<uint32_t>(OpenCLDebugInfo100DebugDeclare)}},
{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
{dbg_global_var->result_id()}},
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {local_var->result_id()}},
{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
{GetEmptyDebugExpression()->result_id()}},
}));
auto* added_dbg_decl =
local_var->NextNode()->InsertBefore(std::move(new_dbg_decl));
if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
context()->get_def_use_mgr()->AnalyzeInstDefUse(added_dbg_decl);
if (context()->AreAnalysesValid(
IRContext::Analysis::kAnalysisInstrToBlockMapping)) {
auto insert_blk = context()->get_instr_block(local_var);
context()->set_instr_block(added_dbg_decl, insert_blk);
}
}
void DebugInfoManager::AnalyzeDebugInsts(Module& module) {
deref_operation_ = nullptr;
debug_info_none_inst_ = nullptr;
@ -638,7 +706,8 @@ void DebugInfoManager::ClearDebugInfo(Instruction* instr) {
dbg_instr_itr->GetOpenCL100DebugOpcode() ==
OpenCLDebugInfo100DebugOperation &&
dbg_instr_itr->GetSingleWordOperand(
kDebugOperationOperandOpCodeIndex) == OpenCLDebugInfo100Deref) {
kDebugOperationOperandOperationIndex) ==
OpenCLDebugInfo100Deref) {
deref_operation_ = &*dbg_instr_itr;
break;
}

View File

@ -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_; }

View File

@ -22,8 +22,8 @@
// Calculates the dominator or postdominator tree for a given function.
// 1 - Compute the successors and predecessors for each BasicBlock. We add a
// dummy node for the start node or for postdominators the exit. This node will
// point to all entry or all exit nodes.
// placeholder node for the start node or for postdominators the exit. This node
// will point to all entry or all exit nodes.
// 2 - Using the CFA::DepthFirstTraversal get a depth first postordered list of
// all BasicBlocks. Using the successors (or for postdominator, predecessors)
// calculated in step 1 to traverse the tree.
@ -107,8 +107,9 @@ class BasicBlockSuccessorHelper {
public:
// For compliance with the dominance tree computation, entry nodes are
// connected to a single dummy node.
BasicBlockSuccessorHelper(Function& func, const BasicBlock* dummy_start_node,
// connected to a single placeholder node.
BasicBlockSuccessorHelper(Function& func,
const BasicBlock* placeholder_start_node,
bool post);
// CFA::CalculateDominators requires std::vector<BasicBlock*>.
@ -139,23 +140,24 @@ class BasicBlockSuccessorHelper {
// Build the successors and predecessors map for each basic blocks |f|.
// If |invert_graph_| is true, all edges are reversed (successors becomes
// predecessors and vice versa).
// For convenience, the start of the graph is |dummy_start_node|.
// For convenience, the start of the graph is |placeholder_start_node|.
// The dominator tree construction requires a unique entry node, which cannot
// be guaranteed for the postdominator graph. The |dummy_start_node| BB is
// here to gather all entry nodes.
void CreateSuccessorMap(Function& f, const BasicBlock* dummy_start_node);
// be guaranteed for the postdominator graph. The |placeholder_start_node| BB
// is here to gather all entry nodes.
void CreateSuccessorMap(Function& f,
const BasicBlock* placeholder_start_node);
};
template <typename BBType>
BasicBlockSuccessorHelper<BBType>::BasicBlockSuccessorHelper(
Function& func, const BasicBlock* dummy_start_node, bool invert)
Function& func, const BasicBlock* placeholder_start_node, bool invert)
: invert_graph_(invert) {
CreateSuccessorMap(func, dummy_start_node);
CreateSuccessorMap(func, placeholder_start_node);
}
template <typename BBType>
void BasicBlockSuccessorHelper<BBType>::CreateSuccessorMap(
Function& f, const BasicBlock* dummy_start_node) {
Function& f, const BasicBlock* placeholder_start_node) {
std::map<uint32_t, BasicBlock*> id_to_BB_map;
auto GetSuccessorBasicBlock = [&f, &id_to_BB_map](uint32_t successor_id) {
BasicBlock*& Succ = id_to_BB_map[successor_id];
@ -173,11 +175,10 @@ void BasicBlockSuccessorHelper<BBType>::CreateSuccessorMap(
if (invert_graph_) {
// For the post dominator tree, we see the inverted graph.
// successors_ in the inverted graph are the predecessors in the CFG.
// The tree construction requires 1 entry point, so we add a dummy node
// that is connected to all function exiting basic blocks.
// An exiting basic block is a block with an OpKill, OpUnreachable,
// OpReturn, OpReturnValue, or OpTerminateInvocation as terminator
// instruction.
// The tree construction requires 1 entry point, so we add a placeholder
// node that is connected to all function exiting basic blocks. An exiting
// basic block is a block with an OpKill, OpUnreachable, OpReturn,
// OpReturnValue, or OpTerminateInvocation as terminator instruction.
for (BasicBlock& bb : f) {
if (bb.hasSuccessor()) {
BasicBlockListTy& pred_list = predecessors_[&bb];
@ -192,14 +193,15 @@ void BasicBlockSuccessorHelper<BBType>::CreateSuccessorMap(
pred_list.push_back(succ);
});
} else {
successors_[dummy_start_node].push_back(&bb);
predecessors_[&bb].push_back(const_cast<BasicBlock*>(dummy_start_node));
successors_[placeholder_start_node].push_back(&bb);
predecessors_[&bb].push_back(
const_cast<BasicBlock*>(placeholder_start_node));
}
}
} else {
successors_[dummy_start_node].push_back(f.entry().get());
successors_[placeholder_start_node].push_back(f.entry().get());
predecessors_[f.entry().get()].push_back(
const_cast<BasicBlock*>(dummy_start_node));
const_cast<BasicBlock*>(placeholder_start_node));
for (BasicBlock& bb : f) {
BasicBlockListTy& succ_list = successors_[&bb];
@ -288,7 +290,7 @@ DominatorTreeNode* DominatorTree::GetOrInsertNode(BasicBlock* bb) {
}
void DominatorTree::GetDominatorEdges(
const Function* f, const BasicBlock* dummy_start_node,
const Function* f, const BasicBlock* placeholder_start_node,
std::vector<std::pair<BasicBlock*, BasicBlock*>>* edges) {
// Each time the depth first traversal calls the postorder callback
// std::function we push that node into the postorder vector to create our
@ -302,7 +304,7 @@ void DominatorTree::GetDominatorEdges(
// BB are derived from F, so we need to const cast it at some point
// no modification is made on F.
BasicBlockSuccessorHelper<BasicBlock> helper{
*const_cast<Function*>(f), dummy_start_node, postdominator_};
*const_cast<Function*>(f), placeholder_start_node, postdominator_};
// The successor function tells DepthFirstTraversal how to move to successive
// nodes by providing an interface to get a list of successor nodes from any
@ -316,7 +318,7 @@ void DominatorTree::GetDominatorEdges(
// If we're building a post dominator tree we traverse the tree in reverse
// using the predecessor function in place of the successor function and vice
// versa.
DepthFirstSearchPostOrder(dummy_start_node, successor_functor,
DepthFirstSearchPostOrder(placeholder_start_node, successor_functor,
postorder_function);
*edges = CFA<BasicBlock>::CalculateDominators(postorder, predecessor_functor);
}
@ -329,12 +331,12 @@ void DominatorTree::InitializeTree(const CFG& cfg, const Function* f) {
return;
}
const BasicBlock* dummy_start_node =
const BasicBlock* placeholder_start_node =
postdominator_ ? cfg.pseudo_exit_block() : cfg.pseudo_entry_block();
// Get the immediate dominator for each node.
std::vector<std::pair<BasicBlock*, BasicBlock*>> edges;
GetDominatorEdges(f, dummy_start_node, &edges);
GetDominatorEdges(f, placeholder_start_node, &edges);
// Transform the vector<pair> into the tree structure which we can use to
// efficiently query dominance.
@ -380,7 +382,7 @@ void DominatorTree::DumpTreeAsDot(std::ostream& out_stream) const {
}
// Print the arrow from the parent to this node. Entry nodes will not have
// parents so draw them as children from the dummy node.
// parents so draw them as children from the placeholder node.
if (node->parent_) {
out_stream << node->parent_->bb_->id() << " -> " << node->bb_->id()
<< ";\n";

View File

@ -246,6 +246,11 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
// Clear line-related debug instructions attached to this instruction.
void clear_dbg_line_insts() { dbg_line_insts_.clear(); }
// Set line-related debug instructions.
void set_dbg_line_insts(const std::vector<Instruction>& lines) {
dbg_line_insts_ = lines;
}
// Same semantics as in the base class except the list the InstructionList
// containing |pos| will now assume ownership of |this|.
// inline void MoveBefore(Instruction* pos);
@ -301,7 +306,7 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
return dbg_scope_.GetInlinedAt();
}
// Updates OpLine and DebugScope based on the information of |from|.
inline void UpdateDebugInfo(const Instruction* from);
inline void UpdateDebugInfoFrom(const Instruction* from);
// Remove the |index|-th operand
void RemoveOperand(uint32_t index) {
operands_.erase(operands_.begin() + index);
@ -667,7 +672,7 @@ inline void Instruction::UpdateDebugInlinedAt(uint32_t new_inlined_at) {
}
}
inline void Instruction::UpdateDebugInfo(const Instruction* from) {
inline void Instruction::UpdateDebugInfoFrom(const Instruction* from) {
if (from == nullptr) return;
clear_dbg_line_insts();
if (!from->dbg_line_insts().empty())

View File

@ -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_)

View File

@ -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; });

View File

@ -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; }

View File

@ -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;
}

View File

@ -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);

View File

@ -511,7 +511,7 @@ void Loop::ComputeLoopStructuredOrder(
}
LoopDescriptor::LoopDescriptor(IRContext* context, const Function* f)
: loops_(), dummy_top_loop_(nullptr) {
: loops_(), placeholder_top_loop_(nullptr) {
PopulateList(context, f);
}
@ -592,7 +592,7 @@ void LoopDescriptor::PopulateList(IRContext* context, const Function* f) {
}
}
for (Loop* loop : loops_) {
if (!loop->HasParent()) dummy_top_loop_.nested_loops_.push_back(loop);
if (!loop->HasParent()) placeholder_top_loop_.nested_loops_.push_back(loop);
}
}
@ -986,7 +986,7 @@ void LoopDescriptor::ClearLoops() {
// Adds a new loop nest to the descriptor set.
Loop* LoopDescriptor::AddLoopNest(std::unique_ptr<Loop> new_loop) {
Loop* loop = new_loop.release();
if (!loop->HasParent()) dummy_top_loop_.nested_loops_.push_back(loop);
if (!loop->HasParent()) placeholder_top_loop_.nested_loops_.push_back(loop);
// Iterate from inner to outer most loop, adding basic block to loop mapping
// as we go.
for (Loop& current_loop :
@ -1000,7 +1000,7 @@ Loop* LoopDescriptor::AddLoopNest(std::unique_ptr<Loop> new_loop) {
}
void LoopDescriptor::RemoveLoop(Loop* loop) {
Loop* parent = loop->GetParent() ? loop->GetParent() : &dummy_top_loop_;
Loop* parent = loop->GetParent() ? loop->GetParent() : &placeholder_top_loop_;
parent->nested_loops_.erase(std::find(parent->nested_loops_.begin(),
parent->nested_loops_.end(), loop));
std::for_each(

View File

@ -406,8 +406,8 @@ class Loop {
// the iterators.
bool loop_is_marked_for_removal_;
// This is only to allow LoopDescriptor::dummy_top_loop_ to add top level
// loops as child.
// This is only to allow LoopDescriptor::placeholder_top_loop_ to add top
// level loops as child.
friend class LoopDescriptor;
friend class LoopUtils;
};
@ -430,14 +430,14 @@ class LoopDescriptor {
// Disable copy constructor, to avoid double-free on destruction.
LoopDescriptor(const LoopDescriptor&) = delete;
// Move constructor.
LoopDescriptor(LoopDescriptor&& other) : dummy_top_loop_(nullptr) {
LoopDescriptor(LoopDescriptor&& other) : placeholder_top_loop_(nullptr) {
// We need to take ownership of the Loop objects in the other
// LoopDescriptor, to avoid double-free.
loops_ = std::move(other.loops_);
other.loops_.clear();
basic_block_to_loop_ = std::move(other.basic_block_to_loop_);
other.basic_block_to_loop_.clear();
dummy_top_loop_ = std::move(other.dummy_top_loop_);
placeholder_top_loop_ = std::move(other.placeholder_top_loop_);
}
// Destructor
@ -470,25 +470,27 @@ class LoopDescriptor {
// Iterators for post order depth first traversal of the loops.
// Inner most loops will be visited first.
inline iterator begin() { return iterator::begin(&dummy_top_loop_); }
inline iterator end() { return iterator::end(&dummy_top_loop_); }
inline iterator begin() { return iterator::begin(&placeholder_top_loop_); }
inline iterator end() { return iterator::end(&placeholder_top_loop_); }
inline const_iterator begin() const { return cbegin(); }
inline const_iterator end() const { return cend(); }
inline const_iterator cbegin() const {
return const_iterator::begin(&dummy_top_loop_);
return const_iterator::begin(&placeholder_top_loop_);
}
inline const_iterator cend() const {
return const_iterator::end(&dummy_top_loop_);
return const_iterator::end(&placeholder_top_loop_);
}
// Iterators for pre-order depth first traversal of the loops.
// Inner most loops will be visited first.
inline pre_iterator pre_begin() { return ++pre_iterator(&dummy_top_loop_); }
inline pre_iterator pre_begin() {
return ++pre_iterator(&placeholder_top_loop_);
}
inline pre_iterator pre_end() { return pre_iterator(); }
inline const_pre_iterator pre_begin() const { return pre_cbegin(); }
inline const_pre_iterator pre_end() const { return pre_cend(); }
inline const_pre_iterator pre_cbegin() const {
return ++const_pre_iterator(&dummy_top_loop_);
return ++const_pre_iterator(&placeholder_top_loop_);
}
inline const_pre_iterator pre_cend() const { return const_pre_iterator(); }
@ -524,14 +526,14 @@ class LoopDescriptor {
void RemoveLoop(Loop* loop);
void SetAsTopLoop(Loop* loop) {
assert(std::find(dummy_top_loop_.begin(), dummy_top_loop_.end(), loop) ==
dummy_top_loop_.end() &&
assert(std::find(placeholder_top_loop_.begin(), placeholder_top_loop_.end(),
loop) == placeholder_top_loop_.end() &&
"already registered");
dummy_top_loop_.nested_loops_.push_back(loop);
placeholder_top_loop_.nested_loops_.push_back(loop);
}
Loop* GetDummyRootLoop() { return &dummy_top_loop_; }
const Loop* GetDummyRootLoop() const { return &dummy_top_loop_; }
Loop* GetPlaceholderRootLoop() { return &placeholder_top_loop_; }
const Loop* GetPlaceholderRootLoop() const { return &placeholder_top_loop_; }
private:
// TODO(dneto): This should be a vector of unique_ptr. But VisualStudio 2013
@ -558,8 +560,8 @@ class LoopDescriptor {
// objects.
LoopContainerType loops_;
// Dummy root: this "loop" is only there to help iterators creation.
Loop dummy_top_loop_;
// Placeholder root: this "loop" is only there to help iterators creation.
Loop placeholder_top_loop_;
std::unordered_map<uint32_t, Loop*> basic_block_to_loop_;

View File

@ -1063,7 +1063,7 @@ LoopPeelingPass::LoopPeelingInfo::HandleInequality(CmpOperator cmp_op,
}
uint32_t cast_iteration = 0;
// sanity check: can we fit |iteration| in a uint32_t ?
// coherence check: can we fit |iteration| in a uint32_t ?
if (static_cast<uint64_t>(iteration) < std::numeric_limits<uint32_t>::max()) {
cast_iteration = static_cast<uint32_t>(iteration);
}

View File

@ -286,6 +286,9 @@ class LoopUnrollerUtilsImpl {
// to be the actual value of the phi at that point.
void LinkLastPhisToStart(Loop* loop) const;
// Kill all debug declaration instructions from |bb|.
void KillDebugDeclares(BasicBlock* bb);
// A pointer to the IRContext. Used to add/remove instructions and for usedef
// chains.
IRContext* context_;
@ -598,6 +601,20 @@ void LoopUnrollerUtilsImpl::FullyUnroll(Loop* loop) {
IRContext::Analysis::kAnalysisDefUse);
}
void LoopUnrollerUtilsImpl::KillDebugDeclares(BasicBlock* bb) {
// We cannot kill an instruction inside BasicBlock::ForEachInst()
// because it will generate dangling pointers. We use |to_be_killed|
// to kill them after the loop.
std::vector<Instruction*> to_be_killed;
bb->ForEachInst([&to_be_killed, this](Instruction* inst) {
if (context_->get_debug_info_mgr()->IsDebugDeclare(inst)) {
to_be_killed.push_back(inst);
}
});
for (auto* inst : to_be_killed) context_->KillInst(inst);
}
// Copy a given basic block, give it a new result_id, and store the new block
// and the id mapping in the state. |preserve_instructions| is used to determine
// whether or not this function should edit instructions other than the
@ -608,6 +625,9 @@ void LoopUnrollerUtilsImpl::CopyBasicBlock(Loop* loop, const BasicBlock* itr,
BasicBlock* basic_block = itr->Clone(context_);
basic_block->SetParent(itr->GetParent());
// We do not want to duplicate DebugDeclare.
KillDebugDeclares(basic_block);
// Assign each result a new unique ID and keep a mapping of the old ids to
// the new ones.
AssignNewResultIds(basic_block);
@ -674,21 +694,21 @@ void LoopUnrollerUtilsImpl::CopyBody(Loop* loop, bool eliminate_conditions) {
std::vector<Instruction*> inductions;
loop->GetInductionVariables(inductions);
for (size_t index = 0; index < inductions.size(); ++index) {
Instruction* master_copy = inductions[index];
Instruction* primary_copy = inductions[index];
assert(master_copy->result_id() != 0);
assert(primary_copy->result_id() != 0);
Instruction* induction_clone =
state_.ids_to_new_inst[state_.new_inst[master_copy->result_id()]];
state_.ids_to_new_inst[state_.new_inst[primary_copy->result_id()]];
state_.new_phis_.push_back(induction_clone);
assert(induction_clone->result_id() != 0);
if (!state_.previous_phis_.empty()) {
state_.new_inst[master_copy->result_id()] = GetPhiDefID(
state_.new_inst[primary_copy->result_id()] = GetPhiDefID(
state_.previous_phis_[index], state_.previous_latch_block_->id());
} else {
// Do not replace the first phi block ids.
state_.new_inst[master_copy->result_id()] = master_copy->result_id();
state_.new_inst[primary_copy->result_id()] = primary_copy->result_id();
}
}
@ -729,13 +749,19 @@ void LoopUnrollerUtilsImpl::FoldConditionBlock(BasicBlock* condition_block,
Instruction& old_branch = *condition_block->tail();
uint32_t new_target = old_branch.GetSingleWordOperand(operand_label);
DebugScope scope = old_branch.GetDebugScope();
const std::vector<Instruction> lines = old_branch.dbg_line_insts();
context_->KillInst(&old_branch);
// Add the new unconditional branch to the merge block.
InstructionBuilder builder(
context_, condition_block,
IRContext::Analysis::kAnalysisDefUse |
IRContext::Analysis::kAnalysisInstrToBlockMapping);
builder.AddBranch(new_target);
Instruction* new_branch = builder.AddBranch(new_target);
new_branch->set_dbg_line_insts(lines);
new_branch->SetDebugScope(scope);
}
void LoopUnrollerUtilsImpl::CloseUnrolledLoop(Loop* loop) {

View File

@ -594,8 +594,8 @@ bool LoopUnswitchPass::ProcessFunction(Function* f) {
bool loop_changed = true;
while (loop_changed) {
loop_changed = false;
for (Loop& loop :
make_range(++TreeDFIterator<Loop>(loop_descriptor.GetDummyRootLoop()),
for (Loop& loop : make_range(
++TreeDFIterator<Loop>(loop_descriptor.GetPlaceholderRootLoop()),
TreeDFIterator<Loop>())) {
if (processed_loop.count(&loop)) continue;
processed_loop.insert(&loop);

View File

@ -111,7 +111,7 @@ bool MergeReturnPass::ProcessStructured(
}
RecordImmediateDominators(function);
AddDummySwitchAroundFunction();
AddSingleCaseSwitchAroundFunction();
std::list<BasicBlock*> order;
cfg()->ComputeStructuredOrder(function, &*function->begin(), &order);
@ -223,7 +223,8 @@ void MergeReturnPass::ProcessStructuredBlock(BasicBlock* block) {
if (tail_opcode == SpvOpReturn || tail_opcode == SpvOpReturnValue ||
tail_opcode == SpvOpUnreachable) {
assert(CurrentState().InBreakable() && "Should be in the dummy construct.");
assert(CurrentState().InBreakable() &&
"Should be in the placeholder construct.");
BranchToBlock(block, CurrentState().BreakMergeId());
return_blocks_.insert(block->id());
}
@ -408,7 +409,7 @@ bool MergeReturnPass::PredicateBlocks(
if (!predicated->insert(block).second) break;
// Skip structured subgraphs.
assert(state->InBreakable() &&
"Should be in the dummy construct at the very least.");
"Should be in the placeholder construct at the very least.");
Instruction* break_merge_inst = state->BreakMergeInst();
uint32_t merge_block_id = break_merge_inst->GetSingleWordInOperand(0);
while (state->BreakMergeId() == merge_block_id) {
@ -768,7 +769,7 @@ void MergeReturnPass::InsertAfterElement(BasicBlock* element,
list->insert(pos, new_element);
}
void MergeReturnPass::AddDummySwitchAroundFunction() {
void MergeReturnPass::AddSingleCaseSwitchAroundFunction() {
CreateReturnBlock();
CreateReturn(final_return_block_);
@ -776,7 +777,7 @@ void MergeReturnPass::AddDummySwitchAroundFunction() {
cfg()->RegisterBlock(final_return_block_);
}
CreateDummySwitch(final_return_block_);
CreateSingleCaseSwitch(final_return_block_);
}
BasicBlock* MergeReturnPass::CreateContinueTarget(uint32_t header_label_id) {
@ -811,7 +812,7 @@ BasicBlock* MergeReturnPass::CreateContinueTarget(uint32_t header_label_id) {
return new_block;
}
void MergeReturnPass::CreateDummySwitch(BasicBlock* merge_target) {
void MergeReturnPass::CreateSingleCaseSwitch(BasicBlock* merge_target) {
// Insert the switch before any code is run. We have to split the entry
// block to make sure the OpVariable instructions remain in the entry block.
BasicBlock* start_block = &*function_->begin();

View File

@ -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

View File

@ -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()) {

View File

@ -138,7 +138,7 @@ bool PrivateToLocalPass::MoveVariable(Instruction* variable,
function->begin()->begin()->InsertBefore(move(var));
// Update uses where the type may have changed.
return UpdateUses(variable->result_id());
return UpdateUses(variable);
}
uint32_t PrivateToLocalPass::GetNewType(uint32_t old_type_id) {
@ -157,6 +157,10 @@ uint32_t PrivateToLocalPass::GetNewType(uint32_t old_type_id) {
bool PrivateToLocalPass::IsValidUse(const Instruction* inst) const {
// The cases in this switch have to match the cases in |UpdateUse|.
// If we don't know how to update it, it is not valid.
if (inst->GetOpenCL100DebugOpcode() ==
OpenCLDebugInfo100DebugGlobalVariable) {
return true;
}
switch (inst->opcode()) {
case SpvOpLoad:
case SpvOpStore:
@ -175,10 +179,16 @@ bool PrivateToLocalPass::IsValidUse(const Instruction* inst) const {
}
}
bool PrivateToLocalPass::UpdateUse(Instruction* inst) {
bool PrivateToLocalPass::UpdateUse(Instruction* inst, Instruction* user) {
// The cases in this switch have to match the cases in |IsValidUse|. If we
// don't think it is valid, the optimization will not view the variable as a
// candidate, and therefore the use will not be updated.
if (inst->GetOpenCL100DebugOpcode() ==
OpenCLDebugInfo100DebugGlobalVariable) {
context()->get_debug_info_mgr()->ConvertDebugGlobalToLocalVariable(inst,
user);
return true;
}
switch (inst->opcode()) {
case SpvOpLoad:
case SpvOpStore:
@ -196,7 +206,7 @@ bool PrivateToLocalPass::UpdateUse(Instruction* inst) {
context()->AnalyzeUses(inst);
// Update uses where the type may have changed.
if (!UpdateUses(inst->result_id())) {
if (!UpdateUses(inst)) {
return false;
}
} break;
@ -211,13 +221,14 @@ bool PrivateToLocalPass::UpdateUse(Instruction* inst) {
return true;
}
bool PrivateToLocalPass::UpdateUses(uint32_t id) {
bool PrivateToLocalPass::UpdateUses(Instruction* inst) {
uint32_t id = inst->result_id();
std::vector<Instruction*> uses;
context()->get_def_use_mgr()->ForEachUser(
id, [&uses](Instruction* use) { uses.push_back(use); });
for (Instruction* use : uses) {
if (!UpdateUse(use)) {
if (!UpdateUse(use, inst)) {
return false;
}
}

View File

@ -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

View File

@ -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);
}
}

View File

@ -25,6 +25,10 @@
#include "source/opt/types.h"
#include "source/util/make_unique.h"
static const uint32_t kDebugDeclareOperandLocalVariableIndex = 4;
static const uint32_t kDebugValueOperandValueIndex = 5;
static const uint32_t kDebugValueOperandExpressionIndex = 6;
namespace spvtools {
namespace opt {
@ -80,6 +84,20 @@ Pass::Status ScalarReplacementPass::ReplaceVariable(
std::vector<Instruction*> dead;
bool replaced_all_uses = get_def_use_mgr()->WhileEachUser(
inst, [this, &replacements, &dead](Instruction* user) {
if (user->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare) {
if (ReplaceWholeDebugDeclare(user, replacements)) {
dead.push_back(user);
return true;
}
return false;
}
if (user->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugValue) {
if (ReplaceWholeDebugValue(user, replacements)) {
dead.push_back(user);
return true;
}
return false;
}
if (!IsAnnotationInst(user->opcode())) {
switch (user->opcode()) {
case SpvOpLoad:
@ -144,6 +162,58 @@ Pass::Status ScalarReplacementPass::ReplaceVariable(
return Status::SuccessWithChange;
}
bool ScalarReplacementPass::ReplaceWholeDebugDeclare(
Instruction* dbg_decl, const std::vector<Instruction*>& replacements) {
// Insert Deref operation to the front of the operation list of |dbg_decl|.
Instruction* dbg_expr = context()->get_def_use_mgr()->GetDef(
dbg_decl->GetSingleWordOperand(kDebugValueOperandExpressionIndex));
auto* deref_expr =
context()->get_debug_info_mgr()->DerefDebugExpression(dbg_expr);
// Add DebugValue instruction with Indexes operand and Deref operation.
int32_t idx = 0;
for (const auto* var : replacements) {
uint32_t dbg_local_variable =
dbg_decl->GetSingleWordOperand(kDebugDeclareOperandLocalVariableIndex);
uint32_t index_id = context()->get_constant_mgr()->GetSIntConst(idx);
Instruction* added_dbg_value =
context()->get_debug_info_mgr()->AddDebugValueWithIndex(
dbg_local_variable,
/*value_id=*/var->result_id(), /*expr_id=*/deref_expr->result_id(),
index_id, /*insert_before=*/var->NextNode());
if (added_dbg_value == nullptr) return false;
added_dbg_value->UpdateDebugInfoFrom(dbg_decl);
++idx;
}
return true;
}
bool ScalarReplacementPass::ReplaceWholeDebugValue(
Instruction* dbg_value, const std::vector<Instruction*>& replacements) {
int32_t idx = 0;
BasicBlock* block = context()->get_instr_block(dbg_value);
for (auto var : replacements) {
// Clone the DebugValue.
std::unique_ptr<Instruction> new_dbg_value(dbg_value->Clone(context()));
uint32_t new_id = TakeNextId();
if (new_id == 0) return false;
new_dbg_value->SetResultId(new_id);
// Update 'Value' operand to the |replacements|.
new_dbg_value->SetOperand(kDebugValueOperandValueIndex, {var->result_id()});
// Append 'Indexes' operand.
new_dbg_value->AddOperand(
{SPV_OPERAND_TYPE_ID,
{context()->get_constant_mgr()->GetSIntConst(idx)}});
// Insert the new DebugValue to the basic block.
auto* added_instr = dbg_value->InsertBefore(std::move(new_dbg_value));
get_def_use_mgr()->AnalyzeInstDefUse(added_instr);
context()->set_instr_block(added_instr, block);
++idx;
}
return true;
}
bool ScalarReplacementPass::ReplaceWholeLoad(
Instruction* load, const std::vector<Instruction*>& replacements) {
// Replaces the load of the entire composite with a load from each replacement
@ -177,6 +247,7 @@ bool ScalarReplacementPass::ReplaceWholeLoad(
where = where.InsertBefore(std::move(newLoad));
get_def_use_mgr()->AnalyzeInstDefUse(&*where);
context()->set_instr_block(&*where, block);
where->UpdateDebugInfoFrom(load);
loads.push_back(&*where);
}
@ -195,6 +266,7 @@ bool ScalarReplacementPass::ReplaceWholeLoad(
}
where = where.InsertBefore(std::move(compositeConstruct));
get_def_use_mgr()->AnalyzeInstDefUse(&*where);
where->UpdateDebugInfoFrom(load);
context()->set_instr_block(&*where, block);
context()->ReplaceAllUsesWith(load->result_id(), compositeId);
return true;
@ -226,6 +298,7 @@ bool ScalarReplacementPass::ReplaceWholeStore(
{SPV_OPERAND_TYPE_ID, {storeInput}},
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {elementIndex++}}}));
auto iter = where.InsertBefore(std::move(extract));
iter->UpdateDebugInfoFrom(store);
get_def_use_mgr()->AnalyzeInstDefUse(&*iter);
context()->set_instr_block(&*iter, block);
@ -242,6 +315,7 @@ bool ScalarReplacementPass::ReplaceWholeStore(
newStore->AddOperand(std::move(copy));
}
iter = where.InsertBefore(std::move(newStore));
iter->UpdateDebugInfoFrom(store);
get_def_use_mgr()->AnalyzeInstDefUse(&*iter);
context()->set_instr_block(&*iter, block);
}
@ -281,6 +355,7 @@ bool ScalarReplacementPass::ReplaceAccessChain(
Operand copy(chain->GetInOperand(i));
replacementChain->AddOperand(std::move(copy));
}
replacementChain->UpdateDebugInfoFrom(chain);
auto iter = chainIter.InsertBefore(std::move(replacementChain));
get_def_use_mgr()->AnalyzeInstDefUse(&*iter);
context()->set_instr_block(&*iter, context()->get_instr_block(chain));
@ -427,6 +502,9 @@ void ScalarReplacementPass::CreateVariable(
}
}
// Update the OpenCL.DebugInfo.100 debug information.
inst->UpdateDebugInfoFrom(varInst);
replacements->push_back(inst);
}
@ -711,6 +789,14 @@ bool ScalarReplacementPass::CheckUses(const Instruction* inst,
get_def_use_mgr()->ForEachUse(inst, [this, max_legal_index, stats, &ok](
const Instruction* user,
uint32_t index) {
if (user->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare ||
user->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugValue) {
// TODO: include num_partial_accesses if it uses Fragment operation or
// DebugValue has Indexes operand.
stats->num_full_accesses++;
return;
}
// Annotations are check as a group separately.
if (!IsAnnotationInst(user->opcode())) {
switch (user->opcode()) {

View File

@ -199,6 +199,21 @@ class ScalarReplacementPass : public Pass {
bool ReplaceWholeStore(Instruction* store,
const std::vector<Instruction*>& replacements);
// Replaces the DebugDeclare to the entire composite.
//
// Generates a DebugValue with Deref operation for each element in the
// scalarized variable from the original DebugDeclare. Returns true if
// successful.
bool ReplaceWholeDebugDeclare(Instruction* dbg_decl,
const std::vector<Instruction*>& replacements);
// Replaces the DebugValue to the entire composite.
//
// Generates a DebugValue for each element in the scalarized variable from
// the original DebugValue. Returns true if successful.
bool ReplaceWholeDebugValue(Instruction* dbg_value,
const std::vector<Instruction*>& replacements);
// Replaces an access chain to the composite variable with either a direct use
// of the appropriate replacement variable or another access chain with the
// replacement variable as the base and one fewer indexes. Returns true if

View File

@ -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;
}

View File

@ -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);

View File

@ -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")

View File

@ -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.

View File

@ -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:

View File

@ -14,8 +14,6 @@
// Validates OpCapability instruction.
#include "source/val/validate.h"
#include <cassert>
#include <string>
#include <unordered_set>
@ -23,6 +21,7 @@
#include "source/diagnostic.h"
#include "source/opcode.h"
#include "source/val/instruction.h"
#include "source/val/validate.h"
#include "source/val/validation_state.h"
namespace spvtools {
@ -166,7 +165,6 @@ bool IsSupportGuaranteedOpenCL_1_2(uint32_t capability, bool embedded_profile) {
switch (capability) {
case SpvCapabilityAddresses:
case SpvCapabilityFloat16Buffer:
case SpvCapabilityGroups:
case SpvCapabilityInt16:
case SpvCapabilityInt8:
case SpvCapabilityKernel:
@ -175,8 +173,6 @@ bool IsSupportGuaranteedOpenCL_1_2(uint32_t capability, bool embedded_profile) {
return true;
case SpvCapabilityInt64:
return !embedded_profile;
case SpvCapabilityPipes:
return embedded_profile;
}
return false;
}
@ -187,6 +183,7 @@ bool IsSupportGuaranteedOpenCL_2_0(uint32_t capability, bool embedded_profile) {
switch (capability) {
case SpvCapabilityDeviceEnqueue:
case SpvCapabilityGenericPointer:
case SpvCapabilityGroups:
case SpvCapabilityPipes:
return true;
}

View File

@ -13,11 +13,13 @@
// limitations under the License.
// Validates correctness of extension SPIR-V instructions.
#include <cstdlib>
#include <sstream>
#include <string>
#include <vector>
#include "spirv/unified1/NonSemanticClspvReflection.h"
#include "OpenCLDebugInfo100.h"
#include "source/diagnostic.h"
#include "source/enum_string_mapping.h"
@ -180,6 +182,460 @@ spv_result_t ValidateOperandDebugType(
<< " is not a valid debug type";
}
bool IsUint32Constant(ValidationState_t& _, uint32_t id) {
auto inst = _.FindDef(id);
if (!inst || inst->opcode() != SpvOpConstant) {
return false;
}
auto type = _.FindDef(inst->type_id());
if (!type || type->opcode() != SpvOpTypeInt) {
return false;
}
if (type->GetOperandAs<uint32_t>(1) != 32) {
return false;
}
if (type->GetOperandAs<uint32_t>(2) != 0) {
return false;
}
return true;
}
spv_result_t ValidateClspvReflectionKernel(ValidationState_t& _,
const Instruction* inst) {
const auto kernel_id = inst->GetOperandAs<uint32_t>(4);
const auto kernel = _.FindDef(kernel_id);
if (kernel->opcode() != SpvOpFunction) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Kernel does not reference a function";
}
bool found_kernel = false;
for (auto entry_point : _.entry_points()) {
if (entry_point == kernel_id) {
found_kernel = true;
break;
}
}
if (!found_kernel) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Kernel does not reference an entry-point";
}
const auto* exec_models = _.GetExecutionModels(kernel_id);
if (!exec_models || exec_models->empty()) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Kernel does not reference an entry-point";
}
for (auto exec_model : *exec_models) {
if (exec_model != SpvExecutionModelGLCompute) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Kernel must refer only to GLCompute entry-points";
}
}
auto name = _.FindDef(inst->GetOperandAs<uint32_t>(5));
if (!name || name->opcode() != SpvOpString) {
return _.diag(SPV_ERROR_INVALID_ID, inst) << "Name must be an OpString";
}
const std::string name_str = reinterpret_cast<const char*>(
name->words().data() + name->operands()[1].offset);
bool found = false;
for (auto& desc : _.entry_point_descriptions(kernel_id)) {
if (name_str == desc.name) {
found = true;
break;
}
}
if (!found) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Name must match an entry-point for Kernel";
}
return SPV_SUCCESS;
}
spv_result_t ValidateClspvReflectionArgumentInfo(ValidationState_t& _,
const Instruction* inst) {
const auto num_operands = inst->operands().size();
if (_.GetIdOpcode(inst->GetOperandAs<uint32_t>(4)) != SpvOpString) {
return _.diag(SPV_ERROR_INVALID_ID, inst) << "Name must be an OpString";
}
if (num_operands > 5) {
if (_.GetIdOpcode(inst->GetOperandAs<uint32_t>(5)) != SpvOpString) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "TypeName must be an OpString";
}
}
if (num_operands > 6) {
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(6))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "AddressQualifier must be a 32-bit unsigned integer "
"OpConstant";
}
}
if (num_operands > 7) {
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(7))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "AccessQualifier must be a 32-bit unsigned integer "
"OpConstant";
}
}
if (num_operands > 8) {
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(8))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "TypeQualifier must be a 32-bit unsigned integer "
"OpConstant";
}
}
return SPV_SUCCESS;
}
spv_result_t ValidateKernelDecl(ValidationState_t& _, const Instruction* inst) {
const auto decl_id = inst->GetOperandAs<uint32_t>(4);
const auto decl = _.FindDef(decl_id);
if (!decl || decl->opcode() != SpvOpExtInst) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Kernel must be a Kernel extended instruction";
}
if (decl->GetOperandAs<uint32_t>(2) != inst->GetOperandAs<uint32_t>(2)) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Kernel must be from the same extended instruction import";
}
const auto ext_inst =
decl->GetOperandAs<NonSemanticClspvReflectionInstructions>(3);
if (ext_inst != NonSemanticClspvReflectionKernel) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Kernel must be a Kernel extended instruction";
}
return SPV_SUCCESS;
}
spv_result_t ValidateArgInfo(ValidationState_t& _, const Instruction* inst,
uint32_t info_index) {
auto info = _.FindDef(inst->GetOperandAs<uint32_t>(info_index));
if (!info || info->opcode() != SpvOpExtInst) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "ArgInfo must be an ArgumentInfo extended instruction";
}
if (info->GetOperandAs<uint32_t>(2) != inst->GetOperandAs<uint32_t>(2)) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "ArgInfo must be from the same extended instruction import";
}
auto ext_inst = info->GetOperandAs<NonSemanticClspvReflectionInstructions>(3);
if (ext_inst != NonSemanticClspvReflectionArgumentInfo) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "ArgInfo must be an ArgumentInfo extended instruction";
}
return SPV_SUCCESS;
}
spv_result_t ValidateClspvReflectionArgumentBuffer(ValidationState_t& _,
const Instruction* inst) {
const auto num_operands = inst->operands().size();
if (auto error = ValidateKernelDecl(_, inst)) {
return error;
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(5))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Ordinal must be a 32-bit unsigned integer OpConstant";
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(6))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "DescriptorSet must be a 32-bit unsigned integer OpConstant";
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(7))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Binding must be a 32-bit unsigned integer OpConstant";
}
if (num_operands == 9) {
if (auto error = ValidateArgInfo(_, inst, 8)) {
return error;
}
}
return SPV_SUCCESS;
}
spv_result_t ValidateClspvReflectionArgumentPodBuffer(ValidationState_t& _,
const Instruction* inst) {
const auto num_operands = inst->operands().size();
if (auto error = ValidateKernelDecl(_, inst)) {
return error;
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(5))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Ordinal must be a 32-bit unsigned integer OpConstant";
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(6))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "DescriptorSet must be a 32-bit unsigned integer OpConstant";
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(7))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Binding must be a 32-bit unsigned integer OpConstant";
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(8))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Offset must be a 32-bit unsigned integer OpConstant";
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(9))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Size must be a 32-bit unsigned integer OpConstant";
}
if (num_operands == 11) {
if (auto error = ValidateArgInfo(_, inst, 10)) {
return error;
}
}
return SPV_SUCCESS;
}
spv_result_t ValidateClspvReflectionArgumentPodPushConstant(
ValidationState_t& _, const Instruction* inst) {
const auto num_operands = inst->operands().size();
if (auto error = ValidateKernelDecl(_, inst)) {
return error;
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(5))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Ordinal must be a 32-bit unsigned integer OpConstant";
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(6))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Offset must be a 32-bit unsigned integer OpConstant";
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(7))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Size must be a 32-bit unsigned integer OpConstant";
}
if (num_operands == 9) {
if (auto error = ValidateArgInfo(_, inst, 8)) {
return error;
}
}
return SPV_SUCCESS;
}
spv_result_t ValidateClspvReflectionArgumentWorkgroup(ValidationState_t& _,
const Instruction* inst) {
const auto num_operands = inst->operands().size();
if (auto error = ValidateKernelDecl(_, inst)) {
return error;
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(5))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Ordinal must be a 32-bit unsigned integer OpConstant";
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(6))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "SpecId must be a 32-bit unsigned integer OpConstant";
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(7))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "ElemSize must be a 32-bit unsigned integer OpConstant";
}
if (num_operands == 9) {
if (auto error = ValidateArgInfo(_, inst, 8)) {
return error;
}
}
return SPV_SUCCESS;
}
spv_result_t ValidateClspvReflectionSpecConstantTriple(
ValidationState_t& _, const Instruction* inst) {
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(4))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "X must be a 32-bit unsigned integer OpConstant";
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(5))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Y must be a 32-bit unsigned integer OpConstant";
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(6))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Z must be a 32-bit unsigned integer OpConstant";
}
return SPV_SUCCESS;
}
spv_result_t ValidateClspvReflectionSpecConstantWorkDim(
ValidationState_t& _, const Instruction* inst) {
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(4))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Dim must be a 32-bit unsigned integer OpConstant";
}
return SPV_SUCCESS;
}
spv_result_t ValidateClspvReflectionPushConstant(ValidationState_t& _,
const Instruction* inst) {
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(4))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Offset must be a 32-bit unsigned integer OpConstant";
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(5))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Size must be a 32-bit unsigned integer OpConstant";
}
return SPV_SUCCESS;
}
spv_result_t ValidateClspvReflectionConstantData(ValidationState_t& _,
const Instruction* inst) {
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(4))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "DescriptorSet must be a 32-bit unsigned integer OpConstant";
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(5))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Binding must be a 32-bit unsigned integer OpConstant";
}
if (_.GetIdOpcode(inst->GetOperandAs<uint32_t>(6)) != SpvOpString) {
return _.diag(SPV_ERROR_INVALID_ID, inst) << "Data must be an OpString";
}
return SPV_SUCCESS;
}
spv_result_t ValidateClspvReflectionSampler(ValidationState_t& _,
const Instruction* inst) {
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(4))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "DescriptorSet must be a 32-bit unsigned integer OpConstant";
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(5))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Binding must be a 32-bit unsigned integer OpConstant";
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(6))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Mask must be a 32-bit unsigned integer OpConstant";
}
return SPV_SUCCESS;
}
spv_result_t ValidateClspvReflectionPropertyRequiredWorkgroupSize(
ValidationState_t& _, const Instruction* inst) {
if (auto error = ValidateKernelDecl(_, inst)) {
return error;
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(5))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "X must be a 32-bit unsigned integer OpConstant";
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(6))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Y must be a 32-bit unsigned integer OpConstant";
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(7))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Z must be a 32-bit unsigned integer OpConstant";
}
return SPV_SUCCESS;
}
spv_result_t ValidateClspvReflectionInstruction(ValidationState_t& _,
const Instruction* inst,
uint32_t /*version*/) {
if (!_.IsVoidType(inst->type_id())) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Return Type must be OpTypeVoid";
}
auto ext_inst = inst->GetOperandAs<NonSemanticClspvReflectionInstructions>(3);
switch (ext_inst) {
case NonSemanticClspvReflectionKernel:
return ValidateClspvReflectionKernel(_, inst);
case NonSemanticClspvReflectionArgumentInfo:
return ValidateClspvReflectionArgumentInfo(_, inst);
case NonSemanticClspvReflectionArgumentStorageBuffer:
case NonSemanticClspvReflectionArgumentUniform:
case NonSemanticClspvReflectionArgumentSampledImage:
case NonSemanticClspvReflectionArgumentStorageImage:
case NonSemanticClspvReflectionArgumentSampler:
return ValidateClspvReflectionArgumentBuffer(_, inst);
case NonSemanticClspvReflectionArgumentPodStorageBuffer:
case NonSemanticClspvReflectionArgumentPodUniform:
return ValidateClspvReflectionArgumentPodBuffer(_, inst);
case NonSemanticClspvReflectionArgumentPodPushConstant:
return ValidateClspvReflectionArgumentPodPushConstant(_, inst);
case NonSemanticClspvReflectionArgumentWorkgroup:
return ValidateClspvReflectionArgumentWorkgroup(_, inst);
case NonSemanticClspvReflectionSpecConstantWorkgroupSize:
case NonSemanticClspvReflectionSpecConstantGlobalOffset:
return ValidateClspvReflectionSpecConstantTriple(_, inst);
case NonSemanticClspvReflectionSpecConstantWorkDim:
return ValidateClspvReflectionSpecConstantWorkDim(_, inst);
case NonSemanticClspvReflectionPushConstantGlobalOffset:
case NonSemanticClspvReflectionPushConstantEnqueuedLocalSize:
case NonSemanticClspvReflectionPushConstantGlobalSize:
case NonSemanticClspvReflectionPushConstantRegionOffset:
case NonSemanticClspvReflectionPushConstantNumWorkgroups:
case NonSemanticClspvReflectionPushConstantRegionGroupOffset:
return ValidateClspvReflectionPushConstant(_, inst);
case NonSemanticClspvReflectionConstantDataStorageBuffer:
case NonSemanticClspvReflectionConstantDataUniform:
return ValidateClspvReflectionConstantData(_, inst);
case NonSemanticClspvReflectionLiteralSampler:
return ValidateClspvReflectionSampler(_, inst);
case NonSemanticClspvReflectionPropertyRequiredWorkgroupSize:
return ValidateClspvReflectionPropertyRequiredWorkgroupSize(_, inst);
default:
break;
}
return SPV_SUCCESS;
}
} // anonymous namespace
spv_result_t ValidateExtension(ValidationState_t& _, const Instruction* inst) {
@ -2441,10 +2897,10 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
CHECK_DEBUG_OPERAND("Local Variable",
OpenCLDebugInfo100DebugLocalVariable, 5);
// TODO: We must discuss DebugDeclare.Variable of OpenCL.100.DebugInfo.
// Currently, it says "Variable must be an id of OpVariable instruction
// which defines the local variable.", but we want to allow
// OpFunctionParameter as well.
// TODO: We must discuss DebugDeclare.Variable of
// OpenCL.100.DebugInfo. Currently, it says "Variable must be an id of
// OpVariable instruction which defines the local variable.", but we
// want to allow OpFunctionParameter as well.
auto* operand = _.FindDef(inst->word(6));
if (operand->opcode() != SpvOpVariable &&
operand->opcode() != SpvOpFunctionParameter) {
@ -2484,6 +2940,30 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
assert(0);
break;
}
} else if (ext_inst_type == SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION) {
auto import_inst = _.FindDef(inst->GetOperandAs<uint32_t>(2));
const std::string name(reinterpret_cast<const char*>(
import_inst->words().data() + import_inst->operands()[1].offset));
const std::string reflection = "NonSemantic.ClspvReflection.";
char* end_ptr;
auto version_string = name.substr(reflection.size());
if (version_string.empty()) {
return _.diag(SPV_ERROR_INVALID_DATA, import_inst)
<< "Missing NonSemantic.ClspvReflection import version";
}
uint32_t version = static_cast<uint32_t>(
std::strtoul(version_string.c_str(), &end_ptr, 10));
if (end_ptr && *end_ptr != '\0') {
return _.diag(SPV_ERROR_INVALID_DATA, import_inst)
<< "NonSemantic.ClspvReflection import does not encode the "
"version correctly";
}
if (version == 0 || version > NonSemanticClspvReflectionRevision) {
return _.diag(SPV_ERROR_INVALID_DATA, import_inst)
<< "Unknown NonSemantic.ClspvReflection import version";
}
return ValidateClspvReflectionInstruction(_, inst, version);
}
return SPV_SUCCESS;

View File

@ -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;