Update SPIR-V Tools to fd773eb50
Includes fix #3253, which fixed the following SwANGLE test:
KHR-GLES31.core.compute_shader.resources-max
Changes:
fd773eb50 Start SPIRV-Tools v2020.3
fd8e13051 Finalize SPIRV-Tools v2020.2
3ef8fe9a5 Update CHANGES
022da4d0e Fix identification of Vulkan images and buffers (#3253)
1346dd5de Disallow phis of images, samplers and sampled images (#3246)
1c8bda372 Add data structure for DebugScope, DebugDeclare in spirv-opt (#3183)
e6f372c5c Whitelist SPV_KHR_ray_tracing (#3241)
7f341ffee Make file formatting comply with POSIX standards (#3242)
60104cd97 Add opt::Operand::AsCString and AsString (#3240)
da52d0875 Add RayQueryProvisionalKHR to opt types (#3239)
18d3896a1 Whitelist SPV_EXT_demote_to_helper_invocation for opt passes (#3236)
5a97e3a39 Add support for KHR_ray_{query,tracing} extensions (#3235)
25ede1ced Roll external/spirv-headers/ 30ef660ce..a17e17e36 (1 commit) (#3230)
7a8f79762 Update dependencies (#3228)
1fe9bcc10 Instrument: Debug Printf support (#3215)
6428ad05e spirv-fuzz: Support OpPhi when adding dead break and continue (#3225)
1af1df3b2 spirv-fuzz: Fix vector width issue in 'add equation instructions' pass (#3223)
dd3d91691 Allow sampledimage types as operand of OpCopyObject (#3222)
7c3de218f spirv-fuzz: Remove duplicated functionality (#3220)
659470446 spirv-fuzz: Allow OpPhi operand to be replaced with a composite synonym (#3221)
4c027048d spirv-fuzz: Add toggle access chain instruction transformation (#3211)
533af4981 spirv-fuzz: Add fuzzer pass to permute function parameters (#3212)
da4cd2148 spirv-fuzz: Use better function name (#3207)
66a682b6a spirv-fuzz: Add swap commutable operands transformation (#3205)
044ecc0b2 spirv-fuzz: Fuzzer pass to add equation instructions (#3202)
a6d3a2dd4 Refactor FuzzerPass::ApplyTransformation code duplication. (#3206)
e1688b60c Avoid use of Python distutils.dir_util (#3203)
661e79eec Adding WebGPU specific Workgroup scope rule (#3204)
70f888131 Add validation rules for OpenCL.DebugInfo.100 extension (#3133)
fb6e3e48d Combine extinst-name and extinst-output-base into one arg. (#3200)
Commands:
./third_party/update-spirvtools.sh
Bug: b/123642959
Bug: b/148460089
Change-Id: I8fa5d3609de1c6ae786c84b93cba7ac015b4f56e
diff --git a/src/Pipeline/SpirvShaderInstructions.inl b/src/Pipeline/SpirvShaderInstructions.inl
index 8ec4a44..4e60dd6 100644
--- a/src/Pipeline/SpirvShaderInstructions.inl
+++ b/src/Pipeline/SpirvShaderInstructions.inl
@@ -374,6 +374,13 @@
DECORATE_OP(T, OpSubgroupAnyKHR)
DECORATE_OP(T, OpSubgroupAllEqualKHR)
DECORATE_OP(T, OpSubgroupReadInvocationKHR)
+DECORATE_OP(T, OpTypeRayQueryProvisionalKHR)
+DECORATE_OP(T, OpRayQueryInitializeKHR)
+DECORATE_OP(T, OpRayQueryTerminateKHR)
+DECORATE_OP(T, OpRayQueryGenerateIntersectionKHR)
+DECORATE_OP(T, OpRayQueryConfirmIntersectionKHR)
+DECORATE_OP(T, OpRayQueryProceedKHR)
+DECORATE_OP(T, OpRayQueryGetIntersectionTypeKHR)
DECORATE_OP(T, OpGroupIAddNonUniformAMD)
DECORATE_OP(T, OpGroupFAddNonUniformAMD)
DECORATE_OP(T, OpGroupFMinNonUniformAMD)
@@ -547,3 +554,20 @@
DECORATE_OP(T, OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL)
DECORATE_OP(T, OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL)
DECORATE_OP(T, OpSubgroupAvcSicGetInterRawSadsINTEL)
+DECORATE_OP(T, OpRayQueryGetRayTMinKHR)
+DECORATE_OP(T, OpRayQueryGetRayFlagsKHR)
+DECORATE_OP(T, OpRayQueryGetIntersectionTKHR)
+DECORATE_OP(T, OpRayQueryGetIntersectionInstanceCustomIndexKHR)
+DECORATE_OP(T, OpRayQueryGetIntersectionInstanceIdKHR)
+DECORATE_OP(T, OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR)
+DECORATE_OP(T, OpRayQueryGetIntersectionGeometryIndexKHR)
+DECORATE_OP(T, OpRayQueryGetIntersectionPrimitiveIndexKHR)
+DECORATE_OP(T, OpRayQueryGetIntersectionBarycentricsKHR)
+DECORATE_OP(T, OpRayQueryGetIntersectionFrontFaceKHR)
+DECORATE_OP(T, OpRayQueryGetIntersectionCandidateAABBOpaqueKHR)
+DECORATE_OP(T, OpRayQueryGetIntersectionObjectRayDirectionKHR)
+DECORATE_OP(T, OpRayQueryGetIntersectionObjectRayOriginKHR)
+DECORATE_OP(T, OpRayQueryGetWorldRayDirectionKHR)
+DECORATE_OP(T, OpRayQueryGetWorldRayOriginKHR)
+DECORATE_OP(T, OpRayQueryGetIntersectionObjectToWorldKHR)
+DECORATE_OP(T, OpRayQueryGetIntersectionWorldToObjectKHR)
diff --git a/third_party/SPIRV-Tools/.gitignore b/third_party/SPIRV-Tools/.gitignore
index 196c63c..b2af56e 100644
--- a/third_party/SPIRV-Tools/.gitignore
+++ b/third_party/SPIRV-Tools/.gitignore
@@ -29,3 +29,6 @@
# C-Lion
/.idea/
/cmake-build-*/
+
+# VSCode
+/.vscode/*
diff --git a/third_party/SPIRV-Tools/BUILD.gn b/third_party/SPIRV-Tools/BUILD.gn
index b7cde34..d3107fd 100644
--- a/third_party/SPIRV-Tools/BUILD.gn
+++ b/third_party/SPIRV-Tools/BUILD.gn
@@ -186,21 +186,19 @@
script = "utils/generate_language_headers.py"
name = invoker.name
- extinst_output_base = "${target_gen_dir}/${name}"
+ extinst_output_path = "${target_gen_dir}/${name}.h"
args = [
- "--extinst-name",
- "${name}",
"--extinst-grammar",
rebase_path(invoker.grammar_file, root_build_dir),
- "--extinst-output-base",
- rebase_path(extinst_output_base, root_build_dir),
+ "--extinst-output-path",
+ rebase_path(extinst_output_path, root_build_dir),
]
inputs = [
invoker.grammar_file,
]
outputs = [
- "${extinst_output_base}.h",
+ "${extinst_output_path}",
]
}
}
@@ -592,6 +590,8 @@
"source/opt/inst_bindless_check_pass.h",
"source/opt/inst_buff_addr_check_pass.cpp",
"source/opt/inst_buff_addr_check_pass.h",
+ "source/opt/inst_debug_printf_pass.cpp",
+ "source/opt/inst_debug_printf_pass.h",
"source/opt/instruction.cpp",
"source/opt/instruction.h",
"source/opt/instruction_list.cpp",
diff --git a/third_party/SPIRV-Tools/CHANGES b/third_party/SPIRV-Tools/CHANGES
index 48c93a4..fe6641e 100644
--- a/third_party/SPIRV-Tools/CHANGES
+++ b/third_party/SPIRV-Tools/CHANGES
@@ -1,7 +1,34 @@
Revision history for SPIRV-Tools
-v2020.2-dev 2020-02-03
- - Start v2020.2-dev
+v2020.3-dev 2020-03-26
+ - Start v2020.3-dev
+
+v2020.2 2020-03-26
+ - General:
+ - Support extended instructions in the vscode language server
+ - Make spvOpcodeString part of the public API (#3174)
+ - Added guide to writing a spirv-fuzz fuzzer pass (#3190)
+ - Add support for KHR_ray_{query,tracing} extensions (#3235)
+ - Optimizer
+ - Debug Printf support (#3215)
+ - Add data structure for DebugScope, DebugDeclare in spirv-opt (#3183)
+ - Fix identification of Vulkan images and buffers (#3253)
+ - Validator
+ - Add support for SPV_AMD_shader_image_load_store_lod (#3186)
+ - Add validation rules for OpenCL.DebugInfo.100 extension (#3133)
+ - Adding WebGPU specific Workgroup scope rule (#3204)
+ - Disallow phis of images, samplers and sampled images (#3246)
+ - Reduce
+ - Fuzz
+ - Fuzzer passes to add local and global variables (#3175)
+ - Add fuzzer passes to add loads/stores (#3176)
+ - Fuzzer pass to add function calls (#3178)
+ - Fuzzer pass that adds access chains (#3182)
+ - Fuzzer pass to add equation instructions (#3202)
+ - Add swap commutable operands transformation (#3205)
+ - Add fuzzer pass to permute function parameters (#3212)
+ - Allow OpPhi operand to be replaced with a composite synonym (#3221)
+ - Linker
v2020.1 2020-02-03
- General:
diff --git a/third_party/SPIRV-Tools/DEPS b/third_party/SPIRV-Tools/DEPS
index 23c4111..c3e78a2 100644
--- a/third_party/SPIRV-Tools/DEPS
+++ b/third_party/SPIRV-Tools/DEPS
@@ -6,7 +6,7 @@
'effcee_revision': 'cd25ec17e9382f99a895b9ef53ff3c277464d07d',
'googletest_revision': 'f2fb48c3b3d79a75a88a99fba6576b25d42ec528',
're2_revision': '5bd613749fd530b576b890283bfb6bc6ea6246cb',
- 'spirv_headers_revision': 'dc77030acc9c6fe7ca21fff54c5a9d7b532d7da6',
+ 'spirv_headers_revision': 'f8bf11a0253a32375c32cad92c841237b96696c0',
}
deps = {
diff --git a/third_party/SPIRV-Tools/build_defs.bzl b/third_party/SPIRV-Tools/build_defs.bzl
index 5d913a1..15b70c7 100644
--- a/third_party/SPIRV-Tools/build_defs.bzl
+++ b/third_party/SPIRV-Tools/build_defs.bzl
@@ -167,16 +167,16 @@
def generate_extinst_lang_headers(name, grammar = None):
if not grammar:
fail("Must specify grammar", "grammar")
- fmtargs = [name]
+ outs = [name + ".h"]
+ fmtargs = outs
native.genrule(
name = "gen_extinst_lang_headers_" + name,
srcs = [grammar],
- outs = [name + ".h"],
+ outs = outs,
cmd = (
"$(location :generate_language_headers) " +
- "--extinst-name={0} " +
"--extinst-grammar=$< " +
- "--extinst-output-base=$(@D)/{0}"
+ "--extinst-output-path=$(location {0})"
).format(*fmtargs),
tools = [":generate_language_headers"],
visibility = ["//visibility:private"],
diff --git a/third_party/SPIRV-Tools/examples/cpp-interface/CMakeLists.txt b/third_party/SPIRV-Tools/examples/cpp-interface/CMakeLists.txt
index d050b07..7887ee7 100644
--- a/third_party/SPIRV-Tools/examples/cpp-interface/CMakeLists.txt
+++ b/third_party/SPIRV-Tools/examples/cpp-interface/CMakeLists.txt
@@ -16,4 +16,4 @@
TARGET spirv-tools-cpp-example
SRCS main.cpp
LIBS SPIRV-Tools-opt
-)
\ No newline at end of file
+)
diff --git a/third_party/SPIRV-Tools/include/spirv-tools/instrument.hpp b/third_party/SPIRV-Tools/include/spirv-tools/instrument.hpp
index 2dcb333..d3180e4 100644
--- a/third_party/SPIRV-Tools/include/spirv-tools/instrument.hpp
+++ b/third_party/SPIRV-Tools/include/spirv-tools/instrument.hpp
@@ -208,6 +208,9 @@
// The binding for the input buffer read by InstBuffAddrCheckPass.
static const int kDebugInputBindingBuffAddr = 2;
+// This is the output buffer written by InstDebugPrintfPass.
+static const int kDebugOutputPrintfStream = 3;
+
// Bindless Validation Input Buffer Format
//
// An input buffer for bindless validation consists of a single array of
diff --git a/third_party/SPIRV-Tools/include/spirv-tools/libspirv.h b/third_party/SPIRV-Tools/include/spirv-tools/libspirv.h
index 21a9608..03c7d1b 100644
--- a/third_party/SPIRV-Tools/include/spirv-tools/libspirv.h
+++ b/third_party/SPIRV-Tools/include/spirv-tools/libspirv.h
@@ -1,4 +1,6 @@
-// Copyright (c) 2015-2016 The Khronos Group Inc.
+// Copyright (c) 2015-2020 The Khronos Group Inc.
+// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights
+// reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -165,6 +167,12 @@
SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS, // SPIR-V Sec 3.29
SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO, // SPIR-V Sec 3.30
SPV_OPERAND_TYPE_CAPABILITY, // SPIR-V Sec 3.31
+ SPV_OPERAND_TYPE_RAY_FLAGS, // SPIR-V Sec 3.RF
+ SPV_OPERAND_TYPE_RAY_QUERY_INTERSECTION, // SPIR-V Sec 3.RQIntersection
+ SPV_OPERAND_TYPE_RAY_QUERY_COMMITTED_INTERSECTION_TYPE, // SPIR-V Sec
+ // 3.RQCommitted
+ SPV_OPERAND_TYPE_RAY_QUERY_CANDIDATE_INTERSECTION_TYPE, // SPIR-V Sec
+ // 3.RQCandidate
// Set 5: Operands that are a single word bitmask.
// Sometimes a set bit indicates the instruction requires still more operands.
diff --git a/third_party/SPIRV-Tools/include/spirv-tools/optimizer.hpp b/third_party/SPIRV-Tools/include/spirv-tools/optimizer.hpp
index c31ccef..b904923 100644
--- a/third_party/SPIRV-Tools/include/spirv-tools/optimizer.hpp
+++ b/third_party/SPIRV-Tools/include/spirv-tools/optimizer.hpp
@@ -791,6 +791,18 @@
uint32_t shader_id,
uint32_t version = 2);
+// Create a pass to instrument OpDebugPrintf instructions.
+// This pass replaces all OpDebugPrintf instructions with instructions to write
+// a record containing the string id and the all specified values into a special
+// printf output buffer (if space allows). This pass is designed to support
+// the printf validation in the Vulkan validation layers.
+//
+// The instrumentation will write buffers in debug descriptor set |desc_set|.
+// It will write |shader_id| in each output record to identify the shader
+// module which generated the record.
+Optimizer::PassToken CreateInstDebugPrintfPass(uint32_t desc_set,
+ uint32_t shader_id);
+
// Create a pass to upgrade to the VulkanKHR memory model.
// This pass upgrades the Logical GLSL450 memory model to Logical VulkanKHR.
// Additionally, it modifies memory, image, atomic and barrier operations to
diff --git a/third_party/SPIRV-Tools/source/CMakeLists.txt b/third_party/SPIRV-Tools/source/CMakeLists.txt
index 4e7e10c..708ca84 100644
--- a/third_party/SPIRV-Tools/source/CMakeLists.txt
+++ b/third_party/SPIRV-Tools/source/CMakeLists.txt
@@ -126,13 +126,11 @@
endmacro(spvtools_vendor_tables)
macro(spvtools_extinst_lang_headers NAME GRAMMAR_FILE)
- set(OUTBASE ${spirv-tools_BINARY_DIR}/${NAME})
- set(OUT_H ${OUTBASE}.h)
+ set(OUT_H ${spirv-tools_BINARY_DIR}/${NAME}.h)
add_custom_command(OUTPUT ${OUT_H}
COMMAND ${PYTHON_EXECUTABLE} ${LANG_HEADER_PROCESSING_SCRIPT}
- --extinst-name=${NAME}
--extinst-grammar=${GRAMMAR_FILE}
- --extinst-output-base=${OUTBASE}
+ --extinst-output-path=${OUT_H}
DEPENDS ${LANG_HEADER_PROCESSING_SCRIPT} ${GRAMMAR_FILE}
COMMENT "Generate language specific header for ${NAME}.")
add_custom_target(spirv-tools-header-${NAME} DEPENDS ${OUT_H})
diff --git a/third_party/SPIRV-Tools/source/binary.cpp b/third_party/SPIRV-Tools/source/binary.cpp
index 0463061..f16bf52 100644
--- a/third_party/SPIRV-Tools/source/binary.cpp
+++ b/third_party/SPIRV-Tools/source/binary.cpp
@@ -1,4 +1,6 @@
-// Copyright (c) 2015-2016 The Khronos Group Inc.
+// Copyright (c) 2015-2020 The Khronos Group Inc.
+// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights
+// reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -633,6 +635,10 @@
case SPV_OPERAND_TYPE_GROUP_OPERATION:
case SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS:
case SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO:
+ case SPV_OPERAND_TYPE_RAY_FLAGS:
+ case SPV_OPERAND_TYPE_RAY_QUERY_INTERSECTION:
+ case SPV_OPERAND_TYPE_RAY_QUERY_COMMITTED_INTERSECTION_TYPE:
+ case SPV_OPERAND_TYPE_RAY_QUERY_CANDIDATE_INTERSECTION_TYPE:
case SPV_OPERAND_TYPE_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING:
case SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE:
case SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER:
diff --git a/third_party/SPIRV-Tools/source/disassemble.cpp b/third_party/SPIRV-Tools/source/disassemble.cpp
index 4b3972b..af30ce0 100644
--- a/third_party/SPIRV-Tools/source/disassemble.cpp
+++ b/third_party/SPIRV-Tools/source/disassemble.cpp
@@ -1,4 +1,6 @@
-// Copyright (c) 2015-2016 The Khronos Group Inc.
+// Copyright (c) 2015-2020 The Khronos Group Inc.
+// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights
+// reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -277,6 +279,10 @@
case SPV_OPERAND_TYPE_GROUP_OPERATION:
case SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS:
case SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO:
+ case SPV_OPERAND_TYPE_RAY_FLAGS:
+ case SPV_OPERAND_TYPE_RAY_QUERY_INTERSECTION:
+ case SPV_OPERAND_TYPE_RAY_QUERY_COMMITTED_INTERSECTION_TYPE:
+ case SPV_OPERAND_TYPE_RAY_QUERY_CANDIDATE_INTERSECTION_TYPE:
case SPV_OPERAND_TYPE_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING:
case SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE:
case SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER:
diff --git a/third_party/SPIRV-Tools/source/enum_set.h b/third_party/SPIRV-Tools/source/enum_set.h
index 2e7046d..d4d31e3 100644
--- a/third_party/SPIRV-Tools/source/enum_set.h
+++ b/third_party/SPIRV-Tools/source/enum_set.h
@@ -93,6 +93,10 @@
// enum value is already in the set.
void Add(EnumType c) { AddWord(ToWord(c)); }
+ // Removes the given enum value from the set. This has no effect if the
+ // enum value is not in the set.
+ void Remove(EnumType c) { RemoveWord(ToWord(c)); }
+
// Returns true if this enum value is in the set.
bool Contains(EnumType c) const { return ContainsWord(ToWord(c)); }
@@ -141,6 +145,17 @@
}
}
+ // Removes the given enum value (as a 32-bit word) from the set. This has no
+ // effect if the enum value is not in the set.
+ void RemoveWord(uint32_t word) {
+ if (auto new_bits = AsMask(word)) {
+ mask_ &= ~new_bits;
+ } else {
+ auto itr = Overflow().find(word);
+ if (itr != Overflow().end()) Overflow().erase(itr);
+ }
+ }
+
// Returns true if the enum represented as a 32-bit word is in the set.
bool ContainsWord(uint32_t word) const {
// We shouldn't call Overflow() since this is a const method.
diff --git a/third_party/SPIRV-Tools/source/fuzz/CMakeLists.txt b/third_party/SPIRV-Tools/source/fuzz/CMakeLists.txt
index 4d5feea..3a9d604 100644
--- a/third_party/SPIRV-Tools/source/fuzz/CMakeLists.txt
+++ b/third_party/SPIRV-Tools/source/fuzz/CMakeLists.txt
@@ -42,6 +42,7 @@
fuzzer_pass_add_dead_blocks.h
fuzzer_pass_add_dead_breaks.h
fuzzer_pass_add_dead_continues.h
+ fuzzer_pass_add_equation_instructions.h
fuzzer_pass_add_function_calls.h
fuzzer_pass_add_global_variables.h
fuzzer_pass_add_loads.h
@@ -61,7 +62,10 @@
fuzzer_pass_obfuscate_constants.h
fuzzer_pass_outline_functions.h
fuzzer_pass_permute_blocks.h
+ fuzzer_pass_permute_function_parameters.h
fuzzer_pass_split_blocks.h
+ fuzzer_pass_swap_commutable_operands.h
+ fuzzer_pass_toggle_access_chain_instruction.h
fuzzer_util.h
id_use_descriptor.h
instruction_descriptor.h
@@ -96,11 +100,13 @@
transformation_composite_construct.h
transformation_composite_extract.h
transformation_copy_object.h
+ transformation_equation_instruction.h
transformation_function_call.h
transformation_load.h
transformation_merge_blocks.h
transformation_move_block_down.h
transformation_outline_function.h
+ transformation_permute_function_parameters.h
transformation_replace_boolean_constant_with_constant_binary.h
transformation_replace_constant_with_uniform.h
transformation_replace_id_with_synonym.h
@@ -110,6 +116,8 @@
transformation_set_selection_control.h
transformation_split_block.h
transformation_store.h
+ transformation_swap_commutable_operands.h
+ transformation_toggle_access_chain_instruction.h
transformation_vector_shuffle.h
uniform_buffer_element_descriptor.h
${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.h
@@ -126,6 +134,7 @@
fuzzer_pass_add_dead_blocks.cpp
fuzzer_pass_add_dead_breaks.cpp
fuzzer_pass_add_dead_continues.cpp
+ fuzzer_pass_add_equation_instructions.cpp
fuzzer_pass_add_function_calls.cpp
fuzzer_pass_add_global_variables.cpp
fuzzer_pass_add_loads.cpp
@@ -145,7 +154,10 @@
fuzzer_pass_obfuscate_constants.cpp
fuzzer_pass_outline_functions.cpp
fuzzer_pass_permute_blocks.cpp
+ fuzzer_pass_permute_function_parameters.cpp
fuzzer_pass_split_blocks.cpp
+ fuzzer_pass_swap_commutable_operands.cpp
+ fuzzer_pass_toggle_access_chain_instruction.cpp
fuzzer_util.cpp
id_use_descriptor.cpp
instruction_descriptor.cpp
@@ -179,11 +191,13 @@
transformation_composite_construct.cpp
transformation_composite_extract.cpp
transformation_copy_object.cpp
+ transformation_equation_instruction.cpp
transformation_function_call.cpp
transformation_load.cpp
transformation_merge_blocks.cpp
transformation_move_block_down.cpp
transformation_outline_function.cpp
+ transformation_permute_function_parameters.cpp
transformation_replace_boolean_constant_with_constant_binary.cpp
transformation_replace_constant_with_uniform.cpp
transformation_replace_id_with_synonym.cpp
@@ -193,6 +207,8 @@
transformation_set_selection_control.cpp
transformation_split_block.cpp
transformation_store.cpp
+ transformation_swap_commutable_operands.cpp
+ transformation_toggle_access_chain_instruction.cpp
transformation_vector_shuffle.cpp
uniform_buffer_element_descriptor.cpp
${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc
diff --git a/third_party/SPIRV-Tools/source/fuzz/equivalence_relation.h b/third_party/SPIRV-Tools/source/fuzz/equivalence_relation.h
index 046536f..7bb8b66 100644
--- a/third_party/SPIRV-Tools/source/fuzz/equivalence_relation.h
+++ b/third_party/SPIRV-Tools/source/fuzz/equivalence_relation.h
@@ -75,18 +75,8 @@
// Register each value if necessary.
for (auto value : {value1, value2}) {
if (!Exists(value)) {
- // Register the value in the equivalence relation. This relies on
- // T having a copy constructor.
- auto unique_pointer_to_value = MakeUnique<T>(value);
- auto pointer_to_value = unique_pointer_to_value.get();
- owned_values_.push_back(std::move(unique_pointer_to_value));
- value_set_.insert(pointer_to_value);
-
- // Initially say that the value is its own parent and that it has no
- // children.
- assert(pointer_to_value && "Representatives should never be null.");
- parent_[pointer_to_value] = pointer_to_value;
- children_[pointer_to_value] = std::vector<const T*>();
+ // Register the value in the equivalence relation.
+ Register(value);
}
}
@@ -112,6 +102,27 @@
}
}
+ // Requires that |value| is not known to the equivalence relation. Registers
+ // it in its own equivalence class and returns a pointer to the equivalence
+ // class representative.
+ const T* Register(T& value) {
+ assert(!Exists(value));
+
+ // This relies on T having a copy constructor.
+ auto unique_pointer_to_value = MakeUnique<T>(value);
+ auto pointer_to_value = unique_pointer_to_value.get();
+ owned_values_.push_back(std::move(unique_pointer_to_value));
+ value_set_.insert(pointer_to_value);
+
+ // Initially say that the value is its own parent and that it has no
+ // children.
+ assert(pointer_to_value && "Representatives should never be null.");
+ parent_[pointer_to_value] = pointer_to_value;
+ children_[pointer_to_value] = std::vector<const T*>();
+
+ return pointer_to_value;
+ }
+
// Returns exactly one representative per equivalence class.
std::vector<const T*> GetEquivalenceClassRepresentatives() const {
std::vector<const T*> result;
@@ -168,7 +179,6 @@
return value_set_.find(&value) != value_set_.end();
}
- private:
// Returns the representative of the equivalence class of |value|, which must
// already be known to the equivalence relation. This is the 'Find' operation
// in a classic union-find data structure.
@@ -207,6 +217,7 @@
return result;
}
+ private:
// Maps every value to a parent. The representative of an equivalence class
// is its own parent. A value's representative can be found by walking its
// chain of ancestors.
diff --git a/third_party/SPIRV-Tools/source/fuzz/fact_manager.cpp b/third_party/SPIRV-Tools/source/fuzz/fact_manager.cpp
index 486e8f5..31d3b94 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fact_manager.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fact_manager.cpp
@@ -28,24 +28,13 @@
namespace {
-std::string ToString(const protobufs::Fact& fact) {
- assert(fact.fact_case() == protobufs::Fact::kConstantUniformFact &&
- "Right now this is the only fact we know how to stringify.");
+std::string ToString(const protobufs::FactConstantUniform& fact) {
std::stringstream stream;
- stream << "("
- << fact.constant_uniform_fact()
- .uniform_buffer_element_descriptor()
- .descriptor_set()
- << ", "
- << fact.constant_uniform_fact()
- .uniform_buffer_element_descriptor()
- .binding()
- << ")[";
+ stream << "(" << fact.uniform_buffer_element_descriptor().descriptor_set()
+ << ", " << fact.uniform_buffer_element_descriptor().binding() << ")[";
bool first = true;
- for (auto index : fact.constant_uniform_fact()
- .uniform_buffer_element_descriptor()
- .index()) {
+ for (auto index : fact.uniform_buffer_element_descriptor().index()) {
if (first) {
first = false;
} else {
@@ -57,7 +46,7 @@
stream << "] == [";
first = true;
- for (auto constant_word : fact.constant_uniform_fact().constant_word()) {
+ for (auto constant_word : fact.constant_word()) {
if (first) {
first = false;
} else {
@@ -70,6 +59,36 @@
return stream.str();
}
+std::string ToString(const protobufs::FactDataSynonym& fact) {
+ std::stringstream stream;
+ stream << fact.data1() << " = " << fact.data2();
+ return stream.str();
+}
+
+std::string ToString(const protobufs::FactIdEquation& fact) {
+ std::stringstream stream;
+ stream << fact.lhs_id();
+ stream << " " << static_cast<SpvOp>(fact.opcode());
+ for (auto rhs_id : fact.rhs_id()) {
+ stream << " " << rhs_id;
+ }
+ return stream.str();
+}
+
+std::string ToString(const protobufs::Fact& fact) {
+ switch (fact.fact_case()) {
+ case protobufs::Fact::kConstantUniformFact:
+ return ToString(fact.constant_uniform_fact());
+ case protobufs::Fact::kDataSynonymFact:
+ return ToString(fact.data_synonym_fact());
+ case protobufs::Fact::kIdEquationFact:
+ return ToString(fact.id_equation_fact());
+ default:
+ assert(false && "Stringification not supported for this fact.");
+ return "";
+ }
+}
+
} // namespace
//=======================
@@ -331,16 +350,71 @@
//==============================
//==============================
-// Data synonym facts
+// Data synonym and id equation facts
+
+// This helper struct represents the right hand side of an equation as an
+// operator applied to a number of data descriptor operands.
+struct Operation {
+ SpvOp opcode;
+ std::vector<const protobufs::DataDescriptor*> operands;
+};
+
+// Hashing for operations, to allow deterministic unordered sets.
+struct OperationHash {
+ size_t operator()(const Operation& operation) const {
+ std::u32string hash;
+ hash.push_back(operation.opcode);
+ for (auto operand : operation.operands) {
+ hash.push_back(static_cast<uint32_t>(DataDescriptorHash()(operand)));
+ }
+ return std::hash<std::u32string>()(hash);
+ }
+};
+
+// Equality for operations, to allow deterministic unordered sets.
+struct OperationEquals {
+ bool operator()(const Operation& first, const Operation& second) const {
+ // Equal operations require...
+ //
+ // Equal opcodes.
+ if (first.opcode != second.opcode) {
+ return false;
+ }
+ // Matching operand counds.
+ if (first.operands.size() != second.operands.size()) {
+ return false;
+ }
+ // Equal operands.
+ for (uint32_t i = 0; i < first.operands.size(); i++) {
+ if (!DataDescriptorEquals()(first.operands[i], second.operands[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+};
+
+// A helper, for debugging, to represent an operation as a string.
+std::string ToString(const Operation& operation) {
+ std::stringstream stream;
+ stream << operation.opcode;
+ for (auto operand : operation.operands) {
+ stream << " " << *operand;
+ }
+ return stream.str();
+}
// The purpose of this class is to group the fields and data used to represent
-// facts about data synonyms.
-class FactManager::DataSynonymFacts {
+// facts about data synonyms and id equations.
+class FactManager::DataSynonymAndIdEquationFacts {
public:
// See method in FactManager which delegates to this method.
void AddFact(const protobufs::FactDataSynonym& fact, opt::IRContext* context);
// See method in FactManager which delegates to this method.
+ void AddFact(const protobufs::FactIdEquation& fact, opt::IRContext* context);
+
+ // See method in FactManager which delegates to this method.
std::vector<const protobufs::DataDescriptor*> GetSynonymsForDataDescriptor(
const protobufs::DataDescriptor& data_descriptor,
opt::IRContext* context) const;
@@ -355,11 +429,12 @@
opt::IRContext* context) const;
private:
- // Adds |fact| to the set of managed facts, and recurses into sub-components
- // of the data descriptors referenced in |fact|, if they are composites, to
+ // Adds the synonym |dd1| = |dd2| to the set of managed facts, and recurses
+ // into sub-components of the data descriptors, if they are composites, to
// record that their components are pairwise-synonymous.
- void AddFactRecursive(const protobufs::FactDataSynonym& fact,
- opt::IRContext* context);
+ void AddDataSynonymFactRecursive(const protobufs::DataDescriptor& dd1,
+ const protobufs::DataDescriptor& dd2,
+ opt::IRContext* context);
// Inspects all known facts and adds corollary facts; e.g. if we know that
// a.x == b.x and a.y == b.y, where a and b have vec2 type, we can record
@@ -371,11 +446,21 @@
void ComputeClosureOfFacts(opt::IRContext* context) const;
// Returns true if and only if |dd1| and |dd2| are valid data descriptors
- // whose associated data have the same type.
+ // whose associated data have the same type (modulo integer signedness).
bool DataDescriptorsAreWellFormedAndComparable(
opt::IRContext* context, const protobufs::DataDescriptor& dd1,
const protobufs::DataDescriptor& dd2) const;
+ // Requires that |lhs_dd| and every element of |rhs_dds| is present in the
+ // |synonymous_| equivalence relation and is its own representative. Records
+ // the fact that the equation "|lhs_dd| |opcode| |rhs_dds|" holds, and adds
+ // any corollaries, in the form of data synonym or equation facts, that
+ // follow from this and other known facts.
+ void AddEquationFactRecursive(
+ const protobufs::DataDescriptor& lhs_dd, SpvOp opcode,
+ const std::vector<const protobufs::DataDescriptor*>& rhs_dds,
+ opt::IRContext* context);
+
// The data descriptors that are known to be synonymous with one another are
// captured by this equivalence relation.
//
@@ -396,31 +481,243 @@
// whether a new fact has been added since the last time such a computation
// was performed.
//
- // It is mutable so faciliate having const methods, that provide answers to
+ // It is mutable to facilitate having const methods, that provide answers to
// questions about data synonym facts, triggering closure computation on
// demand.
- mutable bool closure_computation_required = false;
+ mutable bool closure_computation_required_ = false;
+
+ // Represents a set of equations on data descriptors as a map indexed by
+ // left-hand-side, mapping a left-hand-side to a set of operations, each of
+ // which (together with the left-hand-side) defines an equation.
+ //
+ // All data descriptors occurring in equations are required to be present in
+ // the |synonymous_| equivalence relation, and to be their own representatives
+ // in that relation.
+ std::unordered_map<
+ const protobufs::DataDescriptor*,
+ std::unordered_set<Operation, OperationHash, OperationEquals>>
+ id_equations_;
};
-void FactManager::DataSynonymFacts::AddFact(
+void FactManager::DataSynonymAndIdEquationFacts::AddFact(
const protobufs::FactDataSynonym& fact, opt::IRContext* context) {
// Add the fact, including all facts relating sub-components of the data
// descriptors that are involved.
- AddFactRecursive(fact, context);
+ AddDataSynonymFactRecursive(fact.data1(), fact.data2(), context);
}
-void FactManager::DataSynonymFacts::AddFactRecursive(
- const protobufs::FactDataSynonym& fact, opt::IRContext* context) {
- assert(DataDescriptorsAreWellFormedAndComparable(context, fact.data1(),
- fact.data2()));
+void FactManager::DataSynonymAndIdEquationFacts::AddFact(
+ const protobufs::FactIdEquation& fact, opt::IRContext* context) {
+ protobufs::DataDescriptor lhs_dd = MakeDataDescriptor(fact.lhs_id(), {});
+
+ // Register the LHS in the equivalence relation if needed, and get a pointer
+ // to its representative.
+ if (!synonymous_.Exists(lhs_dd)) {
+ synonymous_.Register(lhs_dd);
+ }
+ const protobufs::DataDescriptor* lhs_dd_ptr = synonymous_.Find(&lhs_dd);
+
+ // Get equivalence class representatives for all ids used on the RHS of the
+ // equation.
+ std::vector<const protobufs::DataDescriptor*> rhs_dd_ptrs;
+ 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));
+ }
+ // We now have the equation in a form where it refers exclusively to
+ // equivalence class representatives. Add it to our set of facts and work
+ // out any follow-on facts.
+ AddEquationFactRecursive(*lhs_dd_ptr, static_cast<SpvOp>(fact.opcode()),
+ rhs_dd_ptrs, context);
+}
+
+void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive(
+ const protobufs::DataDescriptor& lhs_dd, SpvOp opcode,
+ const std::vector<const protobufs::DataDescriptor*>& rhs_dds,
+ opt::IRContext* context) {
+ // Precondition: all data descriptors referenced in this equation must be
+ // equivalence class representatives - i.e. the equation must be in canonical
+ // form.
+ assert(synonymous_.Exists(lhs_dd));
+ assert(synonymous_.Find(&lhs_dd) == &lhs_dd);
+ for (auto rhs_dd : rhs_dds) {
+ (void)(rhs_dd); // Keep compilers happy in release mode.
+ assert(synonymous_.Exists(*rhs_dd));
+ assert(synonymous_.Find(rhs_dd) == rhs_dd);
+ }
+
+ if (id_equations_.count(&lhs_dd) == 0) {
+ // We have not seen an equation with this LHS before, so associate the LHS
+ // with an initially empty set.
+ id_equations_.insert(
+ {&lhs_dd,
+ std::unordered_set<Operation, OperationHash, OperationEquals>()});
+ }
+
+ {
+ auto existing_equations = id_equations_.find(&lhs_dd);
+ assert(existing_equations != id_equations_.end() &&
+ "A set of operations should be present, even if empty.");
+
+ Operation new_operation = {opcode, rhs_dds};
+ if (existing_equations->second.count(new_operation)) {
+ // This equation is known, so there is nothing further to be done.
+ return;
+ }
+ // Add the equation to the set of known equations.
+ existing_equations->second.insert(new_operation);
+ }
+
+ // Now try to work out corollaries implied by the new equation and existing
+ // facts.
+ switch (opcode) {
+ case SpvOpIAdd: {
+ // Equation form: "a = b + c"
+ {
+ auto existing_first_operand_equations = id_equations_.find(rhs_dds[0]);
+ if (existing_first_operand_equations != id_equations_.end()) {
+ for (auto equation : existing_first_operand_equations->second) {
+ if (equation.opcode == SpvOpISub) {
+ // Equation form: "a = (d - e) + c"
+ if (equation.operands[1] == rhs_dds[1]) {
+ // Equation form: "a = (d - c) + c"
+ // We can thus infer "a = d"
+ AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0],
+ context);
+ }
+ if (equation.operands[0] == rhs_dds[1]) {
+ // Equation form: "a = (c - e) + c"
+ // We can thus infer "a = -e"
+ AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
+ {equation.operands[1]}, context);
+ }
+ }
+ }
+ }
+ }
+ {
+ auto existing_second_operand_equations = id_equations_.find(rhs_dds[1]);
+ if (existing_second_operand_equations != id_equations_.end()) {
+ for (auto equation : existing_second_operand_equations->second) {
+ if (equation.opcode == SpvOpISub) {
+ // Equation form: "a = b + (d - e)"
+ if (equation.operands[1] == rhs_dds[0]) {
+ // Equation form: "a = b + (d - b)"
+ // We can thus infer "a = d"
+ AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0],
+ context);
+ }
+ }
+ }
+ }
+ }
+ break;
+ }
+ case SpvOpISub: {
+ // Equation form: "a = b - c"
+ {
+ auto existing_first_operand_equations = id_equations_.find(rhs_dds[0]);
+ if (existing_first_operand_equations != id_equations_.end()) {
+ for (auto equation : existing_first_operand_equations->second) {
+ if (equation.opcode == SpvOpIAdd) {
+ // Equation form: "a = (d + e) - c"
+ if (equation.operands[0] == rhs_dds[1]) {
+ // Equation form: "a = (c + e) - c"
+ // We can thus infer "a = e"
+ AddDataSynonymFactRecursive(lhs_dd, *equation.operands[1],
+ context);
+ }
+ if (equation.operands[1] == rhs_dds[1]) {
+ // Equation form: "a = (d + c) - c"
+ // We can thus infer "a = d"
+ AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0],
+ context);
+ }
+ }
+
+ if (equation.opcode == SpvOpISub) {
+ // Equation form: "a = (d - e) - c"
+ if (equation.operands[0] == rhs_dds[1]) {
+ // Equation form: "a = (c - e) - c"
+ // We can thus infer "a = -e"
+ AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
+ {equation.operands[1]}, context);
+ }
+ }
+ }
+ }
+ }
+
+ {
+ auto existing_second_operand_equations = id_equations_.find(rhs_dds[1]);
+ if (existing_second_operand_equations != id_equations_.end()) {
+ for (auto equation : existing_second_operand_equations->second) {
+ if (equation.opcode == SpvOpIAdd) {
+ // Equation form: "a = b - (d + e)"
+ if (equation.operands[0] == rhs_dds[0]) {
+ // Equation form: "a = b - (b + e)"
+ // We can thus infer "a = -e"
+ AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
+ {equation.operands[1]}, context);
+ }
+ if (equation.operands[1] == rhs_dds[0]) {
+ // Equation form: "a = b - (d + b)"
+ // We can thus infer "a = -d"
+ AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
+ {equation.operands[0]}, context);
+ }
+ }
+ if (equation.opcode == SpvOpISub) {
+ // Equation form: "a = b - (d - e)"
+ if (equation.operands[0] == rhs_dds[0]) {
+ // Equation form: "a = b - (b - e)"
+ // We can thus infer "a = e"
+ AddDataSynonymFactRecursive(lhs_dd, *equation.operands[1],
+ context);
+ }
+ }
+ }
+ }
+ }
+ break;
+ }
+ case SpvOpLogicalNot:
+ case SpvOpSNegate: {
+ // Equation form: "a = !b" or "a = -b"
+ auto existing_equations = id_equations_.find(rhs_dds[0]);
+ if (existing_equations != id_equations_.end()) {
+ for (auto equation : existing_equations->second) {
+ if (equation.opcode == opcode) {
+ // Equation form: "a = !!b" or "a = -(-b)"
+ // We can thus infer "a = b"
+ AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0], context);
+ }
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void FactManager::DataSynonymAndIdEquationFacts::AddDataSynonymFactRecursive(
+ const protobufs::DataDescriptor& dd1, const protobufs::DataDescriptor& dd2,
+ opt::IRContext* context) {
+ assert(DataDescriptorsAreWellFormedAndComparable(context, dd1, dd2));
// Record that the data descriptors provided in the fact are equivalent.
- synonymous_.MakeEquivalent(fact.data1(), fact.data2());
+ synonymous_.MakeEquivalent(dd1, dd2);
// As we have updated the equivalence relation, we might be able to deduce
// more facts by performing a closure computation, so we record that such a
// computation is required; it will be performed next time a method answering
// a data synonym fact-related question is invoked.
- closure_computation_required = true;
+ closure_computation_required_ = true;
// We now check whether this is a synonym about composite objects. If it is,
// we can recursively add synonym facts about their associated sub-components.
@@ -428,9 +725,8 @@
// Get the type of the object referred to by the first data descriptor in the
// synonym fact.
uint32_t type_id = fuzzerutil::WalkCompositeTypeIndices(
- context,
- context->get_def_use_mgr()->GetDef(fact.data1().object())->type_id(),
- fact.data1().index());
+ context, context->get_def_use_mgr()->GetDef(dd1.object())->type_id(),
+ dd1.index());
auto type = context->get_type_mgr()->GetType(type_id);
auto type_instruction = context->get_def_use_mgr()->GetDef(type_id);
assert(type != nullptr &&
@@ -460,21 +756,19 @@
// obj_1[a_1, ..., a_m, i] == obj_2[b_1, ..., b_n, i]
for (uint32_t i = 0; i < num_composite_elements; i++) {
std::vector<uint32_t> extended_indices1 =
- fuzzerutil::RepeatedFieldToVector(fact.data1().index());
+ fuzzerutil::RepeatedFieldToVector(dd1.index());
extended_indices1.push_back(i);
std::vector<uint32_t> extended_indices2 =
- fuzzerutil::RepeatedFieldToVector(fact.data2().index());
+ fuzzerutil::RepeatedFieldToVector(dd2.index());
extended_indices2.push_back(i);
- protobufs::FactDataSynonym extended_data_synonym_fact;
- *extended_data_synonym_fact.mutable_data1() =
- MakeDataDescriptor(fact.data1().object(), std::move(extended_indices1));
- *extended_data_synonym_fact.mutable_data2() =
- MakeDataDescriptor(fact.data2().object(), std::move(extended_indices2));
- AddFactRecursive(extended_data_synonym_fact, context);
+ AddDataSynonymFactRecursive(
+ MakeDataDescriptor(dd1.object(), std::move(extended_indices1)),
+ MakeDataDescriptor(dd2.object(), std::move(extended_indices2)),
+ context);
}
}
-void FactManager::DataSynonymFacts::ComputeClosureOfFacts(
+void FactManager::DataSynonymAndIdEquationFacts::ComputeClosureOfFacts(
opt::IRContext* context) const {
// Suppose that obj_1[a_1, ..., a_m] and obj_2[b_1, ..., b_n] are distinct
// data descriptors that describe objects of the same composite type, and that
@@ -551,10 +845,10 @@
// We keep looking for new facts until we perform a complete pass over the
// equivalence relation without finding any new facts.
- while (closure_computation_required) {
+ while (closure_computation_required_) {
// We have not found any new facts yet during this pass; we set this to
// 'true' if we do find a new fact.
- closure_computation_required = false;
+ closure_computation_required_ = false;
// Consider each class in the equivalence relation.
for (auto representative :
@@ -738,7 +1032,7 @@
synonymous_.MakeEquivalent(dd1_prefix, dd2_prefix);
// As we have added a new synonym fact, we might benefit from doing
// another pass over the equivalence relation.
- closure_computation_required = true;
+ closure_computation_required_ = true;
// Now that we know this pair of data descriptors are synonymous,
// there is no point recording how close they are to being
// synonymous.
@@ -750,20 +1044,56 @@
}
}
-bool FactManager::DataSynonymFacts::DataDescriptorsAreWellFormedAndComparable(
- opt::IRContext* context, const protobufs::DataDescriptor& dd1,
- const protobufs::DataDescriptor& dd2) const {
- auto end_type_1 = fuzzerutil::WalkCompositeTypeIndices(
+bool FactManager::DataSynonymAndIdEquationFacts::
+ DataDescriptorsAreWellFormedAndComparable(
+ opt::IRContext* context, const protobufs::DataDescriptor& dd1,
+ const protobufs::DataDescriptor& dd2) const {
+ auto end_type_id_1 = fuzzerutil::WalkCompositeTypeIndices(
context, context->get_def_use_mgr()->GetDef(dd1.object())->type_id(),
dd1.index());
- auto end_type_2 = fuzzerutil::WalkCompositeTypeIndices(
+ auto end_type_id_2 = fuzzerutil::WalkCompositeTypeIndices(
context, context->get_def_use_mgr()->GetDef(dd2.object())->type_id(),
dd2.index());
- return end_type_1 && end_type_1 == end_type_2;
+ // The end types of the data descriptors must exist.
+ if (end_type_id_1 == 0 || end_type_id_2 == 0) {
+ return false;
+ }
+ // If the end types are the same, the data descriptors are comparable.
+ if (end_type_id_1 == end_type_id_2) {
+ return true;
+ }
+ // Otherwise they are only comparable if they are integer scalars or integer
+ // 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);
+
+ // 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()) {
+ return false;
+ }
+ if (type_1->AsVector()->element_count() !=
+ type_2->AsVector()->element_count()) {
+ return false;
+ }
+ type_1 = type_1->AsVector()->element_type();
+ type_2 = type_2->AsVector()->element_type();
+ }
+ // 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();
}
std::vector<const protobufs::DataDescriptor*>
-FactManager::DataSynonymFacts::GetSynonymsForDataDescriptor(
+FactManager::DataSynonymAndIdEquationFacts::GetSynonymsForDataDescriptor(
const protobufs::DataDescriptor& data_descriptor,
opt::IRContext* context) const {
ComputeClosureOfFacts(context);
@@ -774,7 +1104,7 @@
}
std::vector<uint32_t>
-FactManager::DataSynonymFacts ::GetIdsForWhichSynonymsAreKnown(
+FactManager::DataSynonymAndIdEquationFacts ::GetIdsForWhichSynonymsAreKnown(
opt::IRContext* context) const {
ComputeClosureOfFacts(context);
std::vector<uint32_t> result;
@@ -786,12 +1116,12 @@
return result;
}
-bool FactManager::DataSynonymFacts::IsSynonymous(
+bool FactManager::DataSynonymAndIdEquationFacts::IsSynonymous(
const protobufs::DataDescriptor& data_descriptor1,
const protobufs::DataDescriptor& data_descriptor2,
opt::IRContext* context) const {
- const_cast<FactManager::DataSynonymFacts*>(this)->ComputeClosureOfFacts(
- context);
+ const_cast<FactManager::DataSynonymAndIdEquationFacts*>(this)
+ ->ComputeClosureOfFacts(context);
return synonymous_.Exists(data_descriptor1) &&
synonymous_.Exists(data_descriptor2) &&
synonymous_.IsEquivalent(data_descriptor1, data_descriptor2);
@@ -891,7 +1221,8 @@
FactManager::FactManager()
: uniform_constant_facts_(MakeUnique<ConstantUniformFacts>()),
- data_synonym_facts_(MakeUnique<DataSynonymFacts>()),
+ data_synonym_and_id_equation_facts_(
+ MakeUnique<DataSynonymAndIdEquationFacts>()),
dead_block_facts_(MakeUnique<DeadBlockFacts>()),
livesafe_function_facts_(MakeUnique<LivesafeFunctionFacts>()),
irrelevant_pointee_value_facts_(
@@ -918,7 +1249,8 @@
return uniform_constant_facts_->AddFact(fact.constant_uniform_fact(),
context);
case protobufs::Fact::kDataSynonymFact:
- data_synonym_facts_->AddFact(fact.data_synonym_fact(), context);
+ data_synonym_and_id_equation_facts_->AddFact(fact.data_synonym_fact(),
+ context);
return true;
case protobufs::Fact::kBlockIsDeadFact:
dead_block_facts_->AddFact(fact.block_is_dead_fact());
@@ -938,7 +1270,7 @@
protobufs::FactDataSynonym fact;
*fact.mutable_data1() = data1;
*fact.mutable_data2() = data2;
- data_synonym_facts_->AddFact(fact, context);
+ data_synonym_and_id_equation_facts_->AddFact(fact, context);
}
std::vector<uint32_t> FactManager::GetConstantsAvailableFromUniformsForType(
@@ -973,15 +1305,16 @@
std::vector<uint32_t> FactManager::GetIdsForWhichSynonymsAreKnown(
opt::IRContext* context) const {
- return data_synonym_facts_->GetIdsForWhichSynonymsAreKnown(context);
+ return data_synonym_and_id_equation_facts_->GetIdsForWhichSynonymsAreKnown(
+ context);
}
std::vector<const protobufs::DataDescriptor*>
FactManager::GetSynonymsForDataDescriptor(
const protobufs::DataDescriptor& data_descriptor,
opt::IRContext* context) const {
- return data_synonym_facts_->GetSynonymsForDataDescriptor(data_descriptor,
- context);
+ return data_synonym_and_id_equation_facts_->GetSynonymsForDataDescriptor(
+ data_descriptor, context);
}
std::vector<const protobufs::DataDescriptor*> FactManager::GetSynonymsForId(
@@ -993,8 +1326,8 @@
const protobufs::DataDescriptor& data_descriptor1,
const protobufs::DataDescriptor& data_descriptor2,
opt::IRContext* context) const {
- return data_synonym_facts_->IsSynonymous(data_descriptor1, data_descriptor2,
- context);
+ return data_synonym_and_id_equation_facts_->IsSynonymous(
+ data_descriptor1, data_descriptor2, context);
}
bool FactManager::BlockIsDead(uint32_t block_id) const {
@@ -1027,5 +1360,17 @@
irrelevant_pointee_value_facts_->AddFact(fact);
}
+void FactManager::AddFactIdEquation(uint32_t lhs_id, SpvOp opcode,
+ const std::vector<uint32_t>& rhs_id,
+ opt::IRContext* context) {
+ protobufs::FactIdEquation fact;
+ fact.set_lhs_id(lhs_id);
+ fact.set_opcode(opcode);
+ for (auto an_rhs_id : rhs_id) {
+ fact.add_rhs_id(an_rhs_id);
+ }
+ data_synonym_and_id_equation_facts_->AddFact(fact, context);
+}
+
} // namespace fuzz
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fact_manager.h b/third_party/SPIRV-Tools/source/fuzz/fact_manager.h
index 55cbfa0..f80d677 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fact_manager.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fact_manager.h
@@ -68,6 +68,14 @@
// is irrelevant: it does not affect the observable behaviour of the module.
void AddFactValueOfPointeeIsIrrelevant(uint32_t pointer_id);
+ // Records the fact that |lhs_id| is defined by the equation:
+ //
+ // |lhs_id| = |opcode| |rhs_id[0]| ... |rhs_id[N-1]|
+ //
+ void AddFactIdEquation(uint32_t lhs_id, SpvOp opcode,
+ const std::vector<uint32_t>& rhs_id,
+ opt::IRContext* context);
+
// The fact manager is responsible for managing a few distinct categories of
// facts. In principle there could be different fact managers for each kind
// of fact, but in practice providing one 'go to' place for facts is
@@ -179,9 +187,10 @@
std::unique_ptr<ConstantUniformFacts>
uniform_constant_facts_; // Unique pointer to internal data.
- class DataSynonymFacts; // Opaque class for management of data synonym facts.
- std::unique_ptr<DataSynonymFacts>
- data_synonym_facts_; // Unique pointer to internal data.
+ class DataSynonymAndIdEquationFacts; // Opaque class for management of data
+ // synonym and id equation facts.
+ std::unique_ptr<DataSynonymAndIdEquationFacts>
+ data_synonym_and_id_equation_facts_; // Unique pointer to internal data.
class DeadBlockFacts; // Opaque class for management of dead block facts.
std::unique_ptr<DeadBlockFacts>
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer.cpp
index 6c2821c..119bd3c 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer.cpp
@@ -26,6 +26,7 @@
#include "source/fuzz/fuzzer_pass_add_dead_blocks.h"
#include "source/fuzz/fuzzer_pass_add_dead_breaks.h"
#include "source/fuzz/fuzzer_pass_add_dead_continues.h"
+#include "source/fuzz/fuzzer_pass_add_equation_instructions.h"
#include "source/fuzz/fuzzer_pass_add_function_calls.h"
#include "source/fuzz/fuzzer_pass_add_global_variables.h"
#include "source/fuzz/fuzzer_pass_add_loads.h"
@@ -44,7 +45,10 @@
#include "source/fuzz/fuzzer_pass_obfuscate_constants.h"
#include "source/fuzz/fuzzer_pass_outline_functions.h"
#include "source/fuzz/fuzzer_pass_permute_blocks.h"
+#include "source/fuzz/fuzzer_pass_permute_function_parameters.h"
#include "source/fuzz/fuzzer_pass_split_blocks.h"
+#include "source/fuzz/fuzzer_pass_swap_commutable_operands.h"
+#include "source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h"
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
#include "source/fuzz/pseudo_random_generator.h"
#include "source/opt/build_module.h"
@@ -200,6 +204,9 @@
MaybeAddPass<FuzzerPassAddDeadContinues>(&passes, ir_context.get(),
&fact_manager, &fuzzer_context,
transformation_sequence_out);
+ MaybeAddPass<FuzzerPassAddEquationInstructions>(
+ &passes, ir_context.get(), &fact_manager, &fuzzer_context,
+ transformation_sequence_out);
MaybeAddPass<FuzzerPassAddFunctionCalls>(&passes, ir_context.get(),
&fact_manager, &fuzzer_context,
transformation_sequence_out);
@@ -239,6 +246,9 @@
MaybeAddPass<FuzzerPassPermuteBlocks>(&passes, ir_context.get(),
&fact_manager, &fuzzer_context,
transformation_sequence_out);
+ MaybeAddPass<FuzzerPassPermuteFunctionParameters>(
+ &passes, ir_context.get(), &fact_manager, &fuzzer_context,
+ transformation_sequence_out);
MaybeAddPass<FuzzerPassSplitBlocks>(&passes, ir_context.get(),
&fact_manager, &fuzzer_context,
transformation_sequence_out);
@@ -276,6 +286,12 @@
MaybeAddPass<FuzzerPassAddNoContractionDecorations>(
&final_passes, ir_context.get(), &fact_manager, &fuzzer_context,
transformation_sequence_out);
+ MaybeAddPass<FuzzerPassSwapCommutableOperands>(
+ &final_passes, ir_context.get(), &fact_manager, &fuzzer_context,
+ transformation_sequence_out);
+ MaybeAddPass<FuzzerPassToggleAccessChainInstruction>(
+ &final_passes, ir_context.get(), &fact_manager, &fuzzer_context,
+ transformation_sequence_out);
for (auto& pass : final_passes) {
if (!impl_->ApplyPassAndCheckValidity(pass.get(), *ir_context, tools)) {
return Fuzzer::FuzzerResultStatus::kFuzzerPassLedToInvalidModule;
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.cpp
index 1265772..2f9fc5a 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.cpp
@@ -30,6 +30,8 @@
const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadBlock = {20, 90};
const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadBreak = {5, 80};
const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadContinue = {5, 80};
+const std::pair<uint32_t, uint32_t> kChanceOfAddingEquationInstruction = {5,
+ 90};
const std::pair<uint32_t, uint32_t> kChanceOfAddingGlobalVariable = {20, 90};
const std::pair<uint32_t, uint32_t> kChanceOfAddingLoad = {5, 50};
const std::pair<uint32_t, uint32_t> kChanceOfAddingLocalVariable = {20, 90};
@@ -58,8 +60,11 @@
const std::pair<uint32_t, uint32_t> kChanceOfMovingBlockDown = {20, 50};
const std::pair<uint32_t, uint32_t> kChanceOfObfuscatingConstant = {10, 90};
const std::pair<uint32_t, uint32_t> kChanceOfOutliningFunction = {10, 90};
+const std::pair<uint32_t, uint32_t> kChanceOfPermutingParameters = {30, 90};
const std::pair<uint32_t, uint32_t> kChanceOfReplacingIdWithSynonym = {10, 90};
const std::pair<uint32_t, uint32_t> kChanceOfSplittingBlock = {40, 95};
+const std::pair<uint32_t, uint32_t> kChanceOfTogglingAccessChainInstruction = {
+ 20, 90};
// Default limits for various quantities that are chosen during fuzzing.
// Keep them in alphabetical order.
@@ -102,6 +107,8 @@
ChooseBetweenMinAndMax(kChanceOfAddingDeadBreak);
chance_of_adding_dead_continue_ =
ChooseBetweenMinAndMax(kChanceOfAddingDeadContinue);
+ chance_of_adding_equation_instruction_ =
+ ChooseBetweenMinAndMax(kChanceOfAddingEquationInstruction);
chance_of_adding_global_variable_ =
ChooseBetweenMinAndMax(kChanceOfAddingGlobalVariable);
chance_of_adding_load_ = ChooseBetweenMinAndMax(kChanceOfAddingLoad);
@@ -142,9 +149,13 @@
ChooseBetweenMinAndMax(kChanceOfObfuscatingConstant);
chance_of_outlining_function_ =
ChooseBetweenMinAndMax(kChanceOfOutliningFunction);
+ chance_of_permuting_parameters_ =
+ ChooseBetweenMinAndMax(kChanceOfPermutingParameters);
chance_of_replacing_id_with_synonym_ =
ChooseBetweenMinAndMax(kChanceOfReplacingIdWithSynonym);
chance_of_splitting_block_ = ChooseBetweenMinAndMax(kChanceOfSplittingBlock);
+ chance_of_toggling_access_chain_instruction_ =
+ ChooseBetweenMinAndMax(kChanceOfTogglingAccessChainInstruction);
}
FuzzerContext::~FuzzerContext() = default;
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.h
index 23127ff..1529705 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_context.h
@@ -62,6 +62,40 @@
return result;
}
+ // Randomly shuffles a |sequence| between |lo| and |hi| indices inclusively.
+ // |lo| and |hi| must be valid indices to the |sequence|
+ template <typename T>
+ void Shuffle(std::vector<T>* sequence, size_t lo, size_t hi) const {
+ auto& array = *sequence;
+
+ if (array.empty()) {
+ return;
+ }
+
+ assert(lo <= hi && hi < array.size() && "lo and/or hi indices are invalid");
+
+ // i > lo to account for potential infinite loop when lo == 0
+ for (size_t i = hi; i > lo; --i) {
+ auto index =
+ random_generator_->RandomUint32(static_cast<uint32_t>(i - lo + 1));
+
+ if (lo + index != i) {
+ // Introduce std::swap to the scope but don't use it
+ // directly since there might be a better overload
+ using std::swap;
+ swap(array[lo + index], array[i]);
+ }
+ }
+ }
+
+ // Ramdomly shuffles a |sequence|
+ template <typename T>
+ void Shuffle(std::vector<T>* sequence) const {
+ if (!sequence->empty()) {
+ Shuffle(sequence, 0, sequence->size() - 1);
+ }
+ }
+
// Yields an id that is guaranteed not to be used in the module being fuzzed,
// or to have been issued before.
uint32_t GetFreshId();
@@ -82,6 +116,9 @@
uint32_t GetChanceOfAddingDeadContinue() {
return chance_of_adding_dead_continue_;
}
+ uint32_t GetChanceOfAddingEquationInstruction() {
+ return chance_of_adding_equation_instruction_;
+ }
uint32_t GetChanceOfAddingGlobalVariable() {
return chance_of_adding_global_variable_;
}
@@ -136,10 +173,16 @@
uint32_t GetChanceOfOutliningFunction() {
return chance_of_outlining_function_;
}
+ uint32_t GetChanceOfPermutingParameters() {
+ return chance_of_permuting_parameters_;
+ }
uint32_t GetChanceOfReplacingIdWithSynonym() {
return chance_of_replacing_id_with_synonym_;
}
uint32_t GetChanceOfSplittingBlock() { return chance_of_splitting_block_; }
+ uint32_t GetChanceOfTogglingAccessChainInstruction() {
+ return chance_of_toggling_access_chain_instruction_;
+ }
uint32_t GetRandomLoopControlPeelCount() {
return random_generator_->RandomUint32(max_loop_control_peel_count_);
}
@@ -177,6 +220,7 @@
uint32_t chance_of_adding_dead_block_;
uint32_t chance_of_adding_dead_break_;
uint32_t chance_of_adding_dead_continue_;
+ uint32_t chance_of_adding_equation_instruction_;
uint32_t chance_of_adding_global_variable_;
uint32_t chance_of_adding_load_;
uint32_t chance_of_adding_local_variable_;
@@ -199,8 +243,10 @@
uint32_t chance_of_moving_block_down_;
uint32_t chance_of_obfuscating_constant_;
uint32_t chance_of_outlining_function_;
+ uint32_t chance_of_permuting_parameters_;
uint32_t chance_of_replacing_id_with_synonym_;
uint32_t chance_of_splitting_block_;
+ uint32_t chance_of_toggling_access_chain_instruction_;
// Limits associated with various quantities for which random values are
// chosen during fuzzing.
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.cpp
index 4a22a21..a76f10d 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.cpp
@@ -22,6 +22,7 @@
#include "source/fuzz/transformation_add_global_undef.h"
#include "source/fuzz/transformation_add_type_boolean.h"
#include "source/fuzz/transformation_add_type_float.h"
+#include "source/fuzz/transformation_add_type_function.h"
#include "source/fuzz/transformation_add_type_int.h"
#include "source/fuzz/transformation_add_type_matrix.h"
#include "source/fuzz/transformation_add_type_pointer.h"
@@ -89,12 +90,12 @@
return result;
}
-void FuzzerPass::MaybeAddTransformationBeforeEachInstruction(
+void FuzzerPass::ForEachInstructionWithInstructionDescriptor(
std::function<
void(opt::Function* function, opt::BasicBlock* block,
opt::BasicBlock::iterator inst_it,
const protobufs::InstructionDescriptor& instruction_descriptor)>
- maybe_apply_transformation) {
+ action) {
// Consider every block in every function.
for (auto& function : *GetIRContext()->module()) {
for (auto& block : function) {
@@ -132,11 +133,10 @@
const SpvOp opcode = inst_it->opcode();
// Invoke the provided function, which might apply a transformation.
- maybe_apply_transformation(
- &function, &block, inst_it,
- MakeInstructionDescriptor(
- base, opcode,
- skip_count.count(opcode) ? skip_count.at(opcode) : 0));
+ action(&function, &block, inst_it,
+ MakeInstructionDescriptor(
+ base, opcode,
+ skip_count.count(opcode) ? skip_count.at(opcode) : 0));
if (!inst_it->HasResultId()) {
skip_count[opcode] =
@@ -180,6 +180,26 @@
return result;
}
+uint32_t FuzzerPass::FindOrCreateFunctionType(
+ uint32_t return_type_id, const std::vector<uint32_t>& argument_id) {
+ // FindFunctionType has a sigle argument for OpTypeFunction operands
+ // so we will have to copy them all in this vector
+ std::vector<uint32_t> type_ids(argument_id.size() + 1);
+ type_ids[0] = return_type_id;
+ std::copy(argument_id.begin(), argument_id.end(), type_ids.begin() + 1);
+
+ // Check if type exists
+ auto existing_id = fuzzerutil::FindFunctionType(GetIRContext(), type_ids);
+ if (existing_id) {
+ return existing_id;
+ }
+
+ auto result = GetFuzzerContext()->GetFreshId();
+ ApplyTransformation(
+ TransformationAddTypeFunction(result, return_type_id, argument_id));
+ return result;
+}
+
uint32_t FuzzerPass::FindOrCreateVectorType(uint32_t component_type_id,
uint32_t component_count) {
assert(component_count >= 2 && component_count <= 4 &&
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.h
index 7052685..46ee408 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass.h
@@ -70,24 +70,24 @@
// all times tracking an instruction descriptor that allows the latest
// instruction to be located even if it has no result id.
//
- // The code to manipulate the instruction descriptor is a bit fiddly, and the
+ // The code to manipulate the instruction descriptor is a bit fiddly. The
// point of this method is to avoiding having to duplicate it in multiple
// transformation passes.
//
- // The function |maybe_apply_transformation| is invoked for each instruction
- // |inst_it| in block |block| of function |function| that is encountered. The
+ // The function |action| is invoked for each instruction |inst_it| in block
+ // |block| of function |function| that is encountered. The
// |instruction_descriptor| parameter to the function object allows |inst_it|
// to be identified.
//
- // The job of |maybe_apply_transformation| is to randomly decide whether to
- // try to apply some transformation, and then - if selected - to attempt to
- // apply it.
- void MaybeAddTransformationBeforeEachInstruction(
+ // In most intended use cases, the job of |action| is to randomly decide
+ // whether to try to apply some transformation, and then - if selected - to
+ // attempt to apply it.
+ void ForEachInstructionWithInstructionDescriptor(
std::function<
void(opt::Function* function, opt::BasicBlock* block,
opt::BasicBlock::iterator inst_it,
const protobufs::InstructionDescriptor& instruction_descriptor)>
- maybe_apply_transformation);
+ action);
// A generic helper for applying a transformation that should be applicable
// by construction, and adding it to the sequence of applied transformations.
@@ -112,6 +112,12 @@
// instruction does not exist, a transformation is applied to add it.
uint32_t FindOrCreate32BitFloatType();
+ // Returns the id of an OpTypeFunction %<return_type_id> %<...argument_id>
+ // instruction. If such an instruction doesn't exist, a transformation
+ // is applied to create a new one.
+ uint32_t FindOrCreateFunctionType(uint32_t return_type_id,
+ const std::vector<uint32_t>& argument_id);
+
// Returns the id of an OpTypeVector instruction, with |component_type_id|
// (which must already exist) as its base type, and |component_count|
// elements (which must be in the range [2, 4]). If such an instruction does
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_access_chains.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_access_chains.cpp
index 11f368e..cfc2812 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_access_chains.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_access_chains.cpp
@@ -29,7 +29,7 @@
FuzzerPassAddAccessChains::~FuzzerPassAddAccessChains() = default;
void FuzzerPassAddAccessChains::Apply() {
- MaybeAddTransformationBeforeEachInstruction(
+ ForEachInstructionWithInstructionDescriptor(
[this](opt::Function* function, opt::BasicBlock* block,
opt::BasicBlock::iterator inst_it,
const protobufs::InstructionDescriptor& instruction_descriptor)
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_breaks.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_breaks.cpp
index fa6b098..aefc2fc 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_breaks.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_breaks.cpp
@@ -13,7 +13,7 @@
// limitations under the License.
#include "source/fuzz/fuzzer_pass_add_dead_breaks.h"
-
+#include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/transformation_add_dead_break.h"
#include "source/opt/ir_context.h"
@@ -34,11 +34,16 @@
// We consider each function separately.
for (auto& function : *GetIRContext()->module()) {
// For a given function, we find all the merge blocks in that function.
- std::vector<uint32_t> merge_block_ids;
+ std::vector<opt::BasicBlock*> merge_blocks;
for (auto& block : function) {
auto maybe_merge_id = block.MergeBlockIdIfAny();
if (maybe_merge_id) {
- merge_block_ids.push_back(maybe_merge_id);
+ auto merge_block =
+ fuzzerutil::MaybeFindBlock(GetIRContext(), maybe_merge_id);
+
+ assert(merge_block && "Merge block can't be null");
+
+ merge_blocks.push_back(merge_block);
}
}
// We rather aggressively consider the possibility of adding a break from
@@ -46,12 +51,34 @@
// inapplicable as they would be illegal. That's OK - we later discard the
// ones that turn out to be no good.
for (auto& block : function) {
- for (auto merge_block_id : merge_block_ids) {
- // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2856): right
- // now we completely ignore OpPhi instructions at merge blocks. This
- // will lead to interesting opportunities being missed.
+ for (auto* merge_block : merge_blocks) {
+ // Populate this vector with ids that are available at the branch point
+ // of this basic block. We will use these ids to update OpPhi
+ // instructions later.
+ std::vector<uint32_t> phi_ids;
+
+ // Determine how we need to adjust OpPhi instructions' operands
+ // for this transformation to be valid.
+ //
+ // If |block| has a branch to |merge_block|, the latter must have all of
+ // its OpPhi instructions set up correctly - we don't need to adjust
+ // anything.
+ if (!block.IsSuccessor(merge_block)) {
+ merge_block->ForEachPhiInst([this, &phi_ids](opt::Instruction* phi) {
+ // Add an additional operand for OpPhi instruction.
+ //
+ // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177):
+ // If we have a way to communicate to the fact manager
+ // that a specific id use is irrelevant and could be replaced with
+ // something else, we should add such a fact about the zero
+ // provided as an OpPhi operand
+ phi_ids.push_back(FindOrCreateZeroConstant(phi->type_id()));
+ });
+ }
+
auto candidate_transformation = TransformationAddDeadBreak(
- block.id(), merge_block_id, GetFuzzerContext()->ChooseEven(), {});
+ block.id(), merge_block->id(), GetFuzzerContext()->ChooseEven(),
+ std::move(phi_ids));
if (candidate_transformation.IsApplicable(GetIRContext(),
*GetFactManager())) {
// Only consider a transformation as a candidate if it is applicable.
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_continues.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_continues.cpp
index 51bcb91..852df3d 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_continues.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_dead_continues.cpp
@@ -13,7 +13,7 @@
// limitations under the License.
#include "source/fuzz/fuzzer_pass_add_dead_continues.h"
-
+#include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/transformation_add_dead_continue.h"
#include "source/opt/ir_context.h"
@@ -32,15 +32,46 @@
// Consider every block in every function.
for (auto& function : *GetIRContext()->module()) {
for (auto& block : function) {
+ // Get the label id of the continue target of the innermost loop.
+ auto continue_block_id =
+ block.IsLoopHeader()
+ ? block.ContinueBlockId()
+ : GetIRContext()->GetStructuredCFGAnalysis()->LoopContinueBlock(
+ block.id());
+
+ // This transformation is not applicable if current block is not inside a
+ // loop.
+ if (continue_block_id == 0) {
+ continue;
+ }
+
+ auto* continue_block =
+ fuzzerutil::MaybeFindBlock(GetIRContext(), continue_block_id);
+ assert(continue_block && "Continue block is null");
+
+ // Analyze return type of each OpPhi instruction in the continue target
+ // and provide an id for the transformation if needed.
+ std::vector<uint32_t> phi_ids;
+ // Check whether current block has an edge to the continue target.
+ // If this is the case, we don't need to do anything.
+ if (!block.IsSuccessor(continue_block)) {
+ continue_block->ForEachPhiInst([this, &phi_ids](opt::Instruction* phi) {
+ // Add an additional operand for OpPhi instruction.
+ //
+ // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177):
+ // If we have a way to communicate to the fact manager
+ // that a specific id use is irrelevant and could be replaced with
+ // something else, we should add such a fact about the zero
+ // provided as an OpPhi operand
+ phi_ids.push_back(FindOrCreateZeroConstant(phi->type_id()));
+ });
+ }
+
// Make a transformation to add a dead continue from this node; if the
// node turns out to be inappropriate (e.g. by not being in a loop) the
// precondition for the transformation will fail and it will be ignored.
- //
- // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2856): right
- // now we completely ignore OpPhi instructions at continue targets.
- // This will lead to interesting opportunities being missed.
auto candidate_transformation = TransformationAddDeadContinue(
- block.id(), GetFuzzerContext()->ChooseEven(), {});
+ block.id(), GetFuzzerContext()->ChooseEven(), std::move(phi_ids));
// Probabilistically decide whether to apply the transformation in the
// case that it is applicable.
if (candidate_transformation.IsApplicable(GetIRContext(),
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_equation_instructions.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_equation_instructions.cpp
new file mode 100644
index 0000000..7f34344
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_equation_instructions.cpp
@@ -0,0 +1,238 @@
+// 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_add_equation_instructions.h"
+
+#include <vector>
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_equation_instruction.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAddEquationInstructions::FuzzerPassAddEquationInstructions(
+ opt::IRContext* ir_context, FactManager* fact_manager,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations)
+ : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassAddEquationInstructions::~FuzzerPassAddEquationInstructions() =
+ default;
+
+void FuzzerPassAddEquationInstructions::Apply() {
+ ForEachInstructionWithInstructionDescriptor(
+ [this](opt::Function* function, opt::BasicBlock* block,
+ opt::BasicBlock::iterator inst_it,
+ const protobufs::InstructionDescriptor& instruction_descriptor) {
+ if (!GetFuzzerContext()->ChoosePercentage(
+ GetFuzzerContext()->GetChanceOfAddingEquationInstruction())) {
+ return;
+ }
+
+ // Check that it is OK to add an equation instruction before the given
+ // instruction in principle - e.g. check that this does not lead to
+ // inserting before an OpVariable or OpPhi instruction. We use OpIAdd
+ // as an example opcode for this check, to be representative of *some*
+ // opcode that defines an equation, even though we may choose a
+ // different opcode below.
+ if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpIAdd, inst_it)) {
+ return;
+ }
+
+ // Get all available instructions with result ids and types that are not
+ // OpUndef.
+ std::vector<opt::Instruction*> available_instructions =
+ FindAvailableInstructions(
+ function, block, inst_it,
+ [](opt::IRContext*, opt::Instruction* instruction) -> bool {
+ return instruction->result_id() && instruction->type_id() &&
+ instruction->opcode() != SpvOpUndef;
+ });
+
+ // Try the opcodes for which we know how to make ids at random until
+ // something works.
+ std::vector<SpvOp> candidate_opcodes = {SpvOpIAdd, SpvOpISub,
+ SpvOpLogicalNot, SpvOpSNegate};
+ do {
+ auto opcode =
+ GetFuzzerContext()->RemoveAtRandomIndex(&candidate_opcodes);
+ switch (opcode) {
+ case SpvOpIAdd:
+ case SpvOpISub: {
+ // Instructions of integer (scalar or vector) result type are
+ // suitable for these opcodes.
+ auto integer_instructions =
+ GetIntegerInstructions(available_instructions);
+ if (!integer_instructions.empty()) {
+ // There is at least one such instruction, so pick one at random
+ // for the LHS of an equation.
+ auto lhs = integer_instructions.at(
+ GetFuzzerContext()->RandomIndex(integer_instructions));
+
+ // For the RHS, we can use any instruction with an integer
+ // scalar/vector result type of the same number of components
+ // and the same bit-width for the underlying integer type.
+
+ // Work out the element count and bit-width.
+ auto lhs_type =
+ GetIRContext()->get_type_mgr()->GetType(lhs->type_id());
+ uint32_t lhs_element_count;
+ uint32_t lhs_bit_width;
+ if (lhs_type->AsVector()) {
+ lhs_element_count = lhs_type->AsVector()->element_count();
+ lhs_bit_width = lhs_type->AsVector()
+ ->element_type()
+ ->AsInteger()
+ ->width();
+ } else {
+ lhs_element_count = 1;
+ lhs_bit_width = lhs_type->AsInteger()->width();
+ }
+
+ // Get all the instructions that match on element count and
+ // bit-width.
+ auto candidate_rhs_instructions = RestrictToElementBitWidth(
+ RestrictToVectorWidth(integer_instructions,
+ lhs_element_count),
+ lhs_bit_width);
+
+ // Choose a RHS instruction at random; there is guaranteed to
+ // be at least one choice as the LHS will be available.
+ auto rhs = candidate_rhs_instructions.at(
+ GetFuzzerContext()->RandomIndex(
+ candidate_rhs_instructions));
+
+ // Add the equation instruction.
+ ApplyTransformation(TransformationEquationInstruction(
+ GetFuzzerContext()->GetFreshId(), opcode,
+ {lhs->result_id(), rhs->result_id()},
+ instruction_descriptor));
+ return;
+ }
+ break;
+ }
+ case SpvOpLogicalNot: {
+ // Choose any available instruction of boolean scalar/vector
+ // result type and equate its negation with a fresh id.
+ auto boolean_instructions =
+ GetBooleanInstructions(available_instructions);
+ if (!boolean_instructions.empty()) {
+ ApplyTransformation(TransformationEquationInstruction(
+ GetFuzzerContext()->GetFreshId(), opcode,
+ {boolean_instructions
+ .at(GetFuzzerContext()->RandomIndex(
+ boolean_instructions))
+ ->result_id()},
+ instruction_descriptor));
+ return;
+ }
+ break;
+ }
+ case SpvOpSNegate: {
+ // Similar to OpLogicalNot, but for signed integer negation.
+ auto integer_instructions =
+ GetIntegerInstructions(available_instructions);
+ if (!integer_instructions.empty()) {
+ ApplyTransformation(TransformationEquationInstruction(
+ GetFuzzerContext()->GetFreshId(), opcode,
+ {integer_instructions
+ .at(GetFuzzerContext()->RandomIndex(
+ integer_instructions))
+ ->result_id()},
+ instruction_descriptor));
+ return;
+ }
+ break;
+ }
+ default:
+ assert(false && "Unexpected opcode.");
+ break;
+ }
+ } while (!candidate_opcodes.empty());
+ // Reaching here means that we did not manage to apply any
+ // transformation at this point of the module.
+ });
+}
+
+std::vector<opt::Instruction*>
+FuzzerPassAddEquationInstructions::GetIntegerInstructions(
+ const std::vector<opt::Instruction*>& instructions) const {
+ std::vector<opt::Instruction*> result;
+ for (auto& inst : instructions) {
+ auto type = GetIRContext()->get_type_mgr()->GetType(inst->type_id());
+ if (type->AsInteger() ||
+ (type->AsVector() && type->AsVector()->element_type()->AsInteger())) {
+ result.push_back(inst);
+ }
+ }
+ return result;
+}
+
+std::vector<opt::Instruction*>
+FuzzerPassAddEquationInstructions::GetBooleanInstructions(
+ const std::vector<opt::Instruction*>& instructions) const {
+ std::vector<opt::Instruction*> result;
+ for (auto& inst : instructions) {
+ auto type = GetIRContext()->get_type_mgr()->GetType(inst->type_id());
+ if (type->AsBool() ||
+ (type->AsVector() && type->AsVector()->element_type()->AsBool())) {
+ result.push_back(inst);
+ }
+ }
+ return result;
+}
+
+std::vector<opt::Instruction*>
+FuzzerPassAddEquationInstructions::RestrictToVectorWidth(
+ const std::vector<opt::Instruction*>& instructions,
+ uint32_t vector_width) const {
+ std::vector<opt::Instruction*> result;
+ for (auto& inst : instructions) {
+ auto type = GetIRContext()->get_type_mgr()->GetType(inst->type_id());
+ // Get the vector width of |inst|, which is 1 if |inst| is a scalar and is
+ // otherwise derived from its vector type.
+ uint32_t other_vector_width =
+ type->AsVector() ? type->AsVector()->element_count() : 1;
+ // Keep |inst| if the vector widths match.
+ if (vector_width == other_vector_width) {
+ result.push_back(inst);
+ }
+ }
+ return result;
+}
+
+std::vector<opt::Instruction*>
+FuzzerPassAddEquationInstructions::RestrictToElementBitWidth(
+ const std::vector<opt::Instruction*>& instructions,
+ uint32_t bit_width) const {
+ std::vector<opt::Instruction*> result;
+ for (auto& inst : instructions) {
+ const opt::analysis::Type* type =
+ GetIRContext()->get_type_mgr()->GetType(inst->type_id());
+ if (type->AsVector()) {
+ type = type->AsVector()->element_type();
+ }
+ assert(type->AsInteger() &&
+ "Precondition: all input instructions must "
+ "have integer scalar or vector type.");
+ if (type->AsInteger()->width() == bit_width) {
+ result.push_back(inst);
+ }
+ }
+ return result;
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_equation_instructions.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_equation_instructions.h
new file mode 100644
index 0000000..84229c0
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_equation_instructions.h
@@ -0,0 +1,67 @@
+// 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_ADD_EQUATION_INSTRUCTIONS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_ADD_EQUATION_INSTRUCTIONS_H_
+
+#include <vector>
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Fuzzer pass that sprinkles instructions through the module that define
+// equations using various arithmetic and logical operators.
+class FuzzerPassAddEquationInstructions : public FuzzerPass {
+ public:
+ FuzzerPassAddEquationInstructions(
+ opt::IRContext* ir_context, FactManager* fact_manager,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations);
+
+ ~FuzzerPassAddEquationInstructions();
+
+ void Apply() override;
+
+ private:
+ // Yields those instructions in |instructions| that have integer scalar or
+ // vector result type.
+ std::vector<opt::Instruction*> GetIntegerInstructions(
+ const std::vector<opt::Instruction*>& instructions) const;
+
+ // Yields those instructions in |instructions| that have boolean scalar or
+ // vector result type.
+ std::vector<opt::Instruction*> GetBooleanInstructions(
+ const std::vector<opt::Instruction*>& instructions) const;
+
+ // Requires that |instructions| are scalars or vectors of some type. Returns
+ // only those instructions whose width is |width|. If |width| is 1 this means
+ // the scalars.
+ std::vector<opt::Instruction*> RestrictToVectorWidth(
+ const std::vector<opt::Instruction*>& instructions,
+ uint32_t vector_width) const;
+
+ // Requires that |instructions| are integer scalars or vectors. Returns only
+ // those instructions for which the bit-width of the underlying integer type
+ // is |bit_width|.
+ std::vector<opt::Instruction*> RestrictToElementBitWidth(
+ const std::vector<opt::Instruction*>& instructions,
+ uint32_t bit_width) const;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_EQUATION_INSTRUCTIONS_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_function_calls.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_function_calls.cpp
index c89ae51..545aa16 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_function_calls.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_function_calls.cpp
@@ -32,7 +32,7 @@
FuzzerPassAddFunctionCalls::~FuzzerPassAddFunctionCalls() = default;
void FuzzerPassAddFunctionCalls::Apply() {
- MaybeAddTransformationBeforeEachInstruction(
+ ForEachInstructionWithInstructionDescriptor(
[this](opt::Function* function, opt::BasicBlock* block,
opt::BasicBlock::iterator inst_it,
const protobufs::InstructionDescriptor& instruction_descriptor)
@@ -62,8 +62,8 @@
std::vector<opt::Function*> candidate_functions;
for (auto& other_function : *GetIRContext()->module()) {
if (&other_function != function &&
- !TransformationFunctionCall::FunctionIsEntryPoint(
- GetIRContext(), other_function.result_id())) {
+ !fuzzerutil::FunctionIsEntryPoint(GetIRContext(),
+ other_function.result_id())) {
candidate_functions.push_back(&other_function);
}
}
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_loads.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_loads.cpp
index 2fe1220..851787f 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_loads.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_loads.cpp
@@ -29,7 +29,7 @@
FuzzerPassAddLoads::~FuzzerPassAddLoads() = default;
void FuzzerPassAddLoads::Apply() {
- MaybeAddTransformationBeforeEachInstruction(
+ ForEachInstructionWithInstructionDescriptor(
[this](opt::Function* function, opt::BasicBlock* block,
opt::BasicBlock::iterator inst_it,
const protobufs::InstructionDescriptor& instruction_descriptor)
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp
index ead8c5c..82fb539 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp
@@ -44,12 +44,7 @@
->GetChanceOfAddingNoContractionDecoration())) {
TransformationAddNoContractionDecoration transformation(
inst.result_id());
- assert(transformation.IsApplicable(GetIRContext(),
- *GetFactManager()) &&
- "Transformation should be applicable by construction.");
- transformation.Apply(GetIRContext(), GetFactManager());
- *GetTransformations()->add_transformation() =
- transformation.ToMessage();
+ ApplyTransformation(transformation);
}
}
}
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_stores.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_stores.cpp
index d2c7b3d..794ddc3 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_stores.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_add_stores.cpp
@@ -29,7 +29,7 @@
FuzzerPassAddStores::~FuzzerPassAddStores() = default;
void FuzzerPassAddStores::Apply() {
- MaybeAddTransformationBeforeEachInstruction(
+ ForEachInstructionWithInstructionDescriptor(
[this](opt::Function* function, opt::BasicBlock* block,
opt::BasicBlock::iterator inst_it,
const protobufs::InstructionDescriptor& instruction_descriptor)
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_function_controls.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_function_controls.cpp
index 2a11988..fe229bc 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_function_controls.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_function_controls.cpp
@@ -61,10 +61,7 @@
// Create and add a transformation.
TransformationSetFunctionControl transformation(
function.DefInst().result_id(), new_function_control_mask);
- assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
- "Transformation should be applicable by construction.");
- transformation.Apply(GetIRContext(), GetFactManager());
- *GetTransformations()->add_transformation() = transformation.ToMessage();
+ ApplyTransformation(transformation);
}
}
}
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp
index ac2408a..c9843d0 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp
@@ -107,11 +107,7 @@
// sequence.
TransformationSetLoopControl transformation(block.id(), new_mask,
peel_count, partial_count);
- assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
- "Transformation should be applicable by construction.");
- transformation.Apply(GetIRContext(), GetFactManager());
- *GetTransformations()->add_transformation() =
- transformation.ToMessage();
+ ApplyTransformation(transformation);
}
}
}
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp
index a9d4b32..2d3d676 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp
@@ -97,12 +97,7 @@
TransformationSetMemoryOperandsMask transformation(
MakeInstructionDescriptor(block, inst_it), new_mask, mask_index);
- assert(
- transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
- "Transformation should be applicable by construction.");
- transformation.Apply(GetIRContext(), GetFactManager());
- *GetTransformations()->add_transformation() =
- transformation.ToMessage();
+ ApplyTransformation(transformation);
}
}
}
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp
index 22654f2..397dfed 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp
@@ -62,11 +62,7 @@
// sequence.
TransformationSetSelectionControl transformation(
block.id(), choices[GetFuzzerContext()->RandomIndex(choices)]);
- assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
- "Transformation should be applicable by construction.");
- transformation.Apply(GetIRContext(), GetFactManager());
- *GetTransformations()->add_transformation() =
- transformation.ToMessage();
+ ApplyTransformation(transformation);
}
}
}
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp
index e932017..5711f35 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp
@@ -81,19 +81,19 @@
synonyms_to_try.push_back(data_descriptor);
}
while (!synonyms_to_try.empty()) {
- auto synonym_index = GetFuzzerContext()->RandomIndex(synonyms_to_try);
- auto synonym_to_try = synonyms_to_try[synonym_index];
- synonyms_to_try.erase(synonyms_to_try.begin() + synonym_index);
+ auto synonym_to_try =
+ GetFuzzerContext()->RemoveAtRandomIndex(&synonyms_to_try);
+ // If the synonym's |index_size| is zero, the synonym represents an id.
+ // Otherwise it represents some element of a composite structure, in
+ // which case we need to be able to add an extract instruction to get
+ // that element out.
if (synonym_to_try->index_size() > 0 &&
- use_inst->opcode() == SpvOpPhi) {
- // We are trying to replace an operand to an OpPhi. This means
- // we cannot use a composite synonym, because that requires
- // extracting a component from a composite and we cannot insert
- // an extract instruction before an OpPhi.
- //
- // TODO(afd): We could consider inserting the extract instruction
- // into the relevant parent block of the OpPhi.
+ !fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCompositeExtract,
+ use_inst) &&
+ use_inst->opcode() != SpvOpPhi) {
+ // We cannot insert an extract before this instruction, so this
+ // synonym is no good.
continue;
}
@@ -103,40 +103,43 @@
continue;
}
- // We either replace the use with an id known to be synonymous, or
- // an id that will hold the result of extracting a synonym from a
- // composite.
+ // We either replace the use with an id known to be synonymous (when
+ // the synonym's |index_size| is 0), or an id that will hold the result
+ // of extracting a synonym from a composite (when the synonym's
+ // |index_size| is > 0).
uint32_t id_with_which_to_replace_use;
if (synonym_to_try->index_size() == 0) {
id_with_which_to_replace_use = synonym_to_try->object();
} else {
id_with_which_to_replace_use = GetFuzzerContext()->GetFreshId();
- protobufs::InstructionDescriptor instruction_to_insert_before =
- MakeInstructionDescriptor(GetIRContext(), use_inst);
- TransformationCompositeExtract composite_extract_transformation(
- instruction_to_insert_before, id_with_which_to_replace_use,
- synonym_to_try->object(),
- fuzzerutil::RepeatedFieldToVector(synonym_to_try->index()));
- assert(composite_extract_transformation.IsApplicable(
- GetIRContext(), *GetFactManager()) &&
- "Transformation should be applicable by construction.");
- composite_extract_transformation.Apply(GetIRContext(),
- GetFactManager());
- *GetTransformations()->add_transformation() =
- composite_extract_transformation.ToMessage();
+ opt::Instruction* instruction_to_insert_before = nullptr;
+
+ if (use_inst->opcode() != SpvOpPhi) {
+ instruction_to_insert_before = use_inst;
+ } else {
+ auto parent_block_id =
+ use_inst->GetSingleWordInOperand(use_in_operand_index + 1);
+ auto parent_block_instruction =
+ GetIRContext()->get_def_use_mgr()->GetDef(parent_block_id);
+ auto parent_block =
+ GetIRContext()->get_instr_block(parent_block_instruction);
+
+ instruction_to_insert_before = parent_block->GetMergeInst()
+ ? parent_block->GetMergeInst()
+ : parent_block->terminator();
+ }
+
+ ApplyTransformation(TransformationCompositeExtract(
+ MakeInstructionDescriptor(GetIRContext(),
+ instruction_to_insert_before),
+ id_with_which_to_replace_use, synonym_to_try->object(),
+ fuzzerutil::RepeatedFieldToVector(synonym_to_try->index())));
}
- TransformationReplaceIdWithSynonym replace_id_transformation(
+ ApplyTransformation(TransformationReplaceIdWithSynonym(
MakeIdUseDescriptorFromUse(GetIRContext(), use_inst,
use_in_operand_index),
- id_with_which_to_replace_use);
-
- // The transformation should be applicable by construction.
- assert(replace_id_transformation.IsApplicable(GetIRContext(),
- *GetFactManager()));
- replace_id_transformation.Apply(GetIRContext(), GetFactManager());
- *GetTransformations()->add_transformation() =
- replace_id_transformation.ToMessage();
+ id_with_which_to_replace_use));
break;
}
}
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_construct_composites.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_construct_composites.cpp
index e160302..330b9cf 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_construct_composites.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_construct_composites.cpp
@@ -42,7 +42,7 @@
}
}
- MaybeAddTransformationBeforeEachInstruction(
+ ForEachInstructionWithInstructionDescriptor(
[this, &composite_type_ids](
opt::Function* function, opt::BasicBlock* block,
opt::BasicBlock::iterator inst_it,
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_copy_objects.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_copy_objects.cpp
index 0fbe5cb..588cfb6 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_copy_objects.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_copy_objects.cpp
@@ -29,7 +29,7 @@
FuzzerPassCopyObjects::~FuzzerPassCopyObjects() = default;
void FuzzerPassCopyObjects::Apply() {
- MaybeAddTransformationBeforeEachInstruction(
+ ForEachInstructionWithInstructionDescriptor(
[this](opt::Function* function, opt::BasicBlock* block,
opt::BasicBlock::iterator inst_it,
const protobufs::InstructionDescriptor& instruction_descriptor)
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_function_parameters.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_function_parameters.cpp
new file mode 100644
index 0000000..2c49860
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_function_parameters.cpp
@@ -0,0 +1,81 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// 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 <numeric>
+#include <vector>
+
+#include "source/fuzz/fuzzer_context.h"
+#include "source/fuzz/fuzzer_pass_permute_function_parameters.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_permute_function_parameters.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassPermuteFunctionParameters::FuzzerPassPermuteFunctionParameters(
+ opt::IRContext* ir_context, FactManager* fact_manager,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations)
+ : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassPermuteFunctionParameters::~FuzzerPassPermuteFunctionParameters() =
+ default;
+
+void FuzzerPassPermuteFunctionParameters::Apply() {
+ for (const auto& function : *GetIRContext()->module()) {
+ uint32_t function_id = function.result_id();
+
+ // Skip the function if it is an entry point
+ if (fuzzerutil::FunctionIsEntryPoint(GetIRContext(), function_id)) {
+ continue;
+ }
+
+ if (!GetFuzzerContext()->ChoosePercentage(
+ GetFuzzerContext()->GetChanceOfPermutingParameters())) {
+ continue;
+ }
+
+ // Compute permutation for parameters
+ auto* function_type =
+ fuzzerutil::GetFunctionType(GetIRContext(), &function);
+ assert(function_type && "Function type is null");
+
+ // Don't take return type into account
+ uint32_t arg_size = function_type->NumInOperands() - 1;
+
+ // Create a vector, fill it with [0, n-1] values and shuffle it
+ std::vector<uint32_t> permutation(arg_size);
+ std::iota(permutation.begin(), permutation.end(), 0);
+ GetFuzzerContext()->Shuffle(&permutation);
+
+ // Create a new OpFunctionType instruction with permuted arguments
+ // if needed
+ auto result_type_id = function_type->GetSingleWordInOperand(0);
+ std::vector<uint32_t> argument_ids;
+
+ for (auto index : permutation) {
+ // +1 to take function's return type into account
+ argument_ids.push_back(function_type->GetSingleWordInOperand(index + 1));
+ }
+
+ // Apply our transformation
+ ApplyTransformation(TransformationPermuteFunctionParameters(
+ function_id, FindOrCreateFunctionType(result_type_id, argument_ids),
+ permutation));
+ }
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_function_parameters.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_function_parameters.h
new file mode 100644
index 0000000..bc79804
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_permute_function_parameters.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// 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_PERMUTE_FUNCTION_PARAMETERS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_PERMUTE_FUNCTION_PARAMETERS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Fuzzer pass that, given a non-entry-point function taking n parameters
+// and a permutation of the set [0, n - 1]:
+// 1. Introduces a new function type that is the same as the original
+// function's type but with the order of arguments permuted
+// (only add this if it doesn't already exist)
+// 2. Changes the type of the function to this type
+// 3. Adjusts all calls to the function so that their arguments are permuted
+class FuzzerPassPermuteFunctionParameters : public FuzzerPass {
+ public:
+ FuzzerPassPermuteFunctionParameters(
+ opt::IRContext* ir_context, FactManager* fact_manager,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations);
+
+ ~FuzzerPassPermuteFunctionParameters();
+
+ void Apply() override;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_FUZZER_PASS_PERMUTE_FUNCTION_PARAMETERS_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp
new file mode 100644
index 0000000..4df97c9
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp
@@ -0,0 +1,50 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// 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_swap_commutable_operands.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_swap_commutable_operands.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassSwapCommutableOperands::FuzzerPassSwapCommutableOperands(
+ opt::IRContext* ir_context, FactManager* fact_manager,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations)
+ : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassSwapCommutableOperands::~FuzzerPassSwapCommutableOperands() = default;
+
+void FuzzerPassSwapCommutableOperands::Apply() {
+ auto context = GetIRContext();
+ // Iterates over the module's instructions and checks whether it is
+ // commutative. In this case, the transformation is probabilistically applied.
+ context->module()->ForEachInst(
+ [this, context](opt::Instruction* instruction) {
+ if (spvOpcodeIsCommutativeBinaryOperator(instruction->opcode()) &&
+ GetFuzzerContext()->ChooseEven()) {
+ auto instructionDescriptor =
+ MakeInstructionDescriptor(context, instruction);
+ auto transformation =
+ TransformationSwapCommutableOperands(instructionDescriptor);
+ ApplyTransformation(transformation);
+ }
+ });
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_swap_commutable_operands.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_swap_commutable_operands.h
new file mode 100644
index 0000000..b0206de
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_swap_commutable_operands.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// 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_SWAP_COMMUTABLE_OPERANDS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_SWAP_COMMUTABLE_OPERANDS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// This fuzzer pass searches for all commutative instructions in the module,
+// probabilistically choosing which of these instructions will have its input
+// operands swapped.
+class FuzzerPassSwapCommutableOperands : public FuzzerPass {
+ public:
+ FuzzerPassSwapCommutableOperands(
+ opt::IRContext* ir_context, FactManager* fact_manager,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations);
+
+ ~FuzzerPassSwapCommutableOperands();
+
+ void Apply() override;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_FUZZER_PASS_SWAP_COMMUTABLE_OPERANDS_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp
new file mode 100644
index 0000000..9fb175b
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp
@@ -0,0 +1,54 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// 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_toggle_access_chain_instruction.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_toggle_access_chain_instruction.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassToggleAccessChainInstruction::FuzzerPassToggleAccessChainInstruction(
+ opt::IRContext* ir_context, FactManager* fact_manager,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations)
+ : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
+
+FuzzerPassToggleAccessChainInstruction::
+ ~FuzzerPassToggleAccessChainInstruction() = default;
+
+void FuzzerPassToggleAccessChainInstruction::Apply() {
+ auto context = GetIRContext();
+ // Iterates over the module's instructions and checks whether it is
+ // OpAccessChain or OpInBoundsAccessChain. In this case, the transformation is
+ // probabilistically applied.
+ context->module()->ForEachInst([this,
+ context](opt::Instruction* instruction) {
+ SpvOp opcode = instruction->opcode();
+ if ((opcode == SpvOpAccessChain || opcode == SpvOpInBoundsAccessChain) &&
+ GetFuzzerContext()->ChoosePercentage(
+ GetFuzzerContext()->GetChanceOfTogglingAccessChainInstruction())) {
+ auto instructionDescriptor =
+ MakeInstructionDescriptor(context, instruction);
+ auto transformation =
+ TransformationToggleAccessChainInstruction(instructionDescriptor);
+ ApplyTransformation(transformation);
+ }
+ });
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h
new file mode 100644
index 0000000..ec8c3f7
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// 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_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_
+#define SOURCE_FUZZ_FUZZER_PASS_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// This fuzzer pass searches for all access chain instructions in the module,
+// probabilistically choosing which of these instructions will be toggled.
+class FuzzerPassToggleAccessChainInstruction : public FuzzerPass {
+ public:
+ FuzzerPassToggleAccessChainInstruction(
+ opt::IRContext* ir_context, FactManager* fact_manager,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations);
+
+ ~FuzzerPassToggleAccessChainInstruction();
+
+ void Apply() override;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_FUZZER_PASS_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.cpp b/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.cpp
index 26961c8..4bfa195 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.cpp
@@ -395,6 +395,12 @@
return 0;
}
+opt::Instruction* GetFunctionType(opt::IRContext* context,
+ const opt::Function* function) {
+ uint32_t type_id = function->DefInst().GetSingleWordInOperand(1);
+ return context->get_def_use_mgr()->GetDef(type_id);
+}
+
opt::Function* FindFunction(opt::IRContext* ir_context, uint32_t function_id) {
for (auto& function : *ir_context->module()) {
if (function.result_id() == function_id) {
@@ -404,6 +410,15 @@
return nullptr;
}
+bool FunctionIsEntryPoint(opt::IRContext* context, uint32_t function_id) {
+ for (auto& entry_point : context->module()->entry_points()) {
+ if (entry_point.GetSingleWordInOperand(1) == function_id) {
+ return true;
+ }
+ }
+ return false;
+}
+
bool IdIsAvailableAtUse(opt::IRContext* context,
opt::Instruction* use_instruction,
uint32_t use_input_operand_index, uint32_t id) {
diff --git a/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.h b/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.h
index daa836c..7be0d59 100644
--- a/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.h
+++ b/third_party/SPIRV-Tools/source/fuzz/fuzzer_util.h
@@ -152,10 +152,18 @@
uint32_t FindFunctionType(opt::IRContext* ir_context,
const std::vector<uint32_t>& type_ids);
+// Returns a type instruction (OpTypeFunction) for |function|.
+// Returns |nullptr| if type is not found.
+opt::Instruction* GetFunctionType(opt::IRContext* context,
+ const opt::Function* function);
+
// Returns the function with result id |function_id|, or |nullptr| if no such
// function exists.
opt::Function* FindFunction(opt::IRContext* ir_context, uint32_t function_id);
+// Returns |true| if one of entry points has function id |function_id|.
+bool FunctionIsEntryPoint(opt::IRContext* context, uint32_t function_id);
+
// Checks whether |id| is available (according to dominance rules) at the use
// point defined by input operand |use_input_operand_index| of
// |use_instruction|.
diff --git a/third_party/SPIRV-Tools/source/fuzz/protobufs/spvtoolsfuzz.proto b/third_party/SPIRV-Tools/source/fuzz/protobufs/spvtoolsfuzz.proto
index 9773b60..b816e3b 100644
--- a/third_party/SPIRV-Tools/source/fuzz/protobufs/spvtoolsfuzz.proto
+++ b/third_party/SPIRV-Tools/source/fuzz/protobufs/spvtoolsfuzz.proto
@@ -168,12 +168,22 @@
FactDataSynonym data_synonym_fact = 2;
FactBlockIsDead block_is_dead_fact = 3;
FactFunctionIsLivesafe function_is_livesafe_fact = 4;
- FactPointeeValueIsIrrelevant pointee_value_is_irrelevant = 5;
+ FactPointeeValueIsIrrelevant pointee_value_is_irrelevant_fact = 5;
+ FactIdEquation id_equation_fact = 6;
}
}
// Keep fact message types in alphabetical order:
+message FactBlockIsDead {
+
+ // Records the fact that a block is guaranteed to be dynamically unreachable.
+ // This is useful because it informs the fuzzer that rather arbitrary changes
+ // can be made to this block.
+
+ uint32 block_id = 1;
+}
+
message FactConstantUniform {
// Records the fact that a uniform buffer element is guaranteed to be equal
@@ -203,15 +213,6 @@
}
-message FactBlockIsDead {
-
- // Records the fact that a block is guaranteed to be dynamically unreachable.
- // This is useful because it informs the fuzzer that rather arbitrary changes
- // can be made to this block.
-
- uint32 block_id = 1;
-}
-
message FactFunctionIsLivesafe {
// Records the fact that a function is guaranteed to be "livesafe", meaning
@@ -223,6 +224,31 @@
uint32 function_id = 1;
}
+message FactIdEquation {
+
+ // Records the fact that the equation:
+ //
+ // lhs_id = opcode rhs_id[0] rhs_id[1] ... rhs_id[N-1]
+ //
+ // holds; e.g. that the equation:
+ //
+ // %12 = OpIAdd %13 %14
+ //
+ // holds in the case where lhs_id is 12, rhs_id is [13, 14], and the opcode is
+ // OpIAdd.
+
+ // The left-hand-side of the equation.
+ uint32 lhs_id = 1;
+
+ // A SPIR-V opcode, from a restricted set of instructions for which equation
+ // facts make sense.
+ uint32 opcode = 2;
+
+ // The operands to the right-hand-side of the equation.
+ repeated uint32 rhs_id = 3;
+
+}
+
message FactPointeeValueIsIrrelevant {
// Records the fact that value of the data pointed to by a pointer id does
@@ -342,6 +368,10 @@
TransformationStore store = 37;
TransformationFunctionCall function_call = 38;
TransformationAccessChain access_chain = 39;
+ TransformationEquationInstruction equation_instruction = 40;
+ TransformationSwapCommutableOperands swap_commutable_operands = 41;
+ TransformationPermuteFunctionParameters permute_function_parameters = 42;
+ TransformationToggleAccessChainInstruction toggle_access_chain_instruction = 43;
// Add additional option using the next available number.
}
}
@@ -752,6 +782,28 @@
}
+message TransformationEquationInstruction {
+
+ // A transformation that adds an instruction to the module that defines an
+ // equation between its result id and input operand ids, such that the
+ // equation is guaranteed to hold at any program point where all ids involved
+ // are available (i.e. at any program point dominated by the instruction).
+
+ // The result id of the new instruction
+ uint32 fresh_id = 1;
+
+ // The instruction's opcode
+ uint32 opcode = 2;
+
+ // The input operands to the instruction
+ repeated uint32 in_operand_id = 3;
+
+ // A descriptor for an instruction in a block before which the new
+ // instruction should be inserted
+ InstructionDescriptor instruction_to_insert_before = 4;
+
+}
+
message TransformationFunctionCall {
// A transformation that introduces an OpFunctionCall instruction. The call
@@ -857,6 +909,36 @@
}
+message TransformationPermuteFunctionParameters {
+
+ // A transformation that, given a non-entry-point function taking n
+ // parameters and a permutation of the set [0, n-1]:
+ // - Introduces a new function type that is the same as the original
+ // function's type but with the order of arguments permuted
+ // (only if it doesn't already exist)
+ // - Changes the type of the function to this type
+ // - Adjusts all calls to the function so that their arguments are permuted
+
+ // Function, whose parameters will be permuted
+ uint32 function_id = 1;
+
+ // |new_type_id| is a result id of a valid OpTypeFunction instruction.
+ // New type is valid if:
+ // - it has the same number of operands as the old one
+ // - function's result type is the same as the old one
+ // - function's arguments are permuted according to |permutation| vector
+ uint32 new_type_id = 2;
+
+ // An array of size |n|, where |n| is a number of arguments to a function
+ // with |function_id|. For each i: 0 <= permutation[i] < n.
+ //
+ // i-th element of this array contains a position for an i-th
+ // function's argument (i.e. i-th argument will be permutation[i]-th
+ // after running this transformation)
+ repeated uint32 permutation = 3;
+
+}
+
message TransformationReplaceBooleanConstantWithConstantBinary {
// A transformation to capture replacing a use of a boolean constant with
@@ -1019,6 +1101,24 @@
}
+message TransformationSwapCommutableOperands {
+
+ // A transformation that swaps the operands of a commutative instruction.
+
+ // A descriptor for a commutative instruction
+ InstructionDescriptor instruction_descriptor = 1;
+
+}
+
+message TransformationToggleAccessChainInstruction {
+
+ // A transformation that toggles an access chain instruction.
+
+ // A descriptor for an access chain instruction
+ InstructionDescriptor instruction_descriptor = 1;
+
+}
+
message TransformationVectorShuffle {
// A transformation that adds a vector shuffle instruction.
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation.cpp
index 52fcfd7..f18c86b 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation.cpp
@@ -41,11 +41,13 @@
#include "source/fuzz/transformation_composite_construct.h"
#include "source/fuzz/transformation_composite_extract.h"
#include "source/fuzz/transformation_copy_object.h"
+#include "source/fuzz/transformation_equation_instruction.h"
#include "source/fuzz/transformation_function_call.h"
#include "source/fuzz/transformation_load.h"
#include "source/fuzz/transformation_merge_blocks.h"
#include "source/fuzz/transformation_move_block_down.h"
#include "source/fuzz/transformation_outline_function.h"
+#include "source/fuzz/transformation_permute_function_parameters.h"
#include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h"
#include "source/fuzz/transformation_replace_constant_with_uniform.h"
#include "source/fuzz/transformation_replace_id_with_synonym.h"
@@ -55,6 +57,8 @@
#include "source/fuzz/transformation_set_selection_control.h"
#include "source/fuzz/transformation_split_block.h"
#include "source/fuzz/transformation_store.h"
+#include "source/fuzz/transformation_swap_commutable_operands.h"
+#include "source/fuzz/transformation_toggle_access_chain_instruction.h"
#include "source/fuzz/transformation_vector_shuffle.h"
#include "source/util/make_unique.h"
@@ -128,6 +132,9 @@
message.composite_extract());
case protobufs::Transformation::TransformationCase::kCopyObject:
return MakeUnique<TransformationCopyObject>(message.copy_object());
+ case protobufs::Transformation::TransformationCase::kEquationInstruction:
+ return MakeUnique<TransformationEquationInstruction>(
+ message.equation_instruction());
case protobufs::Transformation::TransformationCase::kFunctionCall:
return MakeUnique<TransformationFunctionCall>(message.function_call());
case protobufs::Transformation::TransformationCase::kLoad:
@@ -140,6 +147,10 @@
return MakeUnique<TransformationOutlineFunction>(
message.outline_function());
case protobufs::Transformation::TransformationCase::
+ kPermuteFunctionParameters:
+ return MakeUnique<TransformationPermuteFunctionParameters>(
+ message.permute_function_parameters());
+ case protobufs::Transformation::TransformationCase::
kReplaceBooleanConstantWithConstantBinary:
return MakeUnique<TransformationReplaceBooleanConstantWithConstantBinary>(
message.replace_boolean_constant_with_constant_binary());
@@ -166,6 +177,13 @@
return MakeUnique<TransformationSplitBlock>(message.split_block());
case protobufs::Transformation::TransformationCase::kStore:
return MakeUnique<TransformationStore>(message.store());
+ case protobufs::Transformation::TransformationCase::kSwapCommutableOperands:
+ return MakeUnique<TransformationSwapCommutableOperands>(
+ message.swap_commutable_operands());
+ case protobufs::Transformation::TransformationCase::
+ kToggleAccessChainInstruction:
+ return MakeUnique<TransformationToggleAccessChainInstruction>(
+ message.toggle_access_chain_instruction());
case protobufs::Transformation::TransformationCase::kVectorShuffle:
return MakeUnique<TransformationVectorShuffle>(message.vector_shuffle());
case protobufs::Transformation::TRANSFORMATION_NOT_SET:
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_continue.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_continue.cpp
index ffa182e..3a4875e 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_continue.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_dead_continue.cpp
@@ -112,7 +112,7 @@
// clone, and check whether the transformed clone is valid.
//
// In principle some of the above checks could be removed, with more reliance
- // being places on the validator. This should be revisited if we are sure
+ // being placed on the validator. This should be revisited if we are sure
// the validator is complete with respect to checking structured control flow
// rules.
auto cloned_context = fuzzerutil::CloneIRContext(context);
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_function.h b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_function.h
index 2b59661..3880963 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_function.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_add_type_function.h
@@ -37,7 +37,7 @@
// - |message_.return_type_id| and each element of |message_.argument_type_id|
// must be the ids of non-function types
// - The module must not contain an OpTypeFunction instruction defining a
- // function type with the signature provided by teh given return and
+ // function type with the signature provided by the given return and
// argument types
bool IsApplicable(opt::IRContext* context,
const FactManager& fact_manager) const override;
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_equation_instruction.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_equation_instruction.cpp
new file mode 100644
index 0000000..21b67f6
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_equation_instruction.cpp
@@ -0,0 +1,186 @@
+// 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/transformation_equation_instruction.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationEquationInstruction::TransformationEquationInstruction(
+ const spvtools::fuzz::protobufs::TransformationEquationInstruction& message)
+ : message_(message) {}
+
+TransformationEquationInstruction::TransformationEquationInstruction(
+ uint32_t fresh_id, SpvOp opcode, const std::vector<uint32_t>& in_operand_id,
+ const protobufs::InstructionDescriptor& instruction_to_insert_before) {
+ message_.set_fresh_id(fresh_id);
+ message_.set_opcode(opcode);
+ for (auto id : in_operand_id) {
+ message_.add_in_operand_id(id);
+ }
+ *message_.mutable_instruction_to_insert_before() =
+ instruction_to_insert_before;
+}
+
+bool TransformationEquationInstruction::IsApplicable(
+ opt::IRContext* context,
+ const spvtools::fuzz::FactManager& /*unused*/) const {
+ // The result id must be fresh.
+ if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+ return false;
+ }
+ // The instruction to insert before must exist.
+ auto insert_before =
+ FindInstruction(message_.instruction_to_insert_before(), context);
+ if (!insert_before) {
+ return false;
+ }
+ // The input ids must all exist, not be OpUndef, and be available before this
+ // instruction.
+ for (auto id : message_.in_operand_id()) {
+ auto inst = context->get_def_use_mgr()->GetDef(id);
+ if (!inst) {
+ return false;
+ }
+ if (inst->opcode() == SpvOpUndef) {
+ return false;
+ }
+ if (!fuzzerutil::IdIsAvailableBeforeInstruction(context, insert_before,
+ id)) {
+ return false;
+ }
+ }
+
+ return MaybeGetResultType(context) != 0;
+}
+
+void TransformationEquationInstruction::Apply(
+ opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const {
+ fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+
+ opt::Instruction::OperandList in_operands;
+ std::vector<uint32_t> rhs_id;
+ for (auto id : message_.in_operand_id()) {
+ in_operands.push_back({SPV_OPERAND_TYPE_ID, {id}});
+ rhs_id.push_back(id);
+ }
+
+ FindInstruction(message_.instruction_to_insert_before(), context)
+ ->InsertBefore(MakeUnique<opt::Instruction>(
+ context, static_cast<SpvOp>(message_.opcode()),
+ MaybeGetResultType(context), message_.fresh_id(), in_operands));
+
+ context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+
+ fact_manager->AddFactIdEquation(message_.fresh_id(),
+ static_cast<SpvOp>(message_.opcode()), rhs_id,
+ context);
+}
+
+protobufs::Transformation TransformationEquationInstruction::ToMessage() const {
+ protobufs::Transformation result;
+ *result.mutable_equation_instruction() = message_;
+ return result;
+}
+
+uint32_t TransformationEquationInstruction::MaybeGetResultType(
+ opt::IRContext* context) const {
+ switch (static_cast<SpvOp>(message_.opcode())) {
+ case SpvOpIAdd:
+ case SpvOpISub: {
+ if (message_.in_operand_id().size() != 2) {
+ return 0;
+ }
+ uint32_t first_operand_width = 0;
+ uint32_t first_operand_type_id = 0;
+ for (uint32_t index = 0; index < 2; index++) {
+ auto operand_inst =
+ context->get_def_use_mgr()->GetDef(message_.in_operand_id(index));
+ if (!operand_inst || !operand_inst->type_id()) {
+ return 0;
+ }
+ auto operand_type =
+ context->get_type_mgr()->GetType(operand_inst->type_id());
+ if (!(operand_type->AsInteger() ||
+ (operand_type->AsVector() &&
+ operand_type->AsVector()->element_type()->AsInteger()))) {
+ return 0;
+ }
+ uint32_t operand_width =
+ operand_type->AsInteger()
+ ? 1
+ : operand_type->AsVector()->element_count();
+ if (index == 0) {
+ first_operand_width = operand_width;
+ first_operand_type_id = operand_inst->type_id();
+ } else {
+ assert(first_operand_width != 0 &&
+ "The first operand should have been processed.");
+ if (operand_width != first_operand_width) {
+ return 0;
+ }
+ }
+ }
+ assert(first_operand_type_id != 0 &&
+ "A type must have been found for the first operand.");
+ return first_operand_type_id;
+ }
+ case SpvOpLogicalNot: {
+ if (message_.in_operand_id().size() != 1) {
+ return 0;
+ }
+ auto operand_inst =
+ context->get_def_use_mgr()->GetDef(message_.in_operand_id(0));
+ if (!operand_inst || !operand_inst->type_id()) {
+ return 0;
+ }
+ auto operand_type =
+ context->get_type_mgr()->GetType(operand_inst->type_id());
+ if (!(operand_type->AsBool() ||
+ (operand_type->AsVector() &&
+ operand_type->AsVector()->element_type()->AsBool()))) {
+ return 0;
+ }
+ return operand_inst->type_id();
+ }
+ case SpvOpSNegate: {
+ if (message_.in_operand_id().size() != 1) {
+ return 0;
+ }
+ auto operand_inst =
+ context->get_def_use_mgr()->GetDef(message_.in_operand_id(0));
+ if (!operand_inst || !operand_inst->type_id()) {
+ return 0;
+ }
+ auto operand_type =
+ context->get_type_mgr()->GetType(operand_inst->type_id());
+ if (!(operand_type->AsInteger() ||
+ (operand_type->AsVector() &&
+ operand_type->AsVector()->element_type()->AsInteger()))) {
+ return 0;
+ }
+ return operand_inst->type_id();
+ }
+
+ default:
+ assert(false && "Inappropriate opcode for equation instruction.");
+ return 0;
+ }
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_equation_instruction.h b/third_party/SPIRV-Tools/source/fuzz/transformation_equation_instruction.h
new file mode 100644
index 0000000..2456ba5
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_equation_instruction.h
@@ -0,0 +1,76 @@
+// Copyright (c) 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_EQUATION_INSTRUCTION_H_
+#define SOURCE_FUZZ_TRANSFORMATION_EQUATION_INSTRUCTION_H_
+
+#include <vector>
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationEquationInstruction : public Transformation {
+ public:
+ explicit TransformationEquationInstruction(
+ const protobufs::TransformationEquationInstruction& message);
+
+ TransformationEquationInstruction(
+ uint32_t fresh_id, SpvOp opcode,
+ const std::vector<uint32_t>& in_operand_id,
+ const protobufs::InstructionDescriptor& instruction_to_insert_before);
+
+ // - |message_.fresh_id| must be fresh.
+ // - |message_.instruction_to_insert_before| must identify an instruction
+ // before which an equation instruction can legitimately be inserted.
+ // - Each id in |message_.in_operand_id| must exist, not be an OpUndef, and
+ // be available before |message_.instruction_to_insert_before|.
+ // - |message_.opcode| must be an opcode for which we know how to handle
+ // equations, the types of the ids in |message_.in_operand_id| must be
+ // suitable for use with this opcode, and the module must contain an
+ // appropriate result type id.
+ bool IsApplicable(opt::IRContext* context,
+ const FactManager& fact_manager) const override;
+
+ // Adds an instruction to the module, right before
+ // |message_.instruction_to_insert_before|, of the form:
+ //
+ // |message_.fresh_id| = |message_.opcode| %type |message_.in_operand_ids|
+ //
+ // where %type is a type id that already exists in the module and that is
+ // compatible with the opcode and input operands.
+ //
+ // The fact manager is also updated to inform it of this equation fact.
+ void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+ protobufs::Transformation ToMessage() const override;
+
+ private:
+ // A helper that, in one fell swoop, checks that |message_.opcode| and the ids
+ // in |message_.in_operand_id| are compatible, and that the module contains
+ // an appropriate result type id. If all is well, the result type id is
+ // returned. Otherwise, 0 is returned.
+ uint32_t MaybeGetResultType(opt::IRContext* context) const;
+
+ protobufs::TransformationEquationInstruction message_;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_TRANSFORMATION_EQUATION_INSTRUCTION_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_function_call.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_function_call.cpp
index 6988664..cea8537 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_function_call.cpp
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_function_call.cpp
@@ -53,7 +53,7 @@
}
// The function must not be an entry point
- if (FunctionIsEntryPoint(context, message_.callee_id())) {
+ if (fuzzerutil::FunctionIsEntryPoint(context, message_.callee_id())) {
return false;
}
@@ -181,15 +181,5 @@
return result;
}
-bool TransformationFunctionCall::FunctionIsEntryPoint(opt::IRContext* context,
- uint32_t function_id) {
- for (auto& entry_point : context->module()->entry_points()) {
- if (entry_point.GetSingleWordInOperand(1) == function_id) {
- return true;
- }
- }
- return false;
-}
-
} // namespace fuzz
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_function_call.h b/third_party/SPIRV-Tools/source/fuzz/transformation_function_call.h
index e977e1d..a9ae5be 100644
--- a/third_party/SPIRV-Tools/source/fuzz/transformation_function_call.h
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_function_call.h
@@ -55,10 +55,6 @@
protobufs::Transformation ToMessage() const override;
- // Helper to determine whether |function_id| is targeted by OpEntryPoint.
- static bool FunctionIsEntryPoint(opt::IRContext* context,
- uint32_t function_id);
-
private:
protobufs::TransformationFunctionCall message_;
};
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_permute_function_parameters.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_permute_function_parameters.cpp
new file mode 100644
index 0000000..2141533
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_permute_function_parameters.cpp
@@ -0,0 +1,184 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// 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 <unordered_set>
+#include <vector>
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_permute_function_parameters.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationPermuteFunctionParameters::
+ TransformationPermuteFunctionParameters(
+ const spvtools::fuzz::protobufs::
+ TransformationPermuteFunctionParameters& message)
+ : message_(message) {}
+
+TransformationPermuteFunctionParameters::
+ TransformationPermuteFunctionParameters(
+ uint32_t function_id, uint32_t new_type_id,
+ const std::vector<uint32_t>& permutation) {
+ message_.set_function_id(function_id);
+ message_.set_new_type_id(new_type_id);
+
+ for (auto index : permutation) {
+ message_.add_permutation(index);
+ }
+}
+
+bool TransformationPermuteFunctionParameters::IsApplicable(
+ opt::IRContext* context, const FactManager& /*unused*/) const {
+ // Check that function exists
+ const auto* function =
+ fuzzerutil::FindFunction(context, message_.function_id());
+ if (!function || function->DefInst().opcode() != SpvOpFunction ||
+ fuzzerutil::FunctionIsEntryPoint(context, function->result_id())) {
+ return false;
+ }
+
+ // Check that permutation has valid indices
+ const auto* function_type = fuzzerutil::GetFunctionType(context, function);
+ assert(function_type && "Function type is null");
+
+ const auto& permutation = message_.permutation();
+
+ // Don't take return type into account
+ auto arg_size = function_type->NumInOperands() - 1;
+
+ // |permutation| vector should be equal to the number of arguments
+ if (static_cast<uint32_t>(permutation.size()) != arg_size) {
+ return false;
+ }
+
+ // Check that all indices are valid
+ // and unique integers from the [0, n-1] set
+ std::unordered_set<uint32_t> unique_indices;
+ for (auto index : permutation) {
+ // We don't compare |index| with 0 since it's an unsigned integer
+ if (index >= arg_size) {
+ return false;
+ }
+
+ unique_indices.insert(index);
+ }
+
+ // Check that permutation doesn't have duplicated values
+ assert(unique_indices.size() == arg_size && "Permutation has duplicates");
+
+ // Check that new function's type is valid:
+ // - Has the same number of operands
+ // - Has the same result type as the old one
+ // - Order of arguments is permuted
+ auto new_type_id = message_.new_type_id();
+ const auto* new_type = context->get_def_use_mgr()->GetDef(new_type_id);
+
+ if (!new_type || new_type->opcode() != SpvOpTypeFunction ||
+ new_type->NumInOperands() != function_type->NumInOperands()) {
+ return false;
+ }
+
+ // Check that both instructions have the same result type
+ if (new_type->GetSingleWordInOperand(0) !=
+ function_type->GetSingleWordInOperand(0)) {
+ return false;
+ }
+
+ // Check that new function type has its arguments permuted
+ for (int i = 0, n = static_cast<int>(permutation.size()); i < n; ++i) {
+ // +1 to take return type into account
+ if (new_type->GetSingleWordInOperand(i + 1) !=
+ function_type->GetSingleWordInOperand(permutation[i] + 1)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void TransformationPermuteFunctionParameters::Apply(
+ opt::IRContext* context, FactManager* /*unused*/) const {
+ // Retrieve all data from the message
+ uint32_t function_id = message_.function_id();
+ uint32_t new_type_id = message_.new_type_id();
+ const auto& permutation = message_.permutation();
+
+ // Find the function that will be transformed
+ auto* function = fuzzerutil::FindFunction(context, function_id);
+ assert(function && "Can't find the function");
+
+ // Change function's type
+ function->DefInst().SetInOperand(1, {new_type_id});
+
+ // Adjust OpFunctionParameter instructions
+
+ // Collect ids and types from OpFunctionParameter instructions
+ std::vector<uint32_t> param_id, param_type;
+ function->ForEachParam(
+ [¶m_id, ¶m_type](const opt::Instruction* param) {
+ param_id.push_back(param->result_id());
+ param_type.push_back(param->type_id());
+ });
+
+ // Permute parameters' ids and types
+ std::vector<uint32_t> permuted_param_id, permuted_param_type;
+ for (auto index : permutation) {
+ permuted_param_id.push_back(param_id[index]);
+ permuted_param_type.push_back(param_type[index]);
+ }
+
+ // Set OpFunctionParameter instructions to point to new parameters
+ size_t i = 0;
+ function->ForEachParam(
+ [&i, &permuted_param_id, &permuted_param_type](opt::Instruction* param) {
+ param->SetResultType(permuted_param_type[i]);
+ param->SetResultId(permuted_param_id[i]);
+ ++i;
+ });
+
+ // Fix all OpFunctionCall instructions
+ context->get_def_use_mgr()->ForEachUser(
+ &function->DefInst(),
+ [function_id, &permutation](opt::Instruction* call) {
+ if (call->opcode() != SpvOpFunctionCall ||
+ call->GetSingleWordInOperand(0) != function_id) {
+ return;
+ }
+
+ opt::Instruction::OperandList call_operands = {
+ call->GetInOperand(0) // Function id
+ };
+
+ for (auto index : permutation) {
+ // Take function id into account
+ call_operands.push_back(call->GetInOperand(index + 1));
+ }
+
+ call->SetInOperands(std::move(call_operands));
+ });
+
+ // Make sure our changes are analyzed
+ context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationPermuteFunctionParameters::ToMessage()
+ const {
+ protobufs::Transformation result;
+ *result.mutable_permute_function_parameters() = message_;
+ return result;
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_permute_function_parameters.h b/third_party/SPIRV-Tools/source/fuzz/transformation_permute_function_parameters.h
new file mode 100644
index 0000000..c67a735
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_permute_function_parameters.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// 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_PERMUTE_FUNCTION_PARAMETERS_H_
+#define SOURCE_FUZZ_TRANSFORMATION_PERMUTE_FUNCTION_PARAMETERS_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationPermuteFunctionParameters : public Transformation {
+ public:
+ explicit TransformationPermuteFunctionParameters(
+ const protobufs::TransformationPermuteFunctionParameters& message);
+
+ TransformationPermuteFunctionParameters(
+ uint32_t function_id, uint32_t new_type_id,
+ const std::vector<uint32_t>& permutation);
+
+ // - |function_id| is a valid non-entry-point OpFunction instruction
+ // - |new_type_id| is a result id of a valid OpTypeFunction instruction.
+ // New type is valid if:
+ // - it has the same number of operands as the old one
+ // - function's result type is the same as the old one
+ // - function's arguments are permuted according to |permutation| vector
+ // - |permutation| is a set of [0..(n - 1)], where n is a number of arguments
+ // to the function
+ bool IsApplicable(opt::IRContext* context,
+ const FactManager& fact_manager) const override;
+
+ // - OpFunction instruction with |result_id == function_id| is changed.
+ // Its arguments are permuted according to the |permutation| vector
+ // - Changed function gets a new type specified by |type_id|
+ // - Calls to the function are adjusted accordingly
+ void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+ protobufs::Transformation ToMessage() const override;
+
+ private:
+ protobufs::TransformationPermuteFunctionParameters message_;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_TRANSFORMATION_PERMUTE_FUNCTION_PARAMETERS_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_swap_commutable_operands.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_swap_commutable_operands.cpp
new file mode 100644
index 0000000..49d9de8
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_swap_commutable_operands.cpp
@@ -0,0 +1,66 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_swap_commutable_operands.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationSwapCommutableOperands::TransformationSwapCommutableOperands(
+ const spvtools::fuzz::protobufs::TransformationSwapCommutableOperands&
+ message)
+ : message_(message) {}
+
+TransformationSwapCommutableOperands::TransformationSwapCommutableOperands(
+ const protobufs::InstructionDescriptor& instruction_descriptor) {
+ *message_.mutable_instruction_descriptor() = instruction_descriptor;
+}
+
+bool TransformationSwapCommutableOperands::IsApplicable(
+ opt::IRContext* context, const spvtools::fuzz::FactManager& /*unused*/
+ ) const {
+ auto instruction =
+ FindInstruction(message_.instruction_descriptor(), context);
+ if (instruction == nullptr) return false;
+
+ SpvOp opcode = static_cast<SpvOp>(
+ message_.instruction_descriptor().target_instruction_opcode());
+ assert(instruction->opcode() == opcode &&
+ "The located instruction must have the same opcode as in the "
+ "descriptor.");
+ return spvOpcodeIsCommutativeBinaryOperator(opcode);
+}
+
+void TransformationSwapCommutableOperands::Apply(
+ opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/
+ ) const {
+ auto instruction =
+ FindInstruction(message_.instruction_descriptor(), context);
+ // By design, the instructions defined to be commutative have exactly two
+ // input parameters.
+ std::swap(instruction->GetInOperand(0), instruction->GetInOperand(1));
+}
+
+protobufs::Transformation TransformationSwapCommutableOperands::ToMessage()
+ const {
+ protobufs::Transformation result;
+ *result.mutable_swap_commutable_operands() = message_;
+ return result;
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_swap_commutable_operands.h b/third_party/SPIRV-Tools/source/fuzz/transformation_swap_commutable_operands.h
new file mode 100644
index 0000000..061e92d
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_swap_commutable_operands.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// 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_SWAP_COMMUTABLE_OPERANDS_H_
+#define SOURCE_FUZZ_TRANSFORMATION_SWAP_COMMUTABLE_OPERANDS_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationSwapCommutableOperands : public Transformation {
+ public:
+ explicit TransformationSwapCommutableOperands(
+ const protobufs::TransformationSwapCommutableOperands& message);
+
+ TransformationSwapCommutableOperands(
+ const protobufs::InstructionDescriptor& instruction_descriptor);
+
+ // - |message_.instruction_descriptor| must identify an existing
+ // commutative instruction
+ bool IsApplicable(opt::IRContext* context,
+ const FactManager& fact_manager) const override;
+
+ // Swaps the commutable operands.
+ void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+ protobufs::Transformation ToMessage() const override;
+
+ private:
+ protobufs::TransformationSwapCommutableOperands message_;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_TRANSFORMATION_SWAP_COMMUTABLE_OPERANDS_H_
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_toggle_access_chain_instruction.cpp b/third_party/SPIRV-Tools/source/fuzz/transformation_toggle_access_chain_instruction.cpp
new file mode 100644
index 0000000..ace331a
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_toggle_access_chain_instruction.cpp
@@ -0,0 +1,83 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_toggle_access_chain_instruction.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationToggleAccessChainInstruction::
+ TransformationToggleAccessChainInstruction(
+ const spvtools::fuzz::protobufs::
+ TransformationToggleAccessChainInstruction& message)
+ : message_(message) {}
+
+TransformationToggleAccessChainInstruction::
+ TransformationToggleAccessChainInstruction(
+ const protobufs::InstructionDescriptor& instruction_descriptor) {
+ *message_.mutable_instruction_descriptor() = instruction_descriptor;
+}
+
+bool TransformationToggleAccessChainInstruction::IsApplicable(
+ opt::IRContext* context, const spvtools::fuzz::FactManager& /*unused*/
+ ) const {
+ auto instruction =
+ FindInstruction(message_.instruction_descriptor(), context);
+ if (instruction == nullptr) {
+ return false;
+ }
+
+ SpvOp opcode = static_cast<SpvOp>(
+ message_.instruction_descriptor().target_instruction_opcode());
+
+ assert(instruction->opcode() == opcode &&
+ "The located instruction must have the same opcode as in the "
+ "descriptor.");
+
+ if (opcode == SpvOpAccessChain || opcode == SpvOpInBoundsAccessChain) {
+ return true;
+ }
+
+ return false;
+}
+
+void TransformationToggleAccessChainInstruction::Apply(
+ opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/
+ ) const {
+ auto instruction =
+ FindInstruction(message_.instruction_descriptor(), context);
+ SpvOp opcode = instruction->opcode();
+
+ if (opcode == SpvOpAccessChain) {
+ instruction->SetOpcode(SpvOpInBoundsAccessChain);
+ } else {
+ assert(opcode == SpvOpInBoundsAccessChain &&
+ "The located instruction must be an OpInBoundsAccessChain "
+ "instruction.");
+ instruction->SetOpcode(SpvOpAccessChain);
+ }
+}
+
+protobufs::Transformation
+TransformationToggleAccessChainInstruction::ToMessage() const {
+ protobufs::Transformation result;
+ *result.mutable_toggle_access_chain_instruction() = message_;
+ return result;
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/fuzz/transformation_toggle_access_chain_instruction.h b/third_party/SPIRV-Tools/source/fuzz/transformation_toggle_access_chain_instruction.h
new file mode 100644
index 0000000..125e1ab
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/fuzz/transformation_toggle_access_chain_instruction.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// 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_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_
+#define SOURCE_FUZZ_TRANSFORMATION_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationToggleAccessChainInstruction : public Transformation {
+ public:
+ explicit TransformationToggleAccessChainInstruction(
+ const protobufs::TransformationToggleAccessChainInstruction& message);
+
+ TransformationToggleAccessChainInstruction(
+ const protobufs::InstructionDescriptor& instruction_descriptor);
+
+ // - |message_.instruction_descriptor| must identify an existing
+ // access chain instruction
+ bool IsApplicable(opt::IRContext* context,
+ const FactManager& fact_manager) const override;
+
+ // Toggles the access chain instruction.
+ void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+ protobufs::Transformation ToMessage() const override;
+
+ private:
+ protobufs::TransformationToggleAccessChainInstruction message_;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_TRANSFORMATION_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_
diff --git a/third_party/SPIRV-Tools/source/opcode.cpp b/third_party/SPIRV-Tools/source/opcode.cpp
index a837b95..80fe3b3 100644
--- a/third_party/SPIRV-Tools/source/opcode.cpp
+++ b/third_party/SPIRV-Tools/source/opcode.cpp
@@ -1,4 +1,6 @@
-// Copyright (c) 2015-2016 The Khronos Group Inc.
+// Copyright (c) 2015-2020 The Khronos Group Inc.
+// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights
+// reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -333,6 +335,9 @@
case SpvOpTypeNamedBarrier:
case SpvOpTypeAccelerationStructureNV:
case SpvOpTypeCooperativeMatrixNV:
+ // case SpvOpTypeAccelerationStructureKHR: covered by
+ // SpvOpTypeAccelerationStructureNV
+ case SpvOpTypeRayQueryProvisionalKHR:
return true;
default:
// In particular, OpTypeForwardPointer does not generate a type,
@@ -612,6 +617,39 @@
}
}
+bool spvOpcodeIsCommutativeBinaryOperator(SpvOp opcode) {
+ switch (opcode) {
+ case SpvOpPtrEqual:
+ case SpvOpPtrNotEqual:
+ case SpvOpIAdd:
+ case SpvOpFAdd:
+ case SpvOpIMul:
+ case SpvOpFMul:
+ case SpvOpDot:
+ case SpvOpIAddCarry:
+ case SpvOpUMulExtended:
+ case SpvOpSMulExtended:
+ case SpvOpBitwiseOr:
+ case SpvOpBitwiseXor:
+ case SpvOpBitwiseAnd:
+ case SpvOpOrdered:
+ case SpvOpUnordered:
+ case SpvOpLogicalEqual:
+ case SpvOpLogicalNotEqual:
+ case SpvOpLogicalOr:
+ case SpvOpLogicalAnd:
+ case SpvOpIEqual:
+ case SpvOpINotEqual:
+ case SpvOpFOrdEqual:
+ case SpvOpFUnordEqual:
+ case SpvOpFOrdNotEqual:
+ case SpvOpFUnordNotEqual:
+ return true;
+ default:
+ return false;
+ }
+}
+
std::vector<uint32_t> spvOpcodeMemorySemanticsOperandIndices(SpvOp opcode) {
switch (opcode) {
case SpvOpMemoryBarrier:
diff --git a/third_party/SPIRV-Tools/source/opcode.h b/third_party/SPIRV-Tools/source/opcode.h
index f79826f..b4f0271 100644
--- a/third_party/SPIRV-Tools/source/opcode.h
+++ b/third_party/SPIRV-Tools/source/opcode.h
@@ -130,6 +130,10 @@
// Returns true if the given opcode is a debug instruction.
bool spvOpcodeIsDebug(SpvOp opcode);
+// Returns true for opcodes that are binary operators,
+// where the order of the operands is irrelevant.
+bool spvOpcodeIsCommutativeBinaryOperator(SpvOp opcode);
+
// Returns a vector containing the indices of the memory semantics <id>
// operands for |opcode|.
std::vector<uint32_t> spvOpcodeMemorySemanticsOperandIndices(SpvOp opcode);
diff --git a/third_party/SPIRV-Tools/source/operand.cpp b/third_party/SPIRV-Tools/source/operand.cpp
index 3042606..755ad6a 100644
--- a/third_party/SPIRV-Tools/source/operand.cpp
+++ b/third_party/SPIRV-Tools/source/operand.cpp
@@ -1,4 +1,6 @@
-// Copyright (c) 2015-2016 The Khronos Group Inc.
+// Copyright (c) 2015-2020 The Khronos Group Inc.
+// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights
+// reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -216,6 +218,14 @@
return "kernel profiling info";
case SPV_OPERAND_TYPE_CAPABILITY:
return "capability";
+ case SPV_OPERAND_TYPE_RAY_FLAGS:
+ return "ray flags";
+ case SPV_OPERAND_TYPE_RAY_QUERY_INTERSECTION:
+ return "ray query intersection";
+ case SPV_OPERAND_TYPE_RAY_QUERY_COMMITTED_INTERSECTION_TYPE:
+ return "ray query committed intersection type";
+ case SPV_OPERAND_TYPE_RAY_QUERY_CANDIDATE_INTERSECTION_TYPE:
+ return "ray query candidate intersection type";
case SPV_OPERAND_TYPE_IMAGE:
case SPV_OPERAND_TYPE_OPTIONAL_IMAGE:
return "image";
@@ -323,6 +333,10 @@
case SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS:
case SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO:
case SPV_OPERAND_TYPE_CAPABILITY:
+ case SPV_OPERAND_TYPE_RAY_FLAGS:
+ case SPV_OPERAND_TYPE_RAY_QUERY_INTERSECTION:
+ case SPV_OPERAND_TYPE_RAY_QUERY_COMMITTED_INTERSECTION_TYPE:
+ case SPV_OPERAND_TYPE_RAY_QUERY_CANDIDATE_INTERSECTION_TYPE:
case SPV_OPERAND_TYPE_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING:
case SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE:
case SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER:
diff --git a/third_party/SPIRV-Tools/source/opt/CMakeLists.txt b/third_party/SPIRV-Tools/source/opt/CMakeLists.txt
index 0f719cb..1428c74 100644
--- a/third_party/SPIRV-Tools/source/opt/CMakeLists.txt
+++ b/third_party/SPIRV-Tools/source/opt/CMakeLists.txt
@@ -58,6 +58,7 @@
inline_pass.h
inst_bindless_check_pass.h
inst_buff_addr_check_pass.h
+ inst_debug_printf_pass.h
instruction.h
instruction_list.h
instrument_pass.h
@@ -164,6 +165,7 @@
inline_pass.cpp
inst_bindless_check_pass.cpp
inst_buff_addr_check_pass.cpp
+ inst_debug_printf_pass.cpp
instruction.cpp
instruction_list.cpp
instrument_pass.cpp
diff --git a/third_party/SPIRV-Tools/source/opt/aggressive_dead_code_elim_pass.cpp b/third_party/SPIRV-Tools/source/opt/aggressive_dead_code_elim_pass.cpp
index 761ff7c..db2b67b 100644
--- a/third_party/SPIRV-Tools/source/opt/aggressive_dead_code_elim_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/aggressive_dead_code_elim_pass.cpp
@@ -923,6 +923,7 @@
"SPV_GOOGLE_hlsl_functionality1",
"SPV_GOOGLE_user_type",
"SPV_NV_shader_subgroup_partitioned",
+ "SPV_EXT_demote_to_helper_invocation",
"SPV_EXT_descriptor_indexing",
"SPV_NV_fragment_shader_barycentric",
"SPV_NV_compute_shader_derivatives",
@@ -930,6 +931,7 @@
"SPV_NV_shading_rate",
"SPV_NV_mesh_shader",
"SPV_NV_ray_tracing",
+ "SPV_KHR_ray_tracing",
"SPV_EXT_fragment_invocation_density",
"SPV_EXT_physical_storage_buffer",
});
diff --git a/third_party/SPIRV-Tools/source/opt/feature_manager.cpp b/third_party/SPIRV-Tools/source/opt/feature_manager.cpp
index 63d50b6..b4d6f1b 100644
--- a/third_party/SPIRV-Tools/source/opt/feature_manager.cpp
+++ b/third_party/SPIRV-Tools/source/opt/feature_manager.cpp
@@ -47,6 +47,11 @@
}
}
+void FeatureManager::RemoveExtension(Extension ext) {
+ if (!extensions_.Contains(ext)) return;
+ extensions_.Remove(ext);
+}
+
void FeatureManager::AddCapability(SpvCapability cap) {
if (capabilities_.Contains(cap)) return;
@@ -60,6 +65,11 @@
}
}
+void FeatureManager::RemoveCapability(SpvCapability cap) {
+ if (!capabilities_.Contains(cap)) return;
+ capabilities_.Remove(cap);
+}
+
void FeatureManager::AddCapabilities(Module* module) {
for (Instruction& inst : module->capabilities()) {
AddCapability(static_cast<SpvCapability>(inst.GetSingleWordInOperand(0)));
diff --git a/third_party/SPIRV-Tools/source/opt/feature_manager.h b/third_party/SPIRV-Tools/source/opt/feature_manager.h
index 2fe3291..881d5e6 100644
--- a/third_party/SPIRV-Tools/source/opt/feature_manager.h
+++ b/third_party/SPIRV-Tools/source/opt/feature_manager.h
@@ -30,11 +30,17 @@
// Returns true if |ext| is an enabled extension in the module.
bool HasExtension(Extension ext) const { return extensions_.Contains(ext); }
+ // Removes the given |extension| from the current FeatureManager.
+ void RemoveExtension(Extension extension);
+
// Returns true if |cap| is an enabled capability in the module.
bool HasCapability(SpvCapability cap) const {
return capabilities_.Contains(cap);
}
+ // Removes the given |capability| from the current FeatureManager.
+ void RemoveCapability(SpvCapability capability);
+
// Analyzes |module| and records enabled extensions and capabilities.
void Analyze(Module* module);
diff --git a/third_party/SPIRV-Tools/source/opt/function.cpp b/third_party/SPIRV-Tools/source/opt/function.cpp
index efda68b..5d50f37 100644
--- a/third_party/SPIRV-Tools/source/opt/function.cpp
+++ b/third_party/SPIRV-Tools/source/opt/function.cpp
@@ -34,6 +34,11 @@
},
true);
+ for (const auto& i : debug_insts_in_header_) {
+ clone->AddDebugInstructionInHeader(
+ std::unique_ptr<Instruction>(i.Clone(ctx)));
+ }
+
clone->blocks_.reserve(blocks_.size());
for (const auto& b : blocks_) {
std::unique_ptr<BasicBlock> bb(b->Clone(ctx));
@@ -79,6 +84,12 @@
}
}
+ for (auto& di : debug_insts_in_header_) {
+ if (!di.WhileEachInst(f, run_on_debug_line_insts)) {
+ return false;
+ }
+ }
+
for (auto& bb : blocks_) {
if (!bb->WhileEachInst(f, run_on_debug_line_insts)) {
return false;
@@ -106,6 +117,12 @@
}
}
+ for (const auto& di : debug_insts_in_header_) {
+ if (!di.WhileEachInst(f, run_on_debug_line_insts)) {
+ return false;
+ }
+ }
+
for (const auto& bb : blocks_) {
if (!static_cast<const BasicBlock*>(bb.get())->WhileEachInst(
f, run_on_debug_line_insts)) {
diff --git a/third_party/SPIRV-Tools/source/opt/function.h b/third_party/SPIRV-Tools/source/opt/function.h
index 3908568..f208d8e 100644
--- a/third_party/SPIRV-Tools/source/opt/function.h
+++ b/third_party/SPIRV-Tools/source/opt/function.h
@@ -56,6 +56,8 @@
// Appends a parameter to this function.
inline void AddParameter(std::unique_ptr<Instruction> p);
+ // Appends a debug instruction in function header to this function.
+ inline void AddDebugInstructionInHeader(std::unique_ptr<Instruction> p);
// Appends a basic block to this function.
inline void AddBasicBlock(std::unique_ptr<BasicBlock> b);
// Appends a basic block to this function at the position |ip|.
@@ -151,6 +153,8 @@
std::unique_ptr<Instruction> def_inst_;
// All parameters to this function.
std::vector<std::unique_ptr<Instruction>> params_;
+ // All debug instructions in this function's header.
+ InstructionList debug_insts_in_header_;
// All basic blocks inside this function in specification order
std::vector<std::unique_ptr<BasicBlock>> blocks_;
// The OpFunctionEnd instruction.
@@ -167,6 +171,11 @@
params_.emplace_back(std::move(p));
}
+inline void Function::AddDebugInstructionInHeader(
+ std::unique_ptr<Instruction> p) {
+ debug_insts_in_header_.push_back(std::move(p));
+}
+
inline void Function::AddBasicBlock(std::unique_ptr<BasicBlock> b) {
AddBasicBlock(std::move(b), end());
}
diff --git a/third_party/SPIRV-Tools/source/opt/inst_debug_printf_pass.cpp b/third_party/SPIRV-Tools/source/opt/inst_debug_printf_pass.cpp
new file mode 100644
index 0000000..c0e6bc3
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/opt/inst_debug_printf_pass.cpp
@@ -0,0 +1,266 @@
+// Copyright (c) 2020 The Khronos Group Inc.
+// Copyright (c) 2020 Valve Corporation
+// Copyright (c) 2020 LunarG Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "inst_debug_printf_pass.h"
+
+#include "spirv/unified1/NonSemanticDebugPrintf.h"
+
+namespace spvtools {
+namespace opt {
+
+void InstDebugPrintfPass::GenOutputValues(Instruction* val_inst,
+ std::vector<uint32_t>* val_ids,
+ InstructionBuilder* builder) {
+ uint32_t val_ty_id = val_inst->type_id();
+ analysis::TypeManager* type_mgr = context()->get_type_mgr();
+ analysis::Type* val_ty = type_mgr->GetType(val_ty_id);
+ switch (val_ty->kind()) {
+ case analysis::Type::kVector: {
+ analysis::Vector* v_ty = val_ty->AsVector();
+ const analysis::Type* c_ty = v_ty->element_type();
+ uint32_t c_ty_id = type_mgr->GetId(c_ty);
+ for (uint32_t c = 0; c < v_ty->element_count(); ++c) {
+ Instruction* c_inst = builder->AddIdLiteralOp(
+ c_ty_id, SpvOpCompositeExtract, val_inst->result_id(), c);
+ GenOutputValues(c_inst, val_ids, builder);
+ }
+ return;
+ }
+ case analysis::Type::kBool: {
+ // Select between uint32 zero or one
+ uint32_t zero_id = builder->GetUintConstantId(0);
+ uint32_t one_id = builder->GetUintConstantId(1);
+ Instruction* sel_inst = builder->AddTernaryOp(
+ GetUintId(), SpvOpSelect, val_inst->result_id(), one_id, zero_id);
+ val_ids->push_back(sel_inst->result_id());
+ return;
+ }
+ case analysis::Type::kFloat: {
+ analysis::Float* f_ty = val_ty->AsFloat();
+ switch (f_ty->width()) {
+ case 16: {
+ // Convert float16 to float32 and recurse
+ Instruction* f32_inst = builder->AddUnaryOp(
+ GetFloatId(), SpvOpFConvert, val_inst->result_id());
+ GenOutputValues(f32_inst, val_ids, builder);
+ return;
+ }
+ case 64: {
+ // Bitcast float64 to uint64 and recurse
+ Instruction* ui64_inst = builder->AddUnaryOp(
+ GetUint64Id(), SpvOpBitcast, val_inst->result_id());
+ GenOutputValues(ui64_inst, val_ids, builder);
+ return;
+ }
+ case 32: {
+ // Bitcase float32 to uint32
+ Instruction* bc_inst = builder->AddUnaryOp(GetUintId(), SpvOpBitcast,
+ val_inst->result_id());
+ val_ids->push_back(bc_inst->result_id());
+ return;
+ }
+ default:
+ assert(false && "unsupported float width");
+ return;
+ }
+ }
+ case analysis::Type::kInteger: {
+ analysis::Integer* i_ty = val_ty->AsInteger();
+ switch (i_ty->width()) {
+ case 64: {
+ Instruction* ui64_inst = val_inst;
+ if (i_ty->IsSigned()) {
+ // Bitcast sint64 to uint64
+ ui64_inst = builder->AddUnaryOp(GetUint64Id(), SpvOpBitcast,
+ val_inst->result_id());
+ }
+ // Break uint64 into 2x uint32
+ Instruction* lo_ui64_inst = builder->AddUnaryOp(
+ GetUintId(), SpvOpUConvert, ui64_inst->result_id());
+ Instruction* rshift_ui64_inst = builder->AddBinaryOp(
+ GetUint64Id(), SpvOpShiftRightLogical, ui64_inst->result_id(),
+ builder->GetUintConstantId(32));
+ Instruction* hi_ui64_inst = builder->AddUnaryOp(
+ GetUintId(), SpvOpUConvert, rshift_ui64_inst->result_id());
+ val_ids->push_back(lo_ui64_inst->result_id());
+ val_ids->push_back(hi_ui64_inst->result_id());
+ return;
+ }
+ case 8: {
+ Instruction* ui8_inst = val_inst;
+ if (i_ty->IsSigned()) {
+ // Bitcast sint8 to uint8
+ ui8_inst = builder->AddUnaryOp(GetUint8Id(), SpvOpBitcast,
+ val_inst->result_id());
+ }
+ // Convert uint8 to uint32
+ Instruction* ui32_inst = builder->AddUnaryOp(
+ GetUintId(), SpvOpUConvert, ui8_inst->result_id());
+ val_ids->push_back(ui32_inst->result_id());
+ return;
+ }
+ case 32: {
+ Instruction* ui32_inst = val_inst;
+ if (i_ty->IsSigned()) {
+ // Bitcast sint32 to uint32
+ ui32_inst = builder->AddUnaryOp(GetUintId(), SpvOpBitcast,
+ val_inst->result_id());
+ }
+ // uint32 needs no further processing
+ val_ids->push_back(ui32_inst->result_id());
+ return;
+ }
+ default:
+ // TODO(greg-lunarg): Support non-32-bit int
+ assert(false && "unsupported int width");
+ return;
+ }
+ }
+ default:
+ assert(false && "unsupported type");
+ return;
+ }
+}
+
+void InstDebugPrintfPass::GenOutputCode(
+ Instruction* printf_inst, uint32_t stage_idx,
+ std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
+ BasicBlock* back_blk_ptr = &*new_blocks->back();
+ InstructionBuilder builder(
+ context(), back_blk_ptr,
+ IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+ // Gen debug printf record validation-specific values. The format string
+ // will have its id written. Vectors will need to be broken down into
+ // component values. float16 will need to be converted to float32. Pointer
+ // and uint64 will need to be converted to two uint32 values. float32 will
+ // need to be bitcast to uint32. int32 will need to be bitcast to uint32.
+ std::vector<uint32_t> val_ids;
+ bool is_first_operand = false;
+ printf_inst->ForEachInId(
+ [&is_first_operand, &val_ids, &builder, this](const uint32_t* iid) {
+ // skip set operand
+ if (!is_first_operand) {
+ is_first_operand = true;
+ return;
+ }
+ Instruction* opnd_inst = get_def_use_mgr()->GetDef(*iid);
+ if (opnd_inst->opcode() == SpvOpString) {
+ uint32_t string_id_id = builder.GetUintConstantId(*iid);
+ val_ids.push_back(string_id_id);
+ } else {
+ GenOutputValues(opnd_inst, &val_ids, &builder);
+ }
+ });
+ GenDebugStreamWrite(uid2offset_[printf_inst->unique_id()], stage_idx, val_ids,
+ &builder);
+ context()->KillInst(printf_inst);
+}
+
+void InstDebugPrintfPass::GenDebugPrintfCode(
+ BasicBlock::iterator ref_inst_itr,
+ UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx,
+ std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
+ // If not DebugPrintf OpExtInst, return.
+ Instruction* printf_inst = &*ref_inst_itr;
+ if (printf_inst->opcode() != SpvOpExtInst) return;
+ if (printf_inst->GetSingleWordInOperand(0) != ext_inst_printf_id_) return;
+ if (printf_inst->GetSingleWordInOperand(1) !=
+ NonSemanticDebugPrintfDebugPrintf)
+ return;
+ // Initialize DefUse manager before dismantling module
+ (void)get_def_use_mgr();
+ // Move original block's preceding instructions into first new block
+ std::unique_ptr<BasicBlock> new_blk_ptr;
+ MovePreludeCode(ref_inst_itr, ref_block_itr, &new_blk_ptr);
+ new_blocks->push_back(std::move(new_blk_ptr));
+ // Generate instructions to output printf args to printf buffer
+ GenOutputCode(printf_inst, stage_idx, new_blocks);
+ // Caller expects at least two blocks with last block containing remaining
+ // code, so end block after instrumentation, create remainder block, and
+ // branch to it
+ uint32_t rem_blk_id = TakeNextId();
+ std::unique_ptr<Instruction> rem_label(NewLabel(rem_blk_id));
+ BasicBlock* back_blk_ptr = &*new_blocks->back();
+ InstructionBuilder builder(
+ context(), back_blk_ptr,
+ IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
+ (void)builder.AddBranch(rem_blk_id);
+ // Gen remainder block
+ new_blk_ptr.reset(new BasicBlock(std::move(rem_label)));
+ builder.SetInsertPoint(&*new_blk_ptr);
+ // Move original block's remaining code into remainder block and add
+ // to new blocks
+ MovePostludeCode(ref_block_itr, &*new_blk_ptr);
+ new_blocks->push_back(std::move(new_blk_ptr));
+}
+
+void InstDebugPrintfPass::InitializeInstDebugPrintf() {
+ // Initialize base class
+ InitializeInstrument();
+}
+
+Pass::Status InstDebugPrintfPass::ProcessImpl() {
+ // Perform printf instrumentation on each entry point function in module
+ InstProcessFunction pfn =
+ [this](BasicBlock::iterator ref_inst_itr,
+ UptrVectorIterator<BasicBlock> ref_block_itr, uint32_t stage_idx,
+ std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
+ return GenDebugPrintfCode(ref_inst_itr, ref_block_itr, stage_idx,
+ new_blocks);
+ };
+ (void)InstProcessEntryPointCallTree(pfn);
+ // Remove DebugPrintf OpExtInstImport instruction
+ Instruction* ext_inst_import_inst =
+ get_def_use_mgr()->GetDef(ext_inst_printf_id_);
+ context()->KillInst(ext_inst_import_inst);
+ // If no remaining non-semantic instruction sets, remove non-semantic debug
+ // info extension from module and feature manager
+ bool non_sem_set_seen = false;
+ for (auto c_itr = context()->module()->ext_inst_import_begin();
+ c_itr != context()->module()->ext_inst_import_end(); ++c_itr) {
+ const char* set_name =
+ reinterpret_cast<const char*>(&c_itr->GetInOperand(0).words[0]);
+ const char* non_sem_str = "NonSemantic.";
+ if (!strncmp(set_name, non_sem_str, strlen(non_sem_str))) {
+ non_sem_set_seen = true;
+ break;
+ }
+ }
+ if (!non_sem_set_seen) {
+ for (auto c_itr = context()->module()->extension_begin();
+ c_itr != context()->module()->extension_end(); ++c_itr) {
+ const char* ext_name =
+ reinterpret_cast<const char*>(&c_itr->GetInOperand(0).words[0]);
+ if (!strcmp(ext_name, "SPV_KHR_non_semantic_info")) {
+ context()->KillInst(&*c_itr);
+ break;
+ }
+ }
+ context()->get_feature_mgr()->RemoveExtension(kSPV_KHR_non_semantic_info);
+ }
+ return Status::SuccessWithChange;
+}
+
+Pass::Status InstDebugPrintfPass::Process() {
+ ext_inst_printf_id_ =
+ get_module()->GetExtInstImportId("NonSemantic.DebugPrintf");
+ if (ext_inst_printf_id_ == 0) return Status::SuccessWithoutChange;
+ InitializeInstDebugPrintf();
+ return ProcessImpl();
+}
+
+} // namespace opt
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/opt/inst_debug_printf_pass.h b/third_party/SPIRV-Tools/source/opt/inst_debug_printf_pass.h
new file mode 100644
index 0000000..2968a20
--- /dev/null
+++ b/third_party/SPIRV-Tools/source/opt/inst_debug_printf_pass.h
@@ -0,0 +1,96 @@
+// Copyright (c) 2020 The Khronos Group Inc.
+// Copyright (c) 2020 Valve Corporation
+// Copyright (c) 2020 LunarG Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef LIBSPIRV_OPT_INST_DEBUG_PRINTF_PASS_H_
+#define LIBSPIRV_OPT_INST_DEBUG_PRINTF_PASS_H_
+
+#include "instrument_pass.h"
+
+namespace spvtools {
+namespace opt {
+
+// This class/pass is designed to support the debug printf GPU-assisted layer
+// of https://github.com/KhronosGroup/Vulkan-ValidationLayers. Its internal and
+// external design may change as the layer evolves.
+class InstDebugPrintfPass : public InstrumentPass {
+ public:
+ // For test harness only
+ InstDebugPrintfPass()
+ : InstrumentPass(7, 23, kInstValidationIdDebugPrintf, 2) {}
+ // For all other interfaces
+ InstDebugPrintfPass(uint32_t desc_set, uint32_t shader_id)
+ : InstrumentPass(desc_set, shader_id, kInstValidationIdDebugPrintf, 2) {}
+
+ ~InstDebugPrintfPass() override = default;
+
+ // See optimizer.hpp for pass user documentation.
+ Status Process() override;
+
+ const char* name() const override { return "inst-printf-pass"; }
+
+ private:
+ // Generate instructions for OpDebugPrintf.
+ //
+ // If |ref_inst_itr| is an OpDebugPrintf, return in |new_blocks| the result
+ // of replacing it with buffer write instructions within its block at
+ // |ref_block_itr|. The instructions write a record to the printf
+ // output buffer stream including |function_idx, instruction_idx, stage_idx|
+ // and removes the OpDebugPrintf. The block at |ref_block_itr| can just be
+ // replaced with the block in |new_blocks|. Besides the buffer writes, this
+ // block will comprise all instructions preceding and following
+ // |ref_inst_itr|.
+ //
+ // This function is designed to be passed to
+ // InstrumentPass::InstProcessEntryPointCallTree(), which applies the
+ // function to each instruction in a module and replaces the instruction
+ // if warranted.
+ //
+ // This instrumentation function utilizes GenDebugStreamWrite() to write its
+ // error records. The validation-specific part of the error record will
+ // consist of a uint32 which is the id of the format string plus a sequence
+ // of uint32s representing the values of the remaining operands of the
+ // DebugPrintf.
+ void GenDebugPrintfCode(BasicBlock::iterator ref_inst_itr,
+ UptrVectorIterator<BasicBlock> ref_block_itr,
+ uint32_t stage_idx,
+ std::vector<std::unique_ptr<BasicBlock>>* new_blocks);
+
+ // Generate a sequence of uint32 instructions in |builder| (if necessary)
+ // representing the value of |val_inst|, which must be a buffer pointer, a
+ // uint64, or a scalar or vector of type uint32, float32 or float16. Append
+ // the ids of all values to the end of |val_ids|.
+ void GenOutputValues(Instruction* val_inst, std::vector<uint32_t>* val_ids,
+ InstructionBuilder* builder);
+
+ // Generate instructions to write a record containing the operands of
+ // |printf_inst| arguments to printf buffer, adding new code to the end of
+ // the last block in |new_blocks|. Kill OpDebugPrintf instruction.
+ void GenOutputCode(Instruction* printf_inst, uint32_t stage_idx,
+ std::vector<std::unique_ptr<BasicBlock>>* new_blocks);
+
+ // Initialize state for instrumenting bindless checking
+ void InitializeInstDebugPrintf();
+
+ // Apply GenDebugPrintfCode to every instruction in module.
+ Pass::Status ProcessImpl();
+
+ uint32_t ext_inst_printf_id_;
+};
+
+} // namespace opt
+} // namespace spvtools
+
+#endif // LIBSPIRV_OPT_INST_DEBUG_PRINTF_PASS_H_
diff --git a/third_party/SPIRV-Tools/source/opt/instruction.cpp b/third_party/SPIRV-Tools/source/opt/instruction.cpp
index 49f9142..3ce38a9 100644
--- a/third_party/SPIRV-Tools/source/opt/instruction.cpp
+++ b/third_party/SPIRV-Tools/source/opt/instruction.cpp
@@ -16,6 +16,7 @@
#include <initializer_list>
+#include "OpenCLDebugInfo100.h"
#include "source/disassemble.h"
#include "source/opt/fold.h"
#include "source/opt/ir_context.h"
@@ -30,6 +31,11 @@
const uint32_t kLoadBaseIndex = 0;
const uint32_t kVariableStorageClassIndex = 0;
const uint32_t kTypeImageSampledIndex = 5;
+
+// Constants for OpenCL.DebugInfo.100 extension instructions.
+const uint32_t kDebugScopeNumWords = 7;
+const uint32_t kDebugScopeNumWordsWithoutInlinedAt = 6;
+const uint32_t kDebugNoScopeNumWords = 5;
} // namespace
Instruction::Instruction(IRContext* c)
@@ -38,7 +44,8 @@
opcode_(SpvOpNop),
has_type_id_(false),
has_result_id_(false),
- unique_id_(c->TakeNextUniqueId()) {}
+ unique_id_(c->TakeNextUniqueId()),
+ dbg_scope_(kNoDebugScope, kNoInlinedAt) {}
Instruction::Instruction(IRContext* c, SpvOp op)
: utils::IntrusiveNodeBase<Instruction>(),
@@ -46,7 +53,8 @@
opcode_(op),
has_type_id_(false),
has_result_id_(false),
- unique_id_(c->TakeNextUniqueId()) {}
+ unique_id_(c->TakeNextUniqueId()),
+ dbg_scope_(kNoDebugScope, kNoInlinedAt) {}
Instruction::Instruction(IRContext* c, const spv_parsed_instruction_t& inst,
std::vector<Instruction>&& dbg_line)
@@ -55,7 +63,8 @@
has_type_id_(inst.type_id != 0),
has_result_id_(inst.result_id != 0),
unique_id_(c->TakeNextUniqueId()),
- dbg_line_insts_(std::move(dbg_line)) {
+ dbg_line_insts_(std::move(dbg_line)),
+ dbg_scope_(kNoDebugScope, kNoInlinedAt) {
assert((!IsDebugLineInst(opcode_) || dbg_line.empty()) &&
"Op(No)Line attaching to Op(No)Line found");
for (uint32_t i = 0; i < inst.num_operands; ++i) {
@@ -67,6 +76,23 @@
}
}
+Instruction::Instruction(IRContext* c, const spv_parsed_instruction_t& inst,
+ const DebugScope& dbg_scope)
+ : context_(c),
+ opcode_(static_cast<SpvOp>(inst.opcode)),
+ has_type_id_(inst.type_id != 0),
+ has_result_id_(inst.result_id != 0),
+ unique_id_(c->TakeNextUniqueId()),
+ dbg_scope_(dbg_scope) {
+ for (uint32_t i = 0; i < inst.num_operands; ++i) {
+ const auto& current_payload = inst.operands[i];
+ std::vector<uint32_t> words(
+ inst.words + current_payload.offset,
+ inst.words + current_payload.offset + current_payload.num_words);
+ operands_.emplace_back(current_payload.type, std::move(words));
+ }
+}
+
Instruction::Instruction(IRContext* c, SpvOp op, uint32_t ty_id,
uint32_t res_id, const OperandList& in_operands)
: utils::IntrusiveNodeBase<Instruction>(),
@@ -75,7 +101,8 @@
has_type_id_(ty_id != 0),
has_result_id_(res_id != 0),
unique_id_(c->TakeNextUniqueId()),
- operands_() {
+ operands_(),
+ dbg_scope_(kNoDebugScope, kNoInlinedAt) {
if (has_type_id_) {
operands_.emplace_back(spv_operand_type_t::SPV_OPERAND_TYPE_TYPE_ID,
std::initializer_list<uint32_t>{ty_id});
@@ -94,7 +121,12 @@
has_result_id_(that.has_result_id_),
unique_id_(that.unique_id_),
operands_(std::move(that.operands_)),
- dbg_line_insts_(std::move(that.dbg_line_insts_)) {}
+ dbg_line_insts_(std::move(that.dbg_line_insts_)),
+ dbg_scope_(that.dbg_scope_) {
+ for (auto& i : dbg_line_insts_) {
+ i.dbg_scope_ = that.dbg_scope_;
+ }
+}
Instruction& Instruction::operator=(Instruction&& that) {
opcode_ = that.opcode_;
@@ -103,6 +135,7 @@
unique_id_ = that.unique_id_;
operands_ = std::move(that.operands_);
dbg_line_insts_ = std::move(that.dbg_line_insts_);
+ dbg_scope_ = that.dbg_scope_;
return *this;
}
@@ -114,6 +147,7 @@
clone->unique_id_ = c->TakeNextUniqueId();
clone->operands_ = operands_;
clone->dbg_line_insts_ = dbg_line_insts_;
+ clone->dbg_scope_ = dbg_scope_;
return clone;
}
@@ -198,6 +232,14 @@
Instruction* base_type =
context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
+
+ // Unpack the optional layer of arraying.
+ if (base_type->opcode() == SpvOpTypeArray ||
+ base_type->opcode() == SpvOpTypeRuntimeArray) {
+ base_type = context()->get_def_use_mgr()->GetDef(
+ base_type->GetSingleWordInOperand(0));
+ }
+
if (base_type->opcode() != SpvOpTypeImage) {
return false;
}
@@ -224,6 +266,14 @@
Instruction* base_type =
context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
+
+ // Unpack the optional layer of arraying.
+ if (base_type->opcode() == SpvOpTypeArray ||
+ base_type->opcode() == SpvOpTypeRuntimeArray) {
+ base_type = context()->get_def_use_mgr()->GetDef(
+ base_type->GetSingleWordInOperand(0));
+ }
+
if (base_type->opcode() != SpvOpTypeImage) {
return false;
}
@@ -250,6 +300,14 @@
Instruction* base_type =
context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
+
+ // Unpack the optional layer of arraying.
+ if (base_type->opcode() == SpvOpTypeArray ||
+ base_type->opcode() == SpvOpTypeRuntimeArray) {
+ base_type = context()->get_def_use_mgr()->GetDef(
+ base_type->GetSingleWordInOperand(0));
+ }
+
if (base_type->opcode() != SpvOpTypeImage) {
return false;
}
@@ -273,6 +331,13 @@
Instruction* base_type =
context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
+ // Unpack the optional layer of arraying.
+ if (base_type->opcode() == SpvOpTypeArray ||
+ base_type->opcode() == SpvOpTypeRuntimeArray) {
+ base_type = context()->get_def_use_mgr()->GetDef(
+ base_type->GetSingleWordInOperand(0));
+ }
+
if (base_type->opcode() != SpvOpTypeStruct) {
return false;
}
@@ -306,6 +371,14 @@
Instruction* base_type =
context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1));
+
+ // Unpack the optional layer of arraying.
+ if (base_type->opcode() == SpvOpTypeArray ||
+ base_type->opcode() == SpvOpTypeRuntimeArray) {
+ base_type = context()->get_def_use_mgr()->GetDef(
+ base_type->GetSingleWordInOperand(0));
+ }
+
if (base_type->opcode() != SpvOpTypeStruct) {
return false;
}
@@ -735,5 +808,28 @@
}
}
+void DebugScope::ToBinary(uint32_t type_id, uint32_t result_id,
+ uint32_t ext_set,
+ std::vector<uint32_t>* binary) const {
+ uint32_t num_words = kDebugScopeNumWords;
+ OpenCLDebugInfo100Instructions dbg_opcode = OpenCLDebugInfo100DebugScope;
+ if (GetLexicalScope() == kNoDebugScope) {
+ num_words = kDebugNoScopeNumWords;
+ dbg_opcode = OpenCLDebugInfo100DebugNoScope;
+ } else if (GetInlinedAt() == kNoInlinedAt) {
+ num_words = kDebugScopeNumWordsWithoutInlinedAt;
+ }
+ std::vector<uint32_t> operands = {
+ (num_words << 16) | static_cast<uint16_t>(SpvOpExtInst),
+ type_id,
+ result_id,
+ ext_set,
+ static_cast<uint32_t>(dbg_opcode),
+ };
+ binary->insert(binary->end(), operands.begin(), operands.end());
+ if (GetLexicalScope() != kNoDebugScope) binary->push_back(GetLexicalScope());
+ if (GetInlinedAt() != kNoInlinedAt) binary->push_back(GetInlinedAt());
+}
+
} // namespace opt
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/source/opt/instruction.h b/third_party/SPIRV-Tools/source/opt/instruction.h
index 322e0aa..a3342c6 100644
--- a/third_party/SPIRV-Tools/source/opt/instruction.h
+++ b/third_party/SPIRV-Tools/source/opt/instruction.h
@@ -32,6 +32,9 @@
#include "source/opt/reflect.h"
#include "spirv-tools/libspirv.h"
+const uint32_t kNoDebugScope = 0;
+const uint32_t kNoInlinedAt = 0;
+
namespace spvtools {
namespace opt {
@@ -80,6 +83,15 @@
spv_operand_type_t type; // Type of this logical operand.
OperandData words; // Binary segments of this logical operand.
+ // Returns a string operand as a C-style string.
+ const char* AsCString() const {
+ assert(type == SPV_OPERAND_TYPE_LITERAL_STRING);
+ return reinterpret_cast<const char*>(words.data());
+ }
+
+ // Returns a string operand as a std::string.
+ std::string AsString() const { return AsCString(); }
+
friend bool operator==(const Operand& o1, const Operand& o2) {
return o1.type == o2.type && o1.words == o2.words;
}
@@ -91,6 +103,44 @@
return !(o1 == o2);
}
+// This structure is used to represent a DebugScope instruction from
+// the OpenCL.100.DebugInfo extened instruction set. Note that we can
+// ignore the result id of DebugScope instruction because it is not
+// used for anything. We do not keep it to reduce the size of
+// structure.
+// TODO: Let validator check that the result id is not used anywhere.
+class DebugScope {
+ public:
+ DebugScope(uint32_t lexical_scope, uint32_t inlined_at)
+ : lexical_scope_(lexical_scope), inlined_at_(inlined_at) {}
+
+ inline bool operator!=(const DebugScope& d) const {
+ return lexical_scope_ != d.lexical_scope_ || inlined_at_ != d.inlined_at_;
+ }
+
+ // Accessor functions for |lexical_scope_|.
+ uint32_t GetLexicalScope() const { return lexical_scope_; }
+ void SetLexicalScope(uint32_t scope) { lexical_scope_ = scope; }
+
+ // Accessor functions for |inlined_at_|.
+ uint32_t GetInlinedAt() const { return inlined_at_; }
+ void SetInlinedAt(uint32_t at) { inlined_at_ = at; }
+
+ // Pushes the binary segments for this DebugScope instruction into
+ // the back of *|binary|.
+ void ToBinary(uint32_t type_id, uint32_t result_id, uint32_t ext_set,
+ std::vector<uint32_t>* binary) const;
+
+ private:
+ // The result id of the lexical scope in which this debug scope is
+ // contained. The value is kNoDebugScope if there is no scope.
+ uint32_t lexical_scope_;
+
+ // The result id of DebugInlinedAt if instruction in this debug scope
+ // is inlined. The value is kNoInlinedAt if it is not inlined.
+ uint32_t inlined_at_;
+};
+
// A SPIR-V instruction. It contains the opcode and any additional logical
// operand, including the result id (if any) and result type id (if any). It
// may also contain line-related debug instruction (OpLine, OpNoLine) directly
@@ -111,7 +161,8 @@
opcode_(SpvOpNop),
has_type_id_(false),
has_result_id_(false),
- unique_id_(0) {}
+ unique_id_(0),
+ dbg_scope_(kNoDebugScope, kNoInlinedAt) {}
// Creates a default OpNop instruction.
Instruction(IRContext*);
@@ -125,6 +176,9 @@
Instruction(IRContext* c, const spv_parsed_instruction_t& inst,
std::vector<Instruction>&& dbg_line = {});
+ Instruction(IRContext* c, const spv_parsed_instruction_t& inst,
+ const DebugScope& dbg_scope);
+
// Creates an instruction with the given opcode |op|, type id: |ty_id|,
// result id: |res_id| and input operands: |in_operands|.
Instruction(IRContext* c, SpvOp op, uint32_t ty_id, uint32_t res_id,
@@ -221,6 +275,9 @@
// Sets the result id
inline void SetResultId(uint32_t res_id);
inline bool HasResultId() const { return has_result_id_; }
+ // Sets DebugScope.
+ inline void SetDebugScope(const DebugScope& scope);
+ inline const DebugScope& GetDebugScope() const { return dbg_scope_; }
// Remove the |index|-th operand
void RemoveOperand(uint32_t index) {
operands_.erase(operands_.begin() + index);
@@ -473,6 +530,9 @@
// empty.
std::vector<Instruction> dbg_line_insts_;
+ // DebugScope that wraps this instruction.
+ DebugScope dbg_scope_;
+
friend InstructionList;
};
@@ -544,6 +604,13 @@
operands_[ridx].words = {res_id};
}
+inline void Instruction::SetDebugScope(const DebugScope& scope) {
+ dbg_scope_ = scope;
+ for (auto& i : dbg_line_insts_) {
+ i.dbg_scope_ = scope;
+ }
+}
+
inline void Instruction::SetResultType(uint32_t ty_id) {
// TODO(dsinclair): Allow setting a type id if there wasn't one
// previously. Need to make room in the operands_ array to place the result,
diff --git a/third_party/SPIRV-Tools/source/opt/instrument_pass.cpp b/third_party/SPIRV-Tools/source/opt/instrument_pass.cpp
index b1a6edb..c8c6c21 100644
--- a/third_party/SPIRV-Tools/source/opt/instrument_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/instrument_pass.cpp
@@ -380,6 +380,8 @@
return kDebugOutputBindingStream;
case kInstValidationIdBuffAddr:
return kDebugOutputBindingStream;
+ case kInstValidationIdDebugPrintf:
+ return kDebugOutputPrintfStream;
default:
assert(false && "unexpected validation id");
}
@@ -529,6 +531,16 @@
return input_buffer_id_;
}
+uint32_t InstrumentPass::GetFloatId() {
+ if (float_id_ == 0) {
+ analysis::TypeManager* type_mgr = context()->get_type_mgr();
+ analysis::Float float_ty(32);
+ analysis::Type* reg_float_ty = type_mgr->GetRegisteredType(&float_ty);
+ float_id_ = type_mgr->GetTypeInstruction(reg_float_ty);
+ }
+ return float_id_;
+}
+
uint32_t InstrumentPass::GetVec4FloatId() {
if (v4float_id_ == 0) {
analysis::TypeManager* type_mgr = context()->get_type_mgr();
@@ -561,6 +573,16 @@
return uint64_id_;
}
+uint32_t InstrumentPass::GetUint8Id() {
+ if (uint8_id_ == 0) {
+ analysis::TypeManager* type_mgr = context()->get_type_mgr();
+ analysis::Integer uint8_ty(8, false);
+ analysis::Type* reg_uint8_ty = type_mgr->GetRegisteredType(&uint8_ty);
+ uint8_id_ = type_mgr->GetTypeInstruction(reg_uint8_ty);
+ }
+ return uint8_id_;
+}
+
uint32_t InstrumentPass::GetVecUintId(uint32_t len) {
analysis::TypeManager* type_mgr = context()->get_type_mgr();
analysis::Integer uint_ty(32, false);
@@ -606,21 +628,22 @@
// Total param count is common params plus validation-specific
// params
uint32_t param_cnt = kInstCommonParamCnt + val_spec_param_cnt;
- if (output_func_id_ == 0) {
+ if (param2output_func_id_[param_cnt] == 0) {
// Create function
- output_func_id_ = TakeNextId();
+ param2output_func_id_[param_cnt] = TakeNextId();
analysis::TypeManager* type_mgr = context()->get_type_mgr();
std::vector<const analysis::Type*> param_types;
for (uint32_t c = 0; c < param_cnt; ++c)
param_types.push_back(type_mgr->GetType(GetUintId()));
analysis::Function func_ty(type_mgr->GetType(GetVoidId()), param_types);
analysis::Type* reg_func_ty = type_mgr->GetRegisteredType(&func_ty);
- std::unique_ptr<Instruction> func_inst(new Instruction(
- get_module()->context(), SpvOpFunction, GetVoidId(), output_func_id_,
- {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
- {SpvFunctionControlMaskNone}},
- {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
- {type_mgr->GetTypeInstruction(reg_func_ty)}}}));
+ std::unique_ptr<Instruction> func_inst(
+ new Instruction(get_module()->context(), SpvOpFunction, GetVoidId(),
+ param2output_func_id_[param_cnt],
+ {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
+ {SpvFunctionControlMaskNone}},
+ {spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+ {type_mgr->GetTypeInstruction(reg_func_ty)}}}));
get_def_use_mgr()->AnalyzeInstDefUse(&*func_inst);
std::unique_ptr<Function> output_func =
MakeUnique<Function>(std::move(func_inst));
@@ -709,10 +732,8 @@
get_def_use_mgr()->AnalyzeInstDefUse(&*func_end_inst);
output_func->SetFunctionEnd(std::move(func_end_inst));
context()->AddFunction(std::move(output_func));
- output_func_param_cnt_ = param_cnt;
}
- assert(param_cnt == output_func_param_cnt_ && "bad arg count");
- return output_func_id_;
+ return param2output_func_id_[param_cnt];
}
uint32_t InstrumentPass::GetDirectReadFunctionId(uint32_t param_cnt) {
@@ -848,7 +869,7 @@
std::unordered_set<uint32_t> done;
// Don't process input and output functions
for (auto& ifn : param2input_func_id_) done.insert(ifn.second);
- if (output_func_id_ != 0) done.insert(output_func_id_);
+ for (auto& ofn : param2output_func_id_) done.insert(ofn.second);
// Process all functions from roots
while (!roots->empty()) {
const uint32_t fi = roots->front();
@@ -926,12 +947,12 @@
output_buffer_id_ = 0;
output_buffer_ptr_id_ = 0;
input_buffer_ptr_id_ = 0;
- output_func_id_ = 0;
- output_func_param_cnt_ = 0;
input_buffer_id_ = 0;
+ float_id_ = 0;
v4float_id_ = 0;
uint_id_ = 0;
uint64_id_ = 0;
+ uint8_id_ = 0;
v4uint_id_ = 0;
v3uint_id_ = 0;
bool_id_ = 0;
@@ -944,6 +965,10 @@
id2function_.clear();
id2block_.clear();
+ // clear maps
+ param2input_func_id_.clear();
+ param2output_func_id_.clear();
+
// Initialize function and block maps.
for (auto& fn : *get_module()) {
id2function_[fn.result_id()] = &fn;
diff --git a/third_party/SPIRV-Tools/source/opt/instrument_pass.h b/third_party/SPIRV-Tools/source/opt/instrument_pass.h
index 02568fb..11afdce 100644
--- a/third_party/SPIRV-Tools/source/opt/instrument_pass.h
+++ b/third_party/SPIRV-Tools/source/opt/instrument_pass.h
@@ -61,6 +61,7 @@
// its output buffers.
static const uint32_t kInstValidationIdBindless = 0;
static const uint32_t kInstValidationIdBuffAddr = 1;
+static const uint32_t kInstValidationIdDebugPrintf = 2;
class InstrumentPass : public Pass {
using cbb_ptr = const BasicBlock*;
@@ -227,9 +228,12 @@
// Return id for 32-bit unsigned type
uint32_t GetUintId();
- // Return id for 32-bit unsigned type
+ // Return id for 64-bit unsigned type
uint32_t GetUint64Id();
+ // Return id for 8-bit unsigned type
+ uint32_t GetUint8Id();
+
// Return id for 32-bit unsigned type
uint32_t GetBoolId();
@@ -267,6 +271,9 @@
// Return id for debug input buffer
uint32_t GetInputBufferId();
+ // Return id for 32-bit float type
+ uint32_t GetFloatId();
+
// Return id for v4float type
uint32_t GetVec4FloatId();
@@ -383,17 +390,17 @@
uint32_t input_buffer_ptr_id_;
// id for debug output function
- uint32_t output_func_id_;
+ std::unordered_map<uint32_t, uint32_t> param2output_func_id_;
// ids for debug input functions
std::unordered_map<uint32_t, uint32_t> param2input_func_id_;
- // param count for output function
- uint32_t output_func_param_cnt_;
-
// id for input buffer variable
uint32_t input_buffer_id_;
+ // id for 32-bit float type
+ uint32_t float_id_;
+
// id for v4float type
uint32_t v4float_id_;
@@ -406,9 +413,12 @@
// id for 32-bit unsigned type
uint32_t uint_id_;
- // id for 32-bit unsigned type
+ // id for 64-bit unsigned type
uint32_t uint64_id_;
+ // id for 8-bit unsigned type
+ uint32_t uint8_id_;
+
// id for bool type
uint32_t bool_id_;
diff --git a/third_party/SPIRV-Tools/source/opt/ir_context.cpp b/third_party/SPIRV-Tools/source/opt/ir_context.cpp
index 7bca29b..72993fd 100644
--- a/third_party/SPIRV-Tools/source/opt/ir_context.cpp
+++ b/third_party/SPIRV-Tools/source/opt/ir_context.cpp
@@ -385,6 +385,8 @@
SpvOpTypeSampler,
SpvOpTypeSampledImage,
SpvOpTypeAccelerationStructureNV,
+ SpvOpTypeAccelerationStructureKHR,
+ SpvOpTypeRayQueryProvisionalKHR,
SpvOpTypeArray,
SpvOpTypeRuntimeArray,
SpvOpTypeStruct,
diff --git a/third_party/SPIRV-Tools/source/opt/ir_loader.cpp b/third_party/SPIRV-Tools/source/opt/ir_loader.cpp
index 836012f..fcde079 100644
--- a/third_party/SPIRV-Tools/source/opt/ir_loader.cpp
+++ b/third_party/SPIRV-Tools/source/opt/ir_loader.cpp
@@ -23,6 +23,10 @@
#include "source/opt/reflect.h"
#include "source/util/make_unique.h"
+static const uint32_t kExtInstSetIndex = 4;
+static const uint32_t kLexicalScopeIndex = 5;
+static const uint32_t kInlinedAtIndex = 6;
+
namespace spvtools {
namespace opt {
@@ -30,16 +34,60 @@
: consumer_(consumer),
module_(m),
source_("<instruction>"),
- inst_index_(0) {}
+ inst_index_(0),
+ last_dbg_scope_(kNoDebugScope, kNoInlinedAt) {}
bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) {
++inst_index_;
const auto opcode = static_cast<SpvOp>(inst->opcode);
if (IsDebugLineInst(opcode)) {
- dbg_line_info_.push_back(Instruction(module()->context(), *inst));
+ dbg_line_info_.push_back(
+ Instruction(module()->context(), *inst, last_dbg_scope_));
return true;
}
+ // If it is a DebugScope or DebugNoScope of debug extension, we do not
+ // create a new instruction, but simply keep the information in
+ // struct DebugScope.
+ if (opcode == SpvOpExtInst && spvExtInstIsDebugInfo(inst->ext_inst_type)) {
+ const uint32_t ext_inst_index = inst->words[kExtInstSetIndex];
+ if (inst->ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) {
+ const OpenCLDebugInfo100Instructions ext_inst_key =
+ OpenCLDebugInfo100Instructions(ext_inst_index);
+ if (ext_inst_key == OpenCLDebugInfo100DebugScope) {
+ uint32_t inlined_at = 0;
+ if (inst->num_words > kInlinedAtIndex)
+ inlined_at = inst->words[kInlinedAtIndex];
+ last_dbg_scope_ =
+ DebugScope(inst->words[kLexicalScopeIndex], inlined_at);
+ module()->SetContainsDebugScope();
+ return true;
+ }
+ if (ext_inst_key == OpenCLDebugInfo100DebugNoScope) {
+ last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt);
+ module()->SetContainsDebugScope();
+ return true;
+ }
+ } else {
+ const DebugInfoInstructions ext_inst_key =
+ DebugInfoInstructions(ext_inst_index);
+ if (ext_inst_key == DebugInfoDebugScope) {
+ uint32_t inlined_at = 0;
+ if (inst->num_words > kInlinedAtIndex)
+ inlined_at = inst->words[kInlinedAtIndex];
+ last_dbg_scope_ =
+ DebugScope(inst->words[kLexicalScopeIndex], inlined_at);
+ module()->SetContainsDebugScope();
+ return true;
+ }
+ if (ext_inst_key == DebugInfoDebugNoScope) {
+ last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt);
+ module()->SetContainsDebugScope();
+ return true;
+ }
+ }
+ }
+
std::unique_ptr<Instruction> spv_inst(
new Instruction(module()->context(), *inst, std::move(dbg_line_info_)));
dbg_line_info_.clear();
@@ -90,6 +138,7 @@
block_->AddInstruction(std::move(spv_inst));
function_->AddBasicBlock(std::move(block_));
block_ = nullptr;
+ last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt);
} else {
if (function_ == nullptr) { // Outside function definition
SPIRV_ASSERT(consumer_, block_ == nullptr);
@@ -131,26 +180,32 @@
return false;
}
} else {
- if (block_ == nullptr) { // Inside function but outside blocks
- if (opcode != SpvOpFunctionParameter) {
- Errorf(consumer_, src, loc,
- "Non-OpFunctionParameter (opcode: %d) found inside "
- "function but outside basic block",
- opcode);
- return false;
- }
- function_->AddParameter(std::move(spv_inst));
- } else {
- if (opcode == SpvOpExtInst &&
- spvExtInstIsDebugInfo(inst->ext_inst_type)) {
- const uint32_t ext_inst_index = inst->words[4];
- if (inst->ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) {
- const OpenCLDebugInfo100Instructions ext_inst_key =
- OpenCLDebugInfo100Instructions(ext_inst_index);
- if (ext_inst_key != OpenCLDebugInfo100DebugScope &&
- ext_inst_key != OpenCLDebugInfo100DebugNoScope &&
- ext_inst_key != OpenCLDebugInfo100DebugDeclare &&
- ext_inst_key != OpenCLDebugInfo100DebugValue) {
+ if (opcode == SpvOpLoopMerge || opcode == SpvOpSelectionMerge)
+ last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt);
+ if (last_dbg_scope_.GetLexicalScope() != kNoDebugScope)
+ spv_inst->SetDebugScope(last_dbg_scope_);
+ if (opcode == SpvOpExtInst &&
+ spvExtInstIsDebugInfo(inst->ext_inst_type)) {
+ const uint32_t ext_inst_index = inst->words[kExtInstSetIndex];
+ if (inst->ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) {
+ const OpenCLDebugInfo100Instructions ext_inst_key =
+ OpenCLDebugInfo100Instructions(ext_inst_index);
+ switch (ext_inst_key) {
+ case OpenCLDebugInfo100DebugDeclare: {
+ if (block_ == nullptr) // Inside function but outside blocks
+ function_->AddDebugInstructionInHeader(std::move(spv_inst));
+ else
+ block_->AddInstruction(std::move(spv_inst));
+ break;
+ }
+ case OpenCLDebugInfo100DebugValue: {
+ if (block_ == nullptr) // Inside function but outside blocks
+ function_->AddDebugInstructionInHeader(std::move(spv_inst));
+ else
+ block_->AddInstruction(std::move(spv_inst));
+ break;
+ }
+ default: {
Errorf(consumer_, src, loc,
"Debug info extension instruction other than DebugScope, "
"DebugNoScope, DebugDeclare, and DebugValue found inside "
@@ -158,13 +213,26 @@
opcode);
return false;
}
- } else {
- const DebugInfoInstructions ext_inst_key =
- DebugInfoInstructions(ext_inst_index);
- if (ext_inst_key != DebugInfoDebugScope &&
- ext_inst_key != DebugInfoDebugNoScope &&
- ext_inst_key != DebugInfoDebugDeclare &&
- ext_inst_key != DebugInfoDebugValue) {
+ }
+ } else {
+ const DebugInfoInstructions ext_inst_key =
+ DebugInfoInstructions(ext_inst_index);
+ switch (ext_inst_key) {
+ case DebugInfoDebugDeclare: {
+ if (block_ == nullptr) // Inside function but outside blocks
+ function_->AddDebugInstructionInHeader(std::move(spv_inst));
+ else
+ block_->AddInstruction(std::move(spv_inst));
+ break;
+ }
+ case DebugInfoDebugValue: {
+ if (block_ == nullptr) // Inside function but outside blocks
+ function_->AddDebugInstructionInHeader(std::move(spv_inst));
+ else
+ block_->AddInstruction(std::move(spv_inst));
+ break;
+ }
+ default: {
Errorf(consumer_, src, loc,
"Debug info extension instruction other than DebugScope, "
"DebugNoScope, DebugDeclare, and DebugValue found inside "
@@ -174,7 +242,19 @@
}
}
}
- block_->AddInstruction(std::move(spv_inst));
+ } else {
+ if (block_ == nullptr) { // Inside function but outside blocks
+ if (opcode != SpvOpFunctionParameter) {
+ Errorf(consumer_, src, loc,
+ "Non-OpFunctionParameter (opcode: %d) found inside "
+ "function but outside basic block",
+ opcode);
+ return false;
+ }
+ function_->AddParameter(std::move(spv_inst));
+ } else {
+ block_->AddInstruction(std::move(spv_inst));
+ }
}
}
}
diff --git a/third_party/SPIRV-Tools/source/opt/ir_loader.h b/third_party/SPIRV-Tools/source/opt/ir_loader.h
index 940d7b0..5079921 100644
--- a/third_party/SPIRV-Tools/source/opt/ir_loader.h
+++ b/third_party/SPIRV-Tools/source/opt/ir_loader.h
@@ -78,6 +78,9 @@
std::unique_ptr<BasicBlock> block_;
// Line related debug instructions accumulated thus far.
std::vector<Instruction> dbg_line_info_;
+
+ // The last DebugScope information that IrLoader::AddInstruction() handled.
+ DebugScope last_dbg_scope_;
};
} // namespace opt
diff --git a/third_party/SPIRV-Tools/source/opt/local_access_chain_convert_pass.cpp b/third_party/SPIRV-Tools/source/opt/local_access_chain_convert_pass.cpp
index 1921596..0afe798 100644
--- a/third_party/SPIRV-Tools/source/opt/local_access_chain_convert_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/local_access_chain_convert_pass.cpp
@@ -371,6 +371,7 @@
"SPV_GOOGLE_hlsl_functionality1",
"SPV_GOOGLE_user_type",
"SPV_NV_shader_subgroup_partitioned",
+ "SPV_EXT_demote_to_helper_invocation",
"SPV_EXT_descriptor_indexing",
"SPV_NV_fragment_shader_barycentric",
"SPV_NV_compute_shader_derivatives",
@@ -378,6 +379,8 @@
"SPV_NV_shading_rate",
"SPV_NV_mesh_shader",
"SPV_NV_ray_tracing",
+ "SPV_KHR_ray_tracing",
+ "SPV_KHR_ray_query",
"SPV_EXT_fragment_invocation_density",
});
}
diff --git a/third_party/SPIRV-Tools/source/opt/local_single_block_elim_pass.cpp b/third_party/SPIRV-Tools/source/opt/local_single_block_elim_pass.cpp
index aebbd00..b5435bb 100644
--- a/third_party/SPIRV-Tools/source/opt/local_single_block_elim_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/local_single_block_elim_pass.cpp
@@ -248,6 +248,7 @@
"SPV_GOOGLE_hlsl_functionality1",
"SPV_GOOGLE_user_type",
"SPV_NV_shader_subgroup_partitioned",
+ "SPV_EXT_demote_to_helper_invocation",
"SPV_EXT_descriptor_indexing",
"SPV_NV_fragment_shader_barycentric",
"SPV_NV_compute_shader_derivatives",
@@ -255,6 +256,8 @@
"SPV_NV_shading_rate",
"SPV_NV_mesh_shader",
"SPV_NV_ray_tracing",
+ "SPV_KHR_ray_tracing",
+ "SPV_KHR_ray_query",
"SPV_EXT_fragment_invocation_density",
"SPV_EXT_physical_storage_buffer",
});
diff --git a/third_party/SPIRV-Tools/source/opt/local_single_store_elim_pass.cpp b/third_party/SPIRV-Tools/source/opt/local_single_store_elim_pass.cpp
index d6beeab..4c71ce1 100644
--- a/third_party/SPIRV-Tools/source/opt/local_single_store_elim_pass.cpp
+++ b/third_party/SPIRV-Tools/source/opt/local_single_store_elim_pass.cpp
@@ -118,6 +118,7 @@
"SPV_NV_shading_rate",
"SPV_NV_mesh_shader",
"SPV_NV_ray_tracing",
+ "SPV_KHR_ray_query",
"SPV_EXT_fragment_invocation_density",
"SPV_EXT_physical_storage_buffer",
});
diff --git a/third_party/SPIRV-Tools/source/opt/module.cpp b/third_party/SPIRV-Tools/source/opt/module.cpp
index 4403894..2959d3d 100644
--- a/third_party/SPIRV-Tools/source/opt/module.cpp
+++ b/third_party/SPIRV-Tools/source/opt/module.cpp
@@ -137,10 +137,27 @@
binary->push_back(header_.bound);
binary->push_back(header_.reserved);
- auto write_inst = [binary, skip_nop](const Instruction* i) {
- if (!(skip_nop && i->IsNop())) i->ToBinaryWithoutAttachedDebugInsts(binary);
+ size_t bound_idx = binary->size() - 2;
+ DebugScope last_scope(kNoDebugScope, kNoInlinedAt);
+ auto write_inst = [binary, skip_nop, &last_scope,
+ this](const Instruction* i) {
+ if (!(skip_nop && i->IsNop())) {
+ const auto& scope = i->GetDebugScope();
+ if (scope != last_scope) {
+ // Emit DebugScope |scope| to |binary|.
+ auto dbg_inst = ext_inst_debuginfo_.begin();
+ scope.ToBinary(dbg_inst->type_id(), context()->TakeNextId(),
+ dbg_inst->GetSingleWordOperand(2), binary);
+ last_scope = scope;
+ }
+
+ i->ToBinaryWithoutAttachedDebugInsts(binary);
+ }
};
ForEachInst(write_inst, true);
+
+ // We create new instructions for DebugScope. The bound must be updated.
+ binary->data()[bound_idx] = header_.bound;
}
uint32_t Module::ComputeIdBound() const {
diff --git a/third_party/SPIRV-Tools/source/opt/module.h b/third_party/SPIRV-Tools/source/opt/module.h
index fc53d35..2c96f02 100644
--- a/third_party/SPIRV-Tools/source/opt/module.h
+++ b/third_party/SPIRV-Tools/source/opt/module.h
@@ -17,6 +17,7 @@
#include <functional>
#include <memory>
+#include <unordered_map>
#include <utility>
#include <vector>
@@ -48,7 +49,7 @@
using const_inst_iterator = InstructionList::const_iterator;
// Creates an empty module with zero'd header.
- Module() : header_({}) {}
+ Module() : header_({}), contains_debug_scope_(false) {}
// Sets the header to the given |header|.
void SetHeader(const ModuleHeader& header) { header_ = header; }
@@ -118,6 +119,10 @@
// Appends a function to this module.
inline void AddFunction(std::unique_ptr<Function> f);
+ // Sets |contains_debug_scope_| as true.
+ inline void SetContainsDebugScope();
+ inline bool ContainsDebugScope() { return contains_debug_scope_; }
+
// Returns a vector of pointers to type-declaration instructions in this
// module.
std::vector<Instruction*> GetTypes();
@@ -295,6 +300,9 @@
// If the module ends with Op*Line instruction, they will not be attached to
// any instruction. We record them here, so they will not be lost.
std::vector<Instruction> trailing_dbg_line_info_;
+
+ // This module contains DebugScope or DebugNoScope.
+ bool contains_debug_scope_;
};
// Pretty-prints |module| to |str|. Returns |str|.
@@ -356,6 +364,8 @@
functions_.emplace_back(std::move(f));
}
+inline void Module::SetContainsDebugScope() { contains_debug_scope_ = true; }
+
inline Module::inst_iterator Module::capability_begin() {
return capabilities_.begin();
}
diff --git a/third_party/SPIRV-Tools/source/opt/optimizer.cpp b/third_party/SPIRV-Tools/source/opt/optimizer.cpp
index 241aa75..0a937e8 100644
--- a/third_party/SPIRV-Tools/source/opt/optimizer.cpp
+++ b/third_party/SPIRV-Tools/source/opt/optimizer.cpp
@@ -425,6 +425,8 @@
RegisterPass(CreateConvertRelaxedToHalfPass());
} else if (pass_name == "relax-float-ops") {
RegisterPass(CreateRelaxFloatOpsPass());
+ } else if (pass_name == "inst-debug-printf") {
+ RegisterPass(CreateInstDebugPrintfPass(7, 23));
} else if (pass_name == "simplify-instructions") {
RegisterPass(CreateSimplificationPass());
} else if (pass_name == "ssa-rewrite") {
@@ -567,7 +569,12 @@
}
#ifndef NDEBUG
- if (status == opt::Pass::Status::SuccessWithoutChange) {
+ // 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
+ // contains DebugScope instructions.
+ if (status == opt::Pass::Status::SuccessWithoutChange &&
+ !context->module()->ContainsDebugScope()) {
std::vector<uint32_t> optimized_binary_with_nop;
context->module()->ToBinary(&optimized_binary_with_nop,
/* skip_nop = */ false);
@@ -886,6 +893,12 @@
input_init_enable, version));
}
+Optimizer::PassToken CreateInstDebugPrintfPass(uint32_t desc_set,
+ uint32_t shader_id) {
+ return MakeUnique<Optimizer::PassToken::Impl>(
+ MakeUnique<opt::InstDebugPrintfPass>(desc_set, shader_id));
+}
+
Optimizer::PassToken CreateInstBuffAddrCheckPass(uint32_t desc_set,
uint32_t shader_id,
uint32_t version) {
diff --git a/third_party/SPIRV-Tools/source/opt/passes.h b/third_party/SPIRV-Tools/source/opt/passes.h
index 1a3675c..5b4ab89 100644
--- a/third_party/SPIRV-Tools/source/opt/passes.h
+++ b/third_party/SPIRV-Tools/source/opt/passes.h
@@ -46,6 +46,7 @@
#include "source/opt/inline_opaque_pass.h"
#include "source/opt/inst_bindless_check_pass.h"
#include "source/opt/inst_buff_addr_check_pass.h"
+#include "source/opt/inst_debug_printf_pass.h"
#include "source/opt/legalize_vector_shuffle_pass.h"
#include "source/opt/licm_pass.h"
#include "source/opt/local_access_chain_convert_pass.h"
diff --git a/third_party/SPIRV-Tools/source/opt/reflect.h b/third_party/SPIRV-Tools/source/opt/reflect.h
index 8106442..51d23a7 100644
--- a/third_party/SPIRV-Tools/source/opt/reflect.h
+++ b/third_party/SPIRV-Tools/source/opt/reflect.h
@@ -46,6 +46,8 @@
return (opcode >= SpvOpTypeVoid && opcode <= SpvOpTypeForwardPointer) ||
opcode == SpvOpTypePipeStorage || opcode == SpvOpTypeNamedBarrier ||
opcode == SpvOpTypeAccelerationStructureNV ||
+ opcode == SpvOpTypeAccelerationStructureKHR ||
+ opcode == SpvOpTypeRayQueryProvisionalKHR ||
opcode == SpvOpTypeCooperativeMatrixNV;
}
inline bool IsConstantInst(SpvOp opcode) {
diff --git a/third_party/SPIRV-Tools/source/opt/type_manager.cpp b/third_party/SPIRV-Tools/source/opt/type_manager.cpp
index 166b828..27c7199 100644
--- a/third_party/SPIRV-Tools/source/opt/type_manager.cpp
+++ b/third_party/SPIRV-Tools/source/opt/type_manager.cpp
@@ -862,6 +862,9 @@
inst.GetSingleWordInOperand(2),
inst.GetSingleWordInOperand(3));
break;
+ case SpvOpTypeRayQueryProvisionalKHR:
+ type = new RayQueryProvisionalKHR();
+ break;
default:
SPIRV_UNIMPLEMENTED(consumer_, "unhandled type");
break;
diff --git a/third_party/SPIRV-Tools/source/opt/types.cpp b/third_party/SPIRV-Tools/source/opt/types.cpp
index 17f8fe9..426d3ea 100644
--- a/third_party/SPIRV-Tools/source/opt/types.cpp
+++ b/third_party/SPIRV-Tools/source/opt/types.cpp
@@ -128,6 +128,7 @@
DeclareKindCase(NamedBarrier);
DeclareKindCase(AccelerationStructureNV);
DeclareKindCase(CooperativeMatrixNV);
+ DeclareKindCase(RayQueryProvisionalKHR);
#undef DeclareKindCase
default:
assert(false && "Unhandled type");
@@ -173,6 +174,7 @@
DeclareKindCase(NamedBarrier);
DeclareKindCase(AccelerationStructureNV);
DeclareKindCase(CooperativeMatrixNV);
+ DeclareKindCase(RayQueryProvisionalKHR);
#undef DeclareKindCase
default:
assert(false && "Unhandled type");
@@ -223,6 +225,7 @@
DeclareKindCase(NamedBarrier);
DeclareKindCase(AccelerationStructureNV);
DeclareKindCase(CooperativeMatrixNV);
+ DeclareKindCase(RayQueryProvisionalKHR);
#undef DeclareKindCase
default:
assert(false && "Unhandled type");
diff --git a/third_party/SPIRV-Tools/source/opt/types.h b/third_party/SPIRV-Tools/source/opt/types.h
index 69071ea..ebeb476 100644
--- a/third_party/SPIRV-Tools/source/opt/types.h
+++ b/third_party/SPIRV-Tools/source/opt/types.h
@@ -59,6 +59,7 @@
class NamedBarrier;
class AccelerationStructureNV;
class CooperativeMatrixNV;
+class RayQueryProvisionalKHR;
// Abstract class for a SPIR-V type. It has a bunch of As<sublcass>() methods,
// which is used as a way to probe the actual <subclass>.
@@ -94,7 +95,8 @@
kPipeStorage,
kNamedBarrier,
kAccelerationStructureNV,
- kCooperativeMatrixNV
+ kCooperativeMatrixNV,
+ kRayQueryProvisionalKHR
};
Type(Kind k) : kind_(k) {}
@@ -199,6 +201,7 @@
DeclareCastMethod(NamedBarrier)
DeclareCastMethod(AccelerationStructureNV)
DeclareCastMethod(CooperativeMatrixNV)
+ DeclareCastMethod(RayQueryProvisionalKHR)
#undef DeclareCastMethod
protected:
@@ -659,6 +662,7 @@
DefineParameterlessType(PipeStorage, pipe_storage);
DefineParameterlessType(NamedBarrier, named_barrier);
DefineParameterlessType(AccelerationStructureNV, accelerationStructureNV);
+DefineParameterlessType(RayQueryProvisionalKHR, rayQueryProvisionalKHR);
#undef DefineParameterlessType
} // namespace analysis
diff --git a/third_party/SPIRV-Tools/source/val/validate_builtins.cpp b/third_party/SPIRV-Tools/source/val/validate_builtins.cpp
index 7623d49..d86c91e 100644
--- a/third_party/SPIRV-Tools/source/val/validate_builtins.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_builtins.cpp
@@ -2263,8 +2263,11 @@
spv_result_t BuiltInsValidator::ValidateVertexIdOrInstanceIdAtDefinition(
const Decoration& decoration, const Instruction& inst) {
const SpvBuiltIn label = SpvBuiltIn(decoration.params()[0]);
- bool allow_instance_id = _.HasCapability(SpvCapabilityRayTracingNV) &&
- label == SpvBuiltInInstanceId;
+ bool allow_instance_id =
+ (_.HasCapability(SpvCapabilityRayTracingNV) ||
+ _.HasCapability(SpvCapabilityRayTracingProvisionalKHR)) &&
+ label == SpvBuiltInInstanceId;
+
if (spvIsVulkanEnv(_.context()->target_env) && !allow_instance_id) {
return _.diag(SPV_ERROR_INVALID_DATA, &inst)
<< "Vulkan spec doesn't allow BuiltIn VertexId/InstanceId "
@@ -3085,7 +3088,8 @@
case SpvBuiltInWorldToObjectNV:
case SpvBuiltInHitTNV:
case SpvBuiltInHitKindNV:
- case SpvBuiltInIncomingRayFlagsNV: {
+ case SpvBuiltInIncomingRayFlagsNV:
+ case SpvBuiltInRayGeometryIndexKHR: {
// No validation rules (for the moment).
break;
}
diff --git a/third_party/SPIRV-Tools/source/val/validate_cfg.cpp b/third_party/SPIRV-Tools/source/val/validate_cfg.cpp
index f3019d1..1c279f6 100644
--- a/third_party/SPIRV-Tools/source/val/validate_cfg.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_cfg.cpp
@@ -62,6 +62,15 @@
}
}
+ if (!_.options()->before_hlsl_legalization) {
+ if (type_opcode == SpvOpTypeSampledImage ||
+ (_.HasCapability(SpvCapabilityShader) &&
+ (type_opcode == SpvOpTypeImage || type_opcode == SpvOpTypeSampler))) {
+ return _.diag(SPV_ERROR_INVALID_ID, inst)
+ << "Result type cannot be Op" << spvOpcodeString(type_opcode);
+ }
+ }
+
// Create a uniqued vector of predecessor ids for comparison against
// incoming values. OpBranchConditional %cond %label %label produces two
// predecessors in the CFG.
diff --git a/third_party/SPIRV-Tools/source/val/validate_extensions.cpp b/third_party/SPIRV-Tools/source/val/validate_extensions.cpp
index 070cc4c..1e311c1 100644
--- a/third_party/SPIRV-Tools/source/val/validate_extensions.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_extensions.cpp
@@ -14,12 +14,11 @@
// Validates correctness of extension SPIR-V instructions.
-#include "source/val/validate.h"
-
#include <sstream>
#include <string>
#include <vector>
+#include "OpenCLDebugInfo100.h"
#include "source/diagnostic.h"
#include "source/enum_string_mapping.h"
#include "source/extensions.h"
@@ -28,6 +27,7 @@
#include "source/opcode.h"
#include "source/spirv_target_env.h"
#include "source/val/instruction.h"
+#include "source/val/validate.h"
#include "source/val/validation_state.h"
namespace spvtools {
@@ -42,6 +42,144 @@
return 0;
}
+// Check that the operand of a debug info instruction |inst| at |word_index|
+// is a result id of an instruction with |expected_opcode|.
+spv_result_t ValidateOperandForDebugInfo(
+ ValidationState_t& _, const std::string& operand_name,
+ SpvOp expected_opcode, const Instruction* inst, uint32_t word_index,
+ const std::function<std::string()>& ext_inst_name) {
+ auto* operand = _.FindDef(inst->word(word_index));
+ if (operand->opcode() != expected_opcode) {
+ spv_opcode_desc desc = nullptr;
+ if (_.grammar().lookupOpcode(expected_opcode, &desc) != SPV_SUCCESS ||
+ !desc) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << ext_inst_name() << ": "
+ << "expected operand " << operand_name << " is invalid";
+ }
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << ext_inst_name() << ": "
+ << "expected operand " << operand_name << " must be a result id of "
+ << "Op" << desc->name;
+ }
+ return SPV_SUCCESS;
+}
+
+#define CHECK_OPERAND(NAME, opcode, index) \
+ do { \
+ auto result = ValidateOperandForDebugInfo(_, NAME, opcode, inst, index, \
+ ext_inst_name); \
+ if (result != SPV_SUCCESS) return result; \
+ } while (0)
+
+// True if the operand of a debug info instruction |inst| at |word_index|
+// satisifies |expectation| that is given as a function. Otherwise,
+// returns false.
+bool DoesDebugInfoOperandMatchExpectation(
+ const ValidationState_t& _,
+ const std::function<bool(OpenCLDebugInfo100Instructions)>& expectation,
+ const Instruction* inst, uint32_t word_index) {
+ auto* debug_inst = _.FindDef(inst->word(word_index));
+ if (debug_inst->opcode() != SpvOpExtInst ||
+ debug_inst->ext_inst_type() != SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 ||
+ !expectation(OpenCLDebugInfo100Instructions(debug_inst->word(4)))) {
+ return false;
+ }
+ return true;
+}
+
+// Check that the operand of a debug info instruction |inst| at |word_index|
+// is a result id of an debug info instruction whose debug instruction type
+// is |expected_debug_inst|.
+spv_result_t ValidateDebugInfoOperand(
+ ValidationState_t& _, const std::string& debug_inst_name,
+ OpenCLDebugInfo100Instructions expected_debug_inst, const Instruction* inst,
+ uint32_t word_index, const std::function<std::string()>& ext_inst_name) {
+ std::function<bool(OpenCLDebugInfo100Instructions)> expectation =
+ [expected_debug_inst](OpenCLDebugInfo100Instructions dbg_inst) {
+ return dbg_inst == expected_debug_inst;
+ };
+ if (DoesDebugInfoOperandMatchExpectation(_, expectation, inst, word_index))
+ return SPV_SUCCESS;
+
+ spv_ext_inst_desc desc = nullptr;
+ _.grammar().lookupExtInst(SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100,
+ expected_debug_inst, &desc);
+ if (_.grammar().lookupExtInst(SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100,
+ expected_debug_inst, &desc) != SPV_SUCCESS ||
+ !desc) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << ext_inst_name() << ": "
+ << "expected operand " << debug_inst_name << " is invalid";
+ }
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << ext_inst_name() << ": "
+ << "expected operand " << debug_inst_name << " must be a result id of "
+ << desc->name;
+}
+
+#define CHECK_DEBUG_OPERAND(NAME, debug_opcode, index) \
+ do { \
+ auto result = ValidateDebugInfoOperand(_, NAME, debug_opcode, inst, index, \
+ ext_inst_name); \
+ if (result != SPV_SUCCESS) return result; \
+ } while (0)
+
+// Check that the operand of a debug info instruction |inst| at |word_index|
+// is a result id of an debug info instruction with DebugTypeBasic.
+spv_result_t ValidateOperandBaseType(
+ ValidationState_t& _, const Instruction* inst, uint32_t word_index,
+ const std::function<std::string()>& ext_inst_name) {
+ return ValidateDebugInfoOperand(_, "Base Type",
+ OpenCLDebugInfo100DebugTypeBasic, inst,
+ word_index, ext_inst_name);
+}
+
+// Check that the operand of a debug info instruction |inst| at |word_index|
+// is a result id of a debug lexical scope instruction which is one of
+// DebugCompilationUnit, DebugFunction, DebugLexicalBlock, or
+// DebugTypeComposite.
+spv_result_t ValidateOperandLexicalScope(
+ ValidationState_t& _, const std::string& debug_inst_name,
+ const Instruction* inst, uint32_t word_index,
+ const std::function<std::string()>& ext_inst_name) {
+ std::function<bool(OpenCLDebugInfo100Instructions)> expectation =
+ [](OpenCLDebugInfo100Instructions dbg_inst) {
+ return dbg_inst == OpenCLDebugInfo100DebugCompilationUnit ||
+ dbg_inst == OpenCLDebugInfo100DebugFunction ||
+ dbg_inst == OpenCLDebugInfo100DebugLexicalBlock ||
+ dbg_inst == OpenCLDebugInfo100DebugTypeComposite;
+ };
+ if (DoesDebugInfoOperandMatchExpectation(_, expectation, inst, word_index))
+ return SPV_SUCCESS;
+
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << ext_inst_name() << ": "
+ << "expected operand " << debug_inst_name
+ << " must be a result id of a lexical scope";
+}
+
+// Check that the operand of a debug info instruction |inst| at |word_index|
+// is a result id of a debug type instruction (See DebugTypeXXX in
+// "4.3. Type instructions" section of OpenCL.DebugInfo.100 spec.
+spv_result_t ValidateOperandDebugType(
+ ValidationState_t& _, const std::string& debug_inst_name,
+ const Instruction* inst, uint32_t word_index,
+ const std::function<std::string()>& ext_inst_name) {
+ std::function<bool(OpenCLDebugInfo100Instructions)> expectation =
+ [](OpenCLDebugInfo100Instructions dbg_inst) {
+ return OpenCLDebugInfo100DebugTypeBasic <= dbg_inst &&
+ dbg_inst <= OpenCLDebugInfo100DebugTypePtrToMember;
+ };
+ if (DoesDebugInfoOperandMatchExpectation(_, expectation, inst, word_index))
+ return SPV_SUCCESS;
+
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << ext_inst_name() << ": "
+ << "expected operand " << debug_inst_name
+ << " is not a valid debug type";
+}
+
} // anonymous namespace
spv_result_t ValidateExtension(ValidationState_t& _, const Instruction* inst) {
@@ -2028,6 +2166,317 @@
break;
}
}
+ } else if (ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) {
+ if (!_.IsVoidType(result_type)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << ext_inst_name() << ": "
+ << "expected result type must be a result id of "
+ << "OpTypeVoid";
+ }
+
+ auto num_words = inst->words().size();
+
+ const OpenCLDebugInfo100Instructions ext_inst_key =
+ OpenCLDebugInfo100Instructions(ext_inst_index);
+ switch (ext_inst_key) {
+ case OpenCLDebugInfo100DebugInfoNone:
+ case OpenCLDebugInfo100DebugNoScope:
+ case OpenCLDebugInfo100DebugOperation:
+ // The binary parser validates the opcode for DebugInfoNone,
+ // DebugNoScope, DebugOperation, and the literal values don't need
+ // further checks.
+ break;
+ case OpenCLDebugInfo100DebugCompilationUnit: {
+ CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
+ break;
+ }
+ case OpenCLDebugInfo100DebugSource: {
+ CHECK_OPERAND("File", SpvOpString, 5);
+ if (num_words == 7) CHECK_OPERAND("Text", SpvOpString, 6);
+ break;
+ }
+ case OpenCLDebugInfo100DebugTypeBasic: {
+ CHECK_OPERAND("Name", SpvOpString, 5);
+ CHECK_OPERAND("Size", SpvOpConstant, 6);
+ // "Encoding" param is already validated by the binary parsing stage.
+ break;
+ }
+ case OpenCLDebugInfo100DebugTypePointer:
+ case OpenCLDebugInfo100DebugTypeQualifier: {
+ auto validate_base_type =
+ ValidateOperandBaseType(_, inst, 5, ext_inst_name);
+ if (validate_base_type != SPV_SUCCESS) return validate_base_type;
+ break;
+ }
+ case OpenCLDebugInfo100DebugTypeVector: {
+ auto validate_base_type =
+ ValidateOperandBaseType(_, inst, 5, ext_inst_name);
+ if (validate_base_type != SPV_SUCCESS) return validate_base_type;
+
+ uint32_t component_count = inst->word(6);
+ if (!component_count || component_count > 4) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << ext_inst_name() << ": Component Count must be positive "
+ << "integer less than or equal to 4";
+ }
+ break;
+ }
+ case OpenCLDebugInfo100DebugTypeArray: {
+ auto validate_base_type =
+ ValidateOperandDebugType(_, "Base Type", inst, 5, ext_inst_name);
+ if (validate_base_type != SPV_SUCCESS) return validate_base_type;
+ for (uint32_t i = 6; i < num_words; ++i) {
+ CHECK_OPERAND("Component Count", SpvOpConstant, i);
+ auto* component_count = _.FindDef(inst->word(i));
+ if (!_.IsIntScalarType(component_count->type_id()) ||
+ !component_count->word(3)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << ext_inst_name() << ": Component Count must be positive "
+ << "integer";
+ }
+ }
+ break;
+ }
+ case OpenCLDebugInfo100DebugTypedef: {
+ CHECK_OPERAND("Name", SpvOpString, 5);
+ auto validate_base_type =
+ ValidateOperandBaseType(_, inst, 6, ext_inst_name);
+ if (validate_base_type != SPV_SUCCESS) return validate_base_type;
+ CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
+ auto validate_parent =
+ ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
+ if (validate_parent != SPV_SUCCESS) return validate_parent;
+ break;
+ }
+ case OpenCLDebugInfo100DebugTypeFunction: {
+ auto* return_type = _.FindDef(inst->word(6));
+ if (return_type->opcode() != SpvOpTypeVoid) {
+ auto validate_return = ValidateOperandDebugType(
+ _, "Return Type", inst, 6, ext_inst_name);
+ if (validate_return != SPV_SUCCESS) return validate_return;
+ }
+ for (uint32_t word_index = 7; word_index < num_words; ++word_index) {
+ auto validate_param = ValidateOperandDebugType(
+ _, "Parameter Types", inst, word_index, ext_inst_name);
+ if (validate_param != SPV_SUCCESS) return validate_param;
+ }
+ break;
+ }
+ case OpenCLDebugInfo100DebugTypeEnum: {
+ CHECK_OPERAND("Name", SpvOpString, 5);
+ if (!DoesDebugInfoOperandMatchExpectation(
+ _,
+ [](OpenCLDebugInfo100Instructions dbg_inst) {
+ return dbg_inst == OpenCLDebugInfo100DebugInfoNone;
+ },
+ inst, 6)) {
+ auto validate_underlying_type = ValidateOperandDebugType(
+ _, "Underlying Types", inst, 6, ext_inst_name);
+ if (validate_underlying_type != SPV_SUCCESS)
+ return validate_underlying_type;
+ }
+ CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
+ auto validate_parent =
+ ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
+ if (validate_parent != SPV_SUCCESS) return validate_parent;
+ CHECK_OPERAND("Size", SpvOpConstant, 11);
+ auto* size = _.FindDef(inst->word(11));
+ if (!_.IsIntScalarType(size->type_id()) || !size->word(3)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << ext_inst_name() << ": expected operand Size is a "
+ << "positive integer";
+ }
+ for (uint32_t word_index = 13; word_index + 1 < num_words;
+ word_index += 2) {
+ CHECK_OPERAND("Value", SpvOpConstant, word_index);
+ CHECK_OPERAND("Name", SpvOpString, word_index + 1);
+ }
+ break;
+ }
+ case OpenCLDebugInfo100DebugTypeComposite: {
+ CHECK_OPERAND("Name", SpvOpString, 5);
+ CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
+ auto validate_parent =
+ ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
+ if (validate_parent != SPV_SUCCESS) return validate_parent;
+ CHECK_OPERAND("Linkage Name", SpvOpString, 11);
+ CHECK_OPERAND("Size", SpvOpConstant, 12);
+ for (uint32_t word_index = 14; word_index < num_words; ++word_index) {
+ if (!DoesDebugInfoOperandMatchExpectation(
+ _,
+ [](OpenCLDebugInfo100Instructions dbg_inst) {
+ return dbg_inst == OpenCLDebugInfo100DebugTypeMember ||
+ dbg_inst == OpenCLDebugInfo100DebugFunction ||
+ dbg_inst == OpenCLDebugInfo100DebugTypeInheritance;
+ },
+ inst, word_index)) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << ext_inst_name() << ": "
+ << "expected operand Members "
+ << "must be DebugTypeMember, DebugFunction, or "
+ "DebugTypeInheritance";
+ }
+ }
+ break;
+ }
+ case OpenCLDebugInfo100DebugTypeMember: {
+ CHECK_OPERAND("Name", SpvOpString, 5);
+ auto validate_type =
+ ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name);
+ if (validate_type != SPV_SUCCESS) return validate_type;
+ CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
+ CHECK_DEBUG_OPERAND("Parent", OpenCLDebugInfo100DebugTypeComposite, 10);
+ CHECK_OPERAND("Offset", SpvOpConstant, 11);
+ CHECK_OPERAND("Size", SpvOpConstant, 12);
+ if (num_words == 15) CHECK_OPERAND("Value", SpvOpConstant, 14);
+ break;
+ }
+ case OpenCLDebugInfo100DebugTypeInheritance: {
+ CHECK_DEBUG_OPERAND("Child", OpenCLDebugInfo100DebugTypeComposite, 5);
+ auto* debug_inst = _.FindDef(inst->word(5));
+ auto composite_type =
+ OpenCLDebugInfo100DebugCompositeType(debug_inst->word(6));
+ if (composite_type != OpenCLDebugInfo100Class &&
+ composite_type != OpenCLDebugInfo100Structure) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << ext_inst_name() << ": "
+ << "expected operand Child must be class or struct debug type";
+ }
+ CHECK_DEBUG_OPERAND("Parent", OpenCLDebugInfo100DebugTypeComposite, 6);
+ debug_inst = _.FindDef(inst->word(6));
+ composite_type =
+ OpenCLDebugInfo100DebugCompositeType(debug_inst->word(6));
+ if (composite_type != OpenCLDebugInfo100Class &&
+ composite_type != OpenCLDebugInfo100Structure) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << ext_inst_name() << ": "
+ << "expected operand Parent must be class or struct debug "
+ "type";
+ }
+ CHECK_OPERAND("Offset", SpvOpConstant, 7);
+ CHECK_OPERAND("Size", SpvOpConstant, 8);
+ break;
+ }
+ case OpenCLDebugInfo100DebugFunction: {
+ CHECK_OPERAND("Name", SpvOpString, 5);
+ auto validate_type =
+ ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name);
+ if (validate_type != SPV_SUCCESS) return validate_type;
+ CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
+ auto validate_parent =
+ ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
+ if (validate_parent != SPV_SUCCESS) return validate_parent;
+ CHECK_OPERAND("Linkage Name", SpvOpString, 11);
+ // TODO: The current OpenCL.100.DebugInfo spec says "Function
+ // is an OpFunction which is described by this instruction.".
+ // However, the function definition can be opted-out e.g.,
+ // inlining. We assume that Function operand can be a
+ // DebugInfoNone, but we must discuss it and update the spec.
+ if (!DoesDebugInfoOperandMatchExpectation(
+ _,
+ [](OpenCLDebugInfo100Instructions dbg_inst) {
+ return dbg_inst == OpenCLDebugInfo100DebugInfoNone;
+ },
+ inst, 14)) {
+ CHECK_OPERAND("Function", SpvOpFunction, 14);
+ }
+ if (num_words == 16) {
+ CHECK_DEBUG_OPERAND("Declaration",
+ OpenCLDebugInfo100DebugFunctionDeclaration, 15);
+ }
+ break;
+ }
+ case OpenCLDebugInfo100DebugFunctionDeclaration: {
+ CHECK_OPERAND("Name", SpvOpString, 5);
+ auto validate_type =
+ ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name);
+ if (validate_type != SPV_SUCCESS) return validate_type;
+ CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
+ auto validate_parent =
+ ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
+ if (validate_parent != SPV_SUCCESS) return validate_parent;
+ CHECK_OPERAND("Linkage Name", SpvOpString, 11);
+ break;
+ }
+ case OpenCLDebugInfo100DebugLexicalBlock: {
+ CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 5);
+ auto validate_parent =
+ ValidateOperandLexicalScope(_, "Parent", inst, 8, ext_inst_name);
+ if (validate_parent != SPV_SUCCESS) return validate_parent;
+ if (num_words == 10) CHECK_OPERAND("Name", SpvOpString, 9);
+ break;
+ }
+ case OpenCLDebugInfo100DebugScope: {
+ // TODO(https://gitlab.khronos.org/spirv/SPIR-V/issues/533): We are
+ // still in spec discussion about what must be "Scope" operand of
+ // DebugScope. Update this code if the conclusion is different.
+ auto validate_scope =
+ ValidateOperandLexicalScope(_, "Scope", inst, 5, ext_inst_name);
+ if (validate_scope != SPV_SUCCESS) return validate_scope;
+ if (num_words == 7) {
+ CHECK_DEBUG_OPERAND("Inlined At", OpenCLDebugInfo100DebugInlinedAt,
+ 6);
+ }
+ break;
+ }
+ case OpenCLDebugInfo100DebugLocalVariable: {
+ CHECK_OPERAND("Name", SpvOpString, 5);
+ auto validate_type =
+ ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name);
+ if (validate_type != SPV_SUCCESS) return validate_type;
+ CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
+ auto validate_parent =
+ ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
+ if (validate_parent != SPV_SUCCESS) return validate_parent;
+ break;
+ }
+ case OpenCLDebugInfo100DebugDeclare: {
+ 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.
+ auto* operand = _.FindDef(inst->word(6));
+ if (operand->opcode() != SpvOpVariable &&
+ operand->opcode() != SpvOpFunctionParameter) {
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
+ << ext_inst_name() << ": "
+ << "expected operand Variable must be a result id of "
+ "OpVariable or OpFunctionParameter";
+ }
+
+ CHECK_DEBUG_OPERAND("Expression", OpenCLDebugInfo100DebugExpression, 7);
+ break;
+ }
+ case OpenCLDebugInfo100DebugExpression: {
+ for (uint32_t word_index = 5; word_index < num_words; ++word_index) {
+ CHECK_DEBUG_OPERAND("Operation", OpenCLDebugInfo100DebugOperation,
+ word_index);
+ }
+ break;
+ }
+
+ // TODO: Add validation rules for remaining cases as well.
+ case OpenCLDebugInfo100DebugTypePtrToMember:
+ case OpenCLDebugInfo100DebugTypeTemplate:
+ case OpenCLDebugInfo100DebugTypeTemplateParameter:
+ case OpenCLDebugInfo100DebugTypeTemplateTemplateParameter:
+ case OpenCLDebugInfo100DebugTypeTemplateParameterPack:
+ case OpenCLDebugInfo100DebugGlobalVariable:
+ case OpenCLDebugInfo100DebugLexicalBlockDiscriminator:
+ case OpenCLDebugInfo100DebugInlinedAt:
+ case OpenCLDebugInfo100DebugInlinedVariable:
+ case OpenCLDebugInfo100DebugValue:
+ case OpenCLDebugInfo100DebugMacroDef:
+ case OpenCLDebugInfo100DebugMacroUndef:
+ case OpenCLDebugInfo100DebugImportedEntity:
+ break;
+ case OpenCLDebugInfo100InstructionsMax:
+ assert(0);
+ break;
+ }
}
return SPV_SUCCESS;
diff --git a/third_party/SPIRV-Tools/source/val/validate_id.cpp b/third_party/SPIRV-Tools/source/val/validate_id.cpp
index c171d31..e1a775a 100644
--- a/third_party/SPIRV-Tools/source/val/validate_id.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_id.cpp
@@ -171,8 +171,8 @@
const auto opcode = inst->opcode();
if (spvOpcodeGeneratesType(def->opcode()) &&
!spvOpcodeGeneratesType(opcode) && !spvOpcodeIsDebug(opcode) &&
- !inst->IsNonSemantic() && !spvOpcodeIsDecoration(opcode) &&
- opcode != SpvOpFunction &&
+ !inst->IsDebugInfo() && !inst->IsNonSemantic() &&
+ !spvOpcodeIsDecoration(opcode) && opcode != SpvOpFunction &&
opcode != SpvOpCooperativeMatrixLengthNV &&
!(opcode == SpvOpSpecConstantOp &&
inst->word(3) == SpvOpCooperativeMatrixLengthNV)) {
@@ -180,8 +180,8 @@
<< "Operand " << _.getIdName(operand_word)
<< " cannot be a type";
} else if (def->type_id() == 0 && !spvOpcodeGeneratesType(opcode) &&
- !spvOpcodeIsDebug(opcode) && !inst->IsNonSemantic() &&
- !spvOpcodeIsDecoration(opcode) &&
+ !spvOpcodeIsDebug(opcode) && !inst->IsDebugInfo() &&
+ !inst->IsNonSemantic() && !spvOpcodeIsDecoration(opcode) &&
!spvOpcodeIsBranch(opcode) && opcode != SpvOpPhi &&
opcode != SpvOpExtInst && opcode != SpvOpExtInstImport &&
opcode != SpvOpSelectionMerge &&
diff --git a/third_party/SPIRV-Tools/source/val/validate_image.cpp b/third_party/SPIRV-Tools/source/val/validate_image.cpp
index ed960d1..5b77058 100644
--- a/third_party/SPIRV-Tools/source/val/validate_image.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_image.cpp
@@ -847,6 +847,7 @@
case SpvOpImageSparseSampleDrefExplicitLod:
case SpvOpImageSparseGather:
case SpvOpImageSparseDrefGather:
+ case SpvOpCopyObject:
return true;
default:
return false;
diff --git a/third_party/SPIRV-Tools/source/val/validate_memory.cpp b/third_party/SPIRV-Tools/source/val/validate_memory.cpp
index bff8b20..1e1a38d 100644
--- a/third_party/SPIRV-Tools/source/val/validate_memory.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_memory.cpp
@@ -1,4 +1,6 @@
// Copyright (c) 2018 Google LLC.
+// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights
+// reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -533,7 +535,9 @@
if (!IsAllowedTypeOrArrayOfSame(
_, pointee,
{SpvOpTypeImage, SpvOpTypeSampler, SpvOpTypeSampledImage,
- SpvOpTypeAccelerationStructureNV})) {
+ SpvOpTypeAccelerationStructureNV,
+ SpvOpTypeAccelerationStructureKHR,
+ SpvOpTypeRayQueryProvisionalKHR})) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "UniformConstant OpVariable <id> '" << _.getIdName(inst->id())
<< "' has illegal type.\n"
@@ -542,6 +546,8 @@
<< "are used only as handles to refer to opaque resources. Such "
<< "variables must be typed as OpTypeImage, OpTypeSampler, "
<< "OpTypeSampledImage, OpTypeAccelerationStructureNV, "
+ "OpTypeAccelerationStructureKHR, "
+ "OpTypeRayQueryProvisionalKHR, "
<< "or an array of one of these types.";
}
}
diff --git a/third_party/SPIRV-Tools/source/val/validate_scopes.cpp b/third_party/SPIRV-Tools/source/val/validate_scopes.cpp
index 320d828..ea3ebcb 100644
--- a/third_party/SPIRV-Tools/source/val/validate_scopes.cpp
+++ b/third_party/SPIRV-Tools/source/val/validate_scopes.cpp
@@ -32,6 +32,7 @@
case SpvScopeSubgroup:
case SpvScopeInvocation:
case SpvScopeQueueFamilyKHR:
+ case SpvScopeShaderCallKHR:
return true;
case SpvScopeMax:
break;
@@ -143,6 +144,20 @@
<< spvOpcodeString(opcode)
<< ": in WebGPU environment Execution Scope is limited to "
<< "Workgroup";
+ } else {
+ _.function(inst->function()->id())
+ ->RegisterExecutionModelLimitation(
+ [](SpvExecutionModel model, std::string* message) {
+ if (model != SpvExecutionModelGLCompute) {
+ if (message) {
+ *message =
+ ": in WebGPU environment, Workgroup Execution Scope is "
+ "limited to GLCompute execution model";
+ }
+ return false;
+ }
+ return true;
+ });
}
}
@@ -261,6 +276,22 @@
}
break;
}
+
+ if (value == SpvScopeWorkgroup) {
+ _.function(inst->function()->id())
+ ->RegisterExecutionModelLimitation(
+ [](SpvExecutionModel model, std::string* message) {
+ if (model != SpvExecutionModelGLCompute) {
+ if (message) {
+ *message =
+ ": in WebGPU environment, Workgroup Memory Scope is "
+ "limited to GLCompute execution model";
+ }
+ return false;
+ }
+ return true;
+ });
+ }
}
// TODO(atgoo@github.com) Add checks for OpenCL and OpenGL environments.
diff --git a/third_party/SPIRV-Tools/test/fuzz/CMakeLists.txt b/third_party/SPIRV-Tools/test/fuzz/CMakeLists.txt
index 4211ff2..99a78fd 100644
--- a/third_party/SPIRV-Tools/test/fuzz/CMakeLists.txt
+++ b/third_party/SPIRV-Tools/test/fuzz/CMakeLists.txt
@@ -48,11 +48,13 @@
transformation_composite_construct_test.cpp
transformation_composite_extract_test.cpp
transformation_copy_object_test.cpp
+ transformation_equation_instruction_test.cpp
transformation_function_call_test.cpp
transformation_load_test.cpp
transformation_merge_blocks_test.cpp
transformation_move_block_down_test.cpp
transformation_outline_function_test.cpp
+ transformation_permute_function_parameters_test.cpp
transformation_replace_boolean_constant_with_constant_binary_test.cpp
transformation_replace_constant_with_uniform_test.cpp
transformation_replace_id_with_synonym_test.cpp
@@ -62,6 +64,8 @@
transformation_set_selection_control_test.cpp
transformation_split_block_test.cpp
transformation_store_test.cpp
+ transformation_swap_commutable_operands_test.cpp
+ transformation_toggle_access_chain_instruction_test.cpp
transformation_vector_shuffle_test.cpp
uniform_buffer_element_descriptor_test.cpp)
diff --git a/third_party/SPIRV-Tools/test/fuzz/fact_manager_test.cpp b/third_party/SPIRV-Tools/test/fuzz/fact_manager_test.cpp
index b3f32cd..2c79f12 100644
--- a/third_party/SPIRV-Tools/test/fuzz/fact_manager_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/fact_manager_test.cpp
@@ -1173,6 +1173,206 @@
context.get()));
}
+TEST(FactManagerTest, LogicalNotEquationFacts) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %12 "main"
+ OpExecutionMode %12 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeBool
+ %7 = OpConstantTrue %6
+ %12 = OpFunction %2 None %3
+ %13 = OpLabel
+ %14 = OpLogicalNot %6 %7
+ %15 = OpCopyObject %6 %7
+ %16 = OpCopyObject %6 %14
+ %17 = OpLogicalNot %6 %16
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(15, {}),
+ MakeDataDescriptor(7, {}), context.get());
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(16, {}),
+ MakeDataDescriptor(14, {}), context.get());
+ fact_manager.AddFactIdEquation(14, SpvOpLogicalNot, {7}, context.get());
+ fact_manager.AddFactIdEquation(17, SpvOpLogicalNot, {16}, context.get());
+
+ ASSERT_TRUE(fact_manager.IsSynonymous(
+ MakeDataDescriptor(15, {}), MakeDataDescriptor(7, {}), context.get()));
+ ASSERT_TRUE(fact_manager.IsSynonymous(
+ MakeDataDescriptor(17, {}), MakeDataDescriptor(7, {}), context.get()));
+ ASSERT_TRUE(fact_manager.IsSynonymous(
+ MakeDataDescriptor(15, {}), MakeDataDescriptor(17, {}), context.get()));
+ ASSERT_TRUE(fact_manager.IsSynonymous(
+ MakeDataDescriptor(16, {}), MakeDataDescriptor(14, {}), context.get()));
+}
+
+TEST(FactManagerTest, SignedNegateEquationFacts) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %12 "main"
+ OpExecutionMode %12 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpConstant %6 24
+ %12 = OpFunction %2 None %3
+ %13 = OpLabel
+ %14 = OpSNegate %6 %7
+ %15 = OpSNegate %6 %14
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ fact_manager.AddFactIdEquation(14, SpvOpSNegate, {7}, context.get());
+ fact_manager.AddFactIdEquation(15, SpvOpSNegate, {14}, context.get());
+
+ ASSERT_TRUE(fact_manager.IsSynonymous(
+ MakeDataDescriptor(7, {}), MakeDataDescriptor(15, {}), context.get()));
+}
+
+TEST(FactManagerTest, AddSubNegateFacts1) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %12 "main"
+ OpExecutionMode %12 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %15 = OpConstant %6 24
+ %16 = OpConstant %6 37
+ %12 = OpFunction %2 None %3
+ %13 = OpLabel
+ %14 = OpIAdd %6 %15 %16
+ %17 = OpCopyObject %6 %15
+ %18 = OpCopyObject %6 %16
+ %19 = OpISub %6 %14 %18 ; ==> synonymous(%19, %15)
+ %20 = OpISub %6 %14 %17 ; ==> synonymous(%20, %16)
+ %21 = OpCopyObject %6 %14
+ %22 = OpISub %6 %16 %21
+ %23 = OpCopyObject %6 %22
+ %24 = OpSNegate %6 %23 ; ==> synonymous(%24, %15)
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ fact_manager.AddFactIdEquation(14, SpvOpIAdd, {15, 16}, context.get());
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(17, {}),
+ MakeDataDescriptor(15, {}), context.get());
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(18, {}),
+ MakeDataDescriptor(16, {}), context.get());
+ fact_manager.AddFactIdEquation(19, SpvOpISub, {14, 18}, context.get());
+ fact_manager.AddFactIdEquation(20, SpvOpISub, {14, 17}, context.get());
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(21, {}),
+ MakeDataDescriptor(14, {}), context.get());
+ fact_manager.AddFactIdEquation(22, SpvOpISub, {16, 21}, context.get());
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(23, {}),
+ MakeDataDescriptor(22, {}), context.get());
+ fact_manager.AddFactIdEquation(24, SpvOpSNegate, {23}, context.get());
+
+ ASSERT_TRUE(fact_manager.IsSynonymous(
+ MakeDataDescriptor(19, {}), MakeDataDescriptor(15, {}), context.get()));
+ ASSERT_TRUE(fact_manager.IsSynonymous(
+ MakeDataDescriptor(20, {}), MakeDataDescriptor(16, {}), context.get()));
+ ASSERT_TRUE(fact_manager.IsSynonymous(
+ MakeDataDescriptor(24, {}), MakeDataDescriptor(15, {}), context.get()));
+}
+
+TEST(FactManagerTest, AddSubNegateFacts2) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %12 "main"
+ OpExecutionMode %12 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %15 = OpConstant %6 24
+ %16 = OpConstant %6 37
+ %12 = OpFunction %2 None %3
+ %13 = OpLabel
+ %14 = OpISub %6 %15 %16
+ %17 = OpIAdd %6 %14 %16 ; ==> synonymous(%17, %15)
+ %18 = OpIAdd %6 %16 %14 ; ==> synonymous(%17, %18, %15)
+ %19 = OpISub %6 %14 %15
+ %20 = OpSNegate %6 %19 ; ==> synonymous(%20, %16)
+ %21 = OpISub %6 %14 %19 ; ==> synonymous(%21, %15)
+ %22 = OpISub %6 %14 %18
+ %23 = OpSNegate %6 %22 ; ==> synonymous(%23, %16)
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ fact_manager.AddFactIdEquation(14, SpvOpISub, {15, 16}, context.get());
+ fact_manager.AddFactIdEquation(17, SpvOpIAdd, {14, 16}, context.get());
+
+ ASSERT_TRUE(fact_manager.IsSynonymous(
+ MakeDataDescriptor(17, {}), MakeDataDescriptor(15, {}), context.get()));
+
+ fact_manager.AddFactIdEquation(18, SpvOpIAdd, {16, 14}, context.get());
+
+ ASSERT_TRUE(fact_manager.IsSynonymous(
+ MakeDataDescriptor(18, {}), MakeDataDescriptor(15, {}), context.get()));
+ ASSERT_TRUE(fact_manager.IsSynonymous(
+ MakeDataDescriptor(17, {}), MakeDataDescriptor(18, {}), context.get()));
+
+ fact_manager.AddFactIdEquation(19, SpvOpISub, {14, 15}, context.get());
+ fact_manager.AddFactIdEquation(20, SpvOpSNegate, {19}, context.get());
+
+ ASSERT_TRUE(fact_manager.IsSynonymous(
+ MakeDataDescriptor(20, {}), MakeDataDescriptor(16, {}), context.get()));
+
+ fact_manager.AddFactIdEquation(21, SpvOpISub, {14, 19}, context.get());
+ ASSERT_TRUE(fact_manager.IsSynonymous(
+ MakeDataDescriptor(21, {}), MakeDataDescriptor(15, {}), context.get()));
+
+ fact_manager.AddFactIdEquation(22, SpvOpISub, {14, 18}, context.get());
+ fact_manager.AddFactIdEquation(23, SpvOpSNegate, {22}, context.get());
+ ASSERT_TRUE(fact_manager.IsSynonymous(
+ MakeDataDescriptor(23, {}), MakeDataDescriptor(16, {}), context.get()));
+}
+
} // namespace
} // namespace fuzz
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/fuzzer_replayer_test.cpp b/third_party/SPIRV-Tools/test/fuzz/fuzzer_replayer_test.cpp
index f444695..b91393e 100644
--- a/third_party/SPIRV-Tools/test/fuzz/fuzzer_replayer_test.cpp
+++ b/third_party/SPIRV-Tools/test/fuzz/fuzzer_replayer_test.cpp
@@ -1515,6 +1515,44 @@
OpFunctionEnd
)";
+// The SPIR-V comes from the following GLSL:
+//
+// #version 310 es
+// precision highp float;
+//
+// layout(location = 0) out vec4 color;
+//
+// void main()
+// {
+// color = vec4(1.0, 0.0, 0.0, 1.0);
+// }
+
+const std::string kTestShader5 = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %9
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %9 "color"
+ OpDecorate %9 Location 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeVector %6 4
+ %8 = OpTypePointer Output %7
+ %9 = OpVariable %8 Output
+ %10 = OpConstant %6 1
+ %11 = OpConstant %6 0
+ %12 = OpConstantComposite %7 %10 %11 %11 %10
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpStore %9 %12
+ OpReturn
+ OpFunctionEnd
+ )";
+
void AddConstantUniformFact(protobufs::FactSequence* facts,
uint32_t descriptor_set, uint32_t binding,
std::vector<uint32_t>&& indices, uint32_t value) {
@@ -1552,8 +1590,8 @@
ASSERT_TRUE(t.Validate(binary_in));
std::vector<fuzzerutil::ModuleSupplier> donor_suppliers;
- for (auto donor :
- {&kTestShader1, &kTestShader2, &kTestShader3, &kTestShader4}) {
+ for (auto donor : {&kTestShader1, &kTestShader2, &kTestShader3, &kTestShader4,
+ &kTestShader5}) {
donor_suppliers.emplace_back([donor]() {
return BuildModule(env, kConsoleMessageConsumer, *donor,
kFuzzAssembleOption);
@@ -1636,6 +1674,13 @@
RunFuzzerAndReplayer(kTestShader4, facts, 14, kNumFuzzerRuns);
}
+TEST(FuzzerReplayerTest, Miscellaneous5) {
+ // Do some fuzzer runs, starting from an initial seed of 29 (seed value chosen
+ // arbitrarily).
+ RunFuzzerAndReplayer(kTestShader5, protobufs::FactSequence(), 29,
+ kNumFuzzerRuns);
+}
+
} // namespace
} // namespace fuzz
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_equation_instruction_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_equation_instruction_test.cpp
new file mode 100644
index 0000000..81d849b
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_equation_instruction_test.cpp
@@ -0,0 +1,462 @@
+// 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/transformation_equation_instruction.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationEquationInstructionTest, SignedNegate) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %12 "main"
+ OpExecutionMode %12 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpConstant %6 24
+ %40 = OpTypeBool
+ %41 = OpConstantTrue %40
+ %20 = OpUndef %6
+ %12 = OpFunction %2 None %3
+ %13 = OpLabel
+ %30 = OpCopyObject %6 %7
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ protobufs::InstructionDescriptor return_instruction =
+ MakeInstructionDescriptor(13, SpvOpReturn, 0);
+
+ // Bad: id already in use.
+ ASSERT_FALSE(TransformationEquationInstruction(7, SpvOpSNegate, {7},
+ return_instruction)
+ .IsApplicable(context.get(), fact_manager));
+
+ // Bad: identified instruction does not exist.
+ ASSERT_FALSE(
+ TransformationEquationInstruction(
+ 14, SpvOpSNegate, {7}, MakeInstructionDescriptor(13, SpvOpLoad, 0))
+ .IsApplicable(context.get(), fact_manager));
+
+ // Bad: id 100 does not exist
+ ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {100},
+ return_instruction)
+ .IsApplicable(context.get(), fact_manager));
+
+ // Bad: id 20 is an OpUndef
+ ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {20},
+ return_instruction)
+ .IsApplicable(context.get(), fact_manager));
+
+ // Bad: id 30 is not available right before its definition
+ ASSERT_FALSE(TransformationEquationInstruction(
+ 14, SpvOpSNegate, {30},
+ MakeInstructionDescriptor(30, SpvOpCopyObject, 0))
+ .IsApplicable(context.get(), fact_manager));
+
+ // Bad: too many arguments to OpSNegate.
+ ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {7, 7},
+ return_instruction)
+ .IsApplicable(context.get(), fact_manager));
+
+ // Bad: 40 is a type id.
+ ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {40},
+ return_instruction)
+ .IsApplicable(context.get(), fact_manager));
+
+ // Bad: wrong type of argument to OpSNegate.
+ ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {41},
+ return_instruction)
+ .IsApplicable(context.get(), fact_manager));
+
+ auto transformation1 = TransformationEquationInstruction(
+ 14, SpvOpSNegate, {7}, return_instruction);
+ ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
+ transformation1.Apply(context.get(), &fact_manager);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ auto transformation2 = TransformationEquationInstruction(
+ 15, SpvOpSNegate, {14}, return_instruction);
+ ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
+ transformation2.Apply(context.get(), &fact_manager);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ ASSERT_TRUE(fact_manager.IsSynonymous(
+ MakeDataDescriptor(15, {}), MakeDataDescriptor(7, {}), context.get()));
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %12 "main"
+ OpExecutionMode %12 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpConstant %6 24
+ %40 = OpTypeBool
+ %41 = OpConstantTrue %40
+ %20 = OpUndef %6
+ %12 = OpFunction %2 None %3
+ %13 = OpLabel
+ %30 = OpCopyObject %6 %7
+ %14 = OpSNegate %6 %7
+ %15 = OpSNegate %6 %14
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationEquationInstructionTest, LogicalNot) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %12 "main"
+ OpExecutionMode %12 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeBool
+ %7 = OpConstantTrue %6
+ %20 = OpTypeInt 32 0
+ %21 = OpConstant %20 5
+ %12 = OpFunction %2 None %3
+ %13 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ protobufs::InstructionDescriptor return_instruction =
+ MakeInstructionDescriptor(13, SpvOpReturn, 0);
+
+ // Bad: too few arguments to OpLogicalNot.
+ ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpLogicalNot, {},
+ return_instruction)
+ .IsApplicable(context.get(), fact_manager));
+
+ // Bad: 6 is a type id.
+ ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpLogicalNot, {6},
+ return_instruction)
+ .IsApplicable(context.get(), fact_manager));
+
+ // Bad: wrong type of argument to OpLogicalNot.
+ ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpLogicalNot, {21},
+ return_instruction)
+ .IsApplicable(context.get(), fact_manager));
+
+ auto transformation1 = TransformationEquationInstruction(
+ 14, SpvOpLogicalNot, {7}, return_instruction);
+ ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
+ transformation1.Apply(context.get(), &fact_manager);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ auto transformation2 = TransformationEquationInstruction(
+ 15, SpvOpLogicalNot, {14}, return_instruction);
+ ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
+ transformation2.Apply(context.get(), &fact_manager);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ ASSERT_TRUE(fact_manager.IsSynonymous(
+ MakeDataDescriptor(15, {}), MakeDataDescriptor(7, {}), context.get()));
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %12 "main"
+ OpExecutionMode %12 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeBool
+ %7 = OpConstantTrue %6
+ %20 = OpTypeInt 32 0
+ %21 = OpConstant %20 5
+ %12 = OpFunction %2 None %3
+ %13 = OpLabel
+ %14 = OpLogicalNot %6 %7
+ %15 = OpLogicalNot %6 %14
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationEquationInstructionTest, AddSubNegate1) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %12 "main"
+ OpExecutionMode %12 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %30 = OpTypeVector %6 3
+ %15 = OpConstant %6 24
+ %16 = OpConstant %6 37
+ %31 = OpConstantComposite %30 %15 %16 %15
+ %33 = OpTypeBool
+ %32 = OpConstantTrue %33
+ %12 = OpFunction %2 None %3
+ %13 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ protobufs::InstructionDescriptor return_instruction =
+ MakeInstructionDescriptor(13, SpvOpReturn, 0);
+
+ // Bad: too many arguments to OpIAdd.
+ ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {15, 16, 16},
+ return_instruction)
+ .IsApplicable(context.get(), fact_manager));
+ // Bad: boolean argument to OpIAdd.
+ ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {15, 32},
+ return_instruction)
+ .IsApplicable(context.get(), fact_manager));
+ // Bad: type as argument to OpIAdd.
+ ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {33, 16},
+ return_instruction)
+ .IsApplicable(context.get(), fact_manager));
+ // Bad: arguments of mismatched widths
+ ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {15, 31},
+ return_instruction)
+ .IsApplicable(context.get(), fact_manager));
+ // Bad: arguments of mismatched widths
+ ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {31, 15},
+ return_instruction)
+ .IsApplicable(context.get(), fact_manager));
+
+ auto transformation1 = TransformationEquationInstruction(
+ 14, SpvOpIAdd, {15, 16}, return_instruction);
+ ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
+ transformation1.Apply(context.get(), &fact_manager);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ auto transformation2 = TransformationEquationInstruction(
+ 19, SpvOpISub, {14, 16}, return_instruction);
+ ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
+ transformation2.Apply(context.get(), &fact_manager);
+ ASSERT_TRUE(IsValid(env, context.get()));
+ ASSERT_TRUE(fact_manager.IsSynonymous(
+ MakeDataDescriptor(15, {}), MakeDataDescriptor(19, {}), context.get()));
+
+ auto transformation3 = TransformationEquationInstruction(
+ 20, SpvOpISub, {14, 15}, return_instruction);
+ ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
+ transformation3.Apply(context.get(), &fact_manager);
+ ASSERT_TRUE(IsValid(env, context.get()));
+ ASSERT_TRUE(fact_manager.IsSynonymous(
+ MakeDataDescriptor(20, {}), MakeDataDescriptor(16, {}), context.get()));
+
+ auto transformation4 = TransformationEquationInstruction(
+ 22, SpvOpISub, {16, 14}, return_instruction);
+ ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager));
+ transformation4.Apply(context.get(), &fact_manager);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ auto transformation5 = TransformationEquationInstruction(
+ 24, SpvOpSNegate, {22}, return_instruction);
+ ASSERT_TRUE(transformation5.IsApplicable(context.get(), fact_manager));
+ transformation5.Apply(context.get(), &fact_manager);
+ ASSERT_TRUE(IsValid(env, context.get()));
+ ASSERT_TRUE(fact_manager.IsSynonymous(
+ MakeDataDescriptor(24, {}), MakeDataDescriptor(15, {}), context.get()));
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %12 "main"
+ OpExecutionMode %12 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %30 = OpTypeVector %6 3
+ %15 = OpConstant %6 24
+ %16 = OpConstant %6 37
+ %31 = OpConstantComposite %30 %15 %16 %15
+ %33 = OpTypeBool
+ %32 = OpConstantTrue %33
+ %12 = OpFunction %2 None %3
+ %13 = OpLabel
+ %14 = OpIAdd %6 %15 %16
+ %19 = OpISub %6 %14 %16 ; ==> synonymous(%19, %15)
+ %20 = OpISub %6 %14 %15 ; ==> synonymous(%20, %16)
+ %22 = OpISub %6 %16 %14
+ %24 = OpSNegate %6 %22 ; ==> synonymous(%24, %15)
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationEquationInstructionTest, AddSubNegate2) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %12 "main"
+ OpExecutionMode %12 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %15 = OpConstant %6 24
+ %16 = OpConstant %6 37
+ %12 = OpFunction %2 None %3
+ %13 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ protobufs::InstructionDescriptor return_instruction =
+ MakeInstructionDescriptor(13, SpvOpReturn, 0);
+
+ auto transformation1 = TransformationEquationInstruction(
+ 14, SpvOpISub, {15, 16}, return_instruction);
+ ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
+ transformation1.Apply(context.get(), &fact_manager);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ auto transformation2 = TransformationEquationInstruction(
+ 17, SpvOpIAdd, {14, 16}, return_instruction);
+ ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
+ transformation2.Apply(context.get(), &fact_manager);
+ ASSERT_TRUE(IsValid(env, context.get()));
+ ASSERT_TRUE(fact_manager.IsSynonymous(
+ MakeDataDescriptor(17, {}), MakeDataDescriptor(15, {}), context.get()));
+
+ auto transformation3 = TransformationEquationInstruction(
+ 18, SpvOpIAdd, {16, 14}, return_instruction);
+ ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
+ transformation3.Apply(context.get(), &fact_manager);
+ ASSERT_TRUE(IsValid(env, context.get()));
+ ASSERT_TRUE(fact_manager.IsSynonymous(
+ MakeDataDescriptor(17, {}), MakeDataDescriptor(18, {}), context.get()));
+ ASSERT_TRUE(fact_manager.IsSynonymous(
+ MakeDataDescriptor(18, {}), MakeDataDescriptor(15, {}), context.get()));
+
+ auto transformation4 = TransformationEquationInstruction(
+ 19, SpvOpISub, {14, 15}, return_instruction);
+ ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager));
+ transformation4.Apply(context.get(), &fact_manager);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ auto transformation5 = TransformationEquationInstruction(
+ 20, SpvOpSNegate, {19}, return_instruction);
+ ASSERT_TRUE(transformation5.IsApplicable(context.get(), fact_manager));
+ transformation5.Apply(context.get(), &fact_manager);
+ ASSERT_TRUE(IsValid(env, context.get()));
+ ASSERT_TRUE(fact_manager.IsSynonymous(
+ MakeDataDescriptor(20, {}), MakeDataDescriptor(16, {}), context.get()));
+
+ auto transformation6 = TransformationEquationInstruction(
+ 21, SpvOpISub, {14, 19}, return_instruction);
+ ASSERT_TRUE(transformation6.IsApplicable(context.get(), fact_manager));
+ transformation6.Apply(context.get(), &fact_manager);
+ ASSERT_TRUE(IsValid(env, context.get()));
+ ASSERT_TRUE(fact_manager.IsSynonymous(
+ MakeDataDescriptor(21, {}), MakeDataDescriptor(15, {}), context.get()));
+
+ auto transformation7 = TransformationEquationInstruction(
+ 22, SpvOpISub, {14, 18}, return_instruction);
+ ASSERT_TRUE(transformation7.IsApplicable(context.get(), fact_manager));
+ transformation7.Apply(context.get(), &fact_manager);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ auto transformation8 = TransformationEquationInstruction(
+ 23, SpvOpSNegate, {22}, return_instruction);
+ ASSERT_TRUE(transformation8.IsApplicable(context.get(), fact_manager));
+ transformation8.Apply(context.get(), &fact_manager);
+ ASSERT_TRUE(IsValid(env, context.get()));
+ ASSERT_TRUE(fact_manager.IsSynonymous(
+ MakeDataDescriptor(23, {}), MakeDataDescriptor(16, {}), context.get()));
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %12 "main"
+ OpExecutionMode %12 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %15 = OpConstant %6 24
+ %16 = OpConstant %6 37
+ %12 = OpFunction %2 None %3
+ %13 = OpLabel
+ %14 = OpISub %6 %15 %16
+ %17 = OpIAdd %6 %14 %16 ; ==> synonymous(%17, %15)
+ %18 = OpIAdd %6 %16 %14 ; ==> synonymous(%17, %18, %15)
+ %19 = OpISub %6 %14 %15
+ %20 = OpSNegate %6 %19 ; ==> synonymous(%20, %16)
+ %21 = OpISub %6 %14 %19 ; ==> synonymous(%21, %15)
+ %22 = OpISub %6 %14 %18
+ %23 = OpSNegate %6 %22 ; ==> synonymous(%23, %16)
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_permute_function_parameters_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_permute_function_parameters_test.cpp
new file mode 100644
index 0000000..1af4699
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_permute_function_parameters_test.cpp
@@ -0,0 +1,430 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_permute_function_parameters.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationPermuteFunctionParametersTest, BasicTest) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %72 %74
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %12 "g(f1;f1;"
+ OpName %10 "x"
+ OpName %11 "y"
+ OpName %22 "f(f1;i1;vf2;"
+ OpName %19 "x"
+ OpName %20 "y"
+ OpName %21 "z"
+ OpName %28 "cond(i1;f1;"
+ OpName %26 "a"
+ OpName %27 "b"
+ OpName %53 "param"
+ OpName %54 "param"
+ OpName %66 "param"
+ OpName %67 "param"
+ OpName %72 "color"
+ OpName %74 "gl_FragCoord"
+ OpName %75 "param"
+ OpName %79 "param"
+ OpName %85 "param"
+ OpName %86 "param"
+ OpName %91 "param"
+ OpName %92 "param"
+ OpName %93 "param"
+ OpName %99 "param"
+ OpName %100 "param"
+ OpName %101 "param"
+ OpDecorate %20 RelaxedPrecision
+ OpDecorate %26 RelaxedPrecision
+ OpDecorate %47 RelaxedPrecision
+ OpDecorate %58 RelaxedPrecision
+ OpDecorate %72 Location 0
+ OpDecorate %74 BuiltIn FragCoord
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypePointer Function %6
+ %8 = OpTypeVector %6 4
+ %9 = OpTypeFunction %8 %7 %7
+ %14 = OpTypeInt 32 1
+ %15 = OpTypePointer Function %14
+ %16 = OpTypeVector %6 2
+ %17 = OpTypePointer Function %16
+ %18 = OpTypeFunction %8 %7 %15 %17
+ %24 = OpTypeBool
+ %25 = OpTypeFunction %24 %15 %7
+ %105 = OpTypeFunction %24 %7 %15 ; predefined type for %28
+ %31 = OpConstant %6 255
+ %33 = OpConstant %6 0
+ %34 = OpConstant %6 1
+ %42 = OpTypeInt 32 0
+ %43 = OpConstant %42 0
+ %49 = OpConstant %42 1
+ %64 = OpConstant %14 4
+ %65 = OpConstant %6 5
+ %71 = OpTypePointer Output %8
+ %72 = OpVariable %71 Output
+ %73 = OpTypePointer Input %8
+ %74 = OpVariable %73 Input
+ %76 = OpTypePointer Input %6
+ %84 = OpConstant %14 5
+ %90 = OpConstant %6 3
+ %98 = OpConstant %6 4
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %66 = OpVariable %15 Function
+ %67 = OpVariable %7 Function
+ %75 = OpVariable %7 Function
+ %79 = OpVariable %7 Function
+ %85 = OpVariable %15 Function
+ %86 = OpVariable %7 Function
+ %91 = OpVariable %7 Function
+ %92 = OpVariable %15 Function
+ %93 = OpVariable %17 Function
+ %99 = OpVariable %7 Function
+ %100 = OpVariable %15 Function
+ %101 = OpVariable %17 Function
+ OpStore %66 %64
+ OpStore %67 %65
+ %68 = OpFunctionCall %24 %28 %66 %67
+ OpSelectionMerge %70 None
+ OpBranchConditional %68 %69 %83
+ %69 = OpLabel
+ %77 = OpAccessChain %76 %74 %43
+ %78 = OpLoad %6 %77
+ OpStore %75 %78
+ %80 = OpAccessChain %76 %74 %49
+ %81 = OpLoad %6 %80
+ OpStore %79 %81
+ %82 = OpFunctionCall %8 %12 %75 %79
+ OpStore %72 %82
+ OpBranch %70
+ %83 = OpLabel
+ OpStore %85 %84
+ OpStore %86 %65
+ %87 = OpFunctionCall %24 %28 %85 %86
+ OpSelectionMerge %89 None
+ OpBranchConditional %87 %88 %97
+ %88 = OpLabel
+ OpStore %91 %90
+ OpStore %92 %64
+ %94 = OpLoad %8 %74
+ %95 = OpVectorShuffle %16 %94 %94 0 1
+ OpStore %93 %95
+ %96 = OpFunctionCall %8 %22 %91 %92 %93
+ OpStore %72 %96
+ OpBranch %89
+ %97 = OpLabel
+ OpStore %99 %98
+ OpStore %100 %84
+ %102 = OpLoad %8 %74
+ %103 = OpVectorShuffle %16 %102 %102 0 1
+ OpStore %101 %103
+ %104 = OpFunctionCall %8 %22 %99 %100 %101
+ OpStore %72 %104
+ OpBranch %89
+ %89 = OpLabel
+ OpBranch %70
+ %70 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %12 = OpFunction %8 None %9
+ %10 = OpFunctionParameter %7
+ %11 = OpFunctionParameter %7
+ %13 = OpLabel
+ %30 = OpLoad %6 %10
+ %32 = OpFDiv %6 %30 %31
+ %35 = OpLoad %6 %11
+ %36 = OpFDiv %6 %35 %31
+ %37 = OpFSub %6 %34 %36
+ %38 = OpCompositeConstruct %8 %32 %33 %37 %34
+ OpReturnValue %38
+ OpFunctionEnd
+ %22 = OpFunction %8 None %18
+ %19 = OpFunctionParameter %7
+ %20 = OpFunctionParameter %15
+ %21 = OpFunctionParameter %17
+ %23 = OpLabel
+ %53 = OpVariable %7 Function
+ %54 = OpVariable %7 Function
+ %41 = OpLoad %6 %19
+ %44 = OpAccessChain %7 %21 %43
+ %45 = OpLoad %6 %44
+ %46 = OpFAdd %6 %41 %45
+ %47 = OpLoad %14 %20
+ %48 = OpConvertSToF %6 %47
+ %50 = OpAccessChain %7 %21 %49
+ %51 = OpLoad %6 %50
+ %52 = OpFAdd %6 %48 %51
+ OpStore %53 %46
+ OpStore %54 %52
+ %55 = OpFunctionCall %8 %12 %53 %54
+ OpReturnValue %55
+ OpFunctionEnd
+ %28 = OpFunction %24 None %25
+ %26 = OpFunctionParameter %15
+ %27 = OpFunctionParameter %7
+ %29 = OpLabel
+ %58 = OpLoad %14 %26
+ %59 = OpConvertSToF %6 %58
+ %60 = OpLoad %6 %27
+ %61 = OpFOrdLessThan %24 %59 %60
+ OpReturnValue %61
+ OpFunctionEnd
+
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+
+ // Can't permute main function
+ ASSERT_FALSE(TransformationPermuteFunctionParameters(4, 0, {}).IsApplicable(
+ context.get(), fact_manager));
+
+ // Can't permute invalid instruction
+ ASSERT_FALSE(TransformationPermuteFunctionParameters(101, 0, {})
+ .IsApplicable(context.get(), fact_manager));
+
+ // Permutation has too many values
+ ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 0, {2, 1, 0, 3})
+ .IsApplicable(context.get(), fact_manager));
+
+ // Permutation has too few values
+ ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 0, {0, 1})
+ .IsApplicable(context.get(), fact_manager));
+
+ // Permutation has invalid values
+ ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 0, {3, 1, 0})
+ .IsApplicable(context.get(), fact_manager));
+
+ // Type id is not an OpTypeFunction instruction
+ ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 42, {2, 1, 0})
+ .IsApplicable(context.get(), fact_manager));
+
+ // Type id has incorrect number of operands
+ ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 9, {2, 1, 0})
+ .IsApplicable(context.get(), fact_manager));
+
+ // OpTypeFunction has operands out of order
+ ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 18, {2, 1, 0})
+ .IsApplicable(context.get(), fact_manager));
+
+ // Successful transformations
+ {
+ // Function has two operands of the same type:
+ // initial OpTypeFunction should be enough
+ TransformationPermuteFunctionParameters transformation(12, 9, {1, 0});
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+ transformation.Apply(context.get(), &fact_manager);
+ ASSERT_TRUE(IsValid(env, context.get()));
+ }
+ {
+ TransformationPermuteFunctionParameters transformation(28, 105, {1, 0});
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
+ transformation.Apply(context.get(), &fact_manager);
+ ASSERT_TRUE(IsValid(env, context.get()));
+ }
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %72 %74
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %12 "g(f1;f1;"
+ OpName %10 "x"
+ OpName %11 "y"
+ OpName %22 "f(f1;i1;vf2;"
+ OpName %19 "x"
+ OpName %20 "y"
+ OpName %21 "z"
+ OpName %28 "cond(i1;f1;"
+ OpName %26 "a"
+ OpName %27 "b"
+ OpName %53 "param"
+ OpName %54 "param"
+ OpName %66 "param"
+ OpName %67 "param"
+ OpName %72 "color"
+ OpName %74 "gl_FragCoord"
+ OpName %75 "param"
+ OpName %79 "param"
+ OpName %85 "param"
+ OpName %86 "param"
+ OpName %91 "param"
+ OpName %92 "param"
+ OpName %93 "param"
+ OpName %99 "param"
+ OpName %100 "param"
+ OpName %101 "param"
+ OpDecorate %20 RelaxedPrecision
+ OpDecorate %26 RelaxedPrecision
+ OpDecorate %47 RelaxedPrecision
+ OpDecorate %58 RelaxedPrecision
+ OpDecorate %72 Location 0
+ OpDecorate %74 BuiltIn FragCoord
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypePointer Function %6
+ %8 = OpTypeVector %6 4
+ %9 = OpTypeFunction %8 %7 %7
+ %14 = OpTypeInt 32 1
+ %15 = OpTypePointer Function %14
+ %16 = OpTypeVector %6 2
+ %17 = OpTypePointer Function %16
+ %18 = OpTypeFunction %8 %7 %15 %17
+ %24 = OpTypeBool
+ %25 = OpTypeFunction %24 %15 %7
+ %105 = OpTypeFunction %24 %7 %15 ; predefined type for %28
+ %31 = OpConstant %6 255
+ %33 = OpConstant %6 0
+ %34 = OpConstant %6 1
+ %42 = OpTypeInt 32 0
+ %43 = OpConstant %42 0
+ %49 = OpConstant %42 1
+ %64 = OpConstant %14 4
+ %65 = OpConstant %6 5
+ %71 = OpTypePointer Output %8
+ %72 = OpVariable %71 Output
+ %73 = OpTypePointer Input %8
+ %74 = OpVariable %73 Input
+ %76 = OpTypePointer Input %6
+ %84 = OpConstant %14 5
+ %90 = OpConstant %6 3
+ %98 = OpConstant %6 4
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %66 = OpVariable %15 Function
+ %67 = OpVariable %7 Function
+ %75 = OpVariable %7 Function
+ %79 = OpVariable %7 Function
+ %85 = OpVariable %15 Function
+ %86 = OpVariable %7 Function
+ %91 = OpVariable %7 Function
+ %92 = OpVariable %15 Function
+ %93 = OpVariable %17 Function
+ %99 = OpVariable %7 Function
+ %100 = OpVariable %15 Function
+ %101 = OpVariable %17 Function
+ OpStore %66 %64
+ OpStore %67 %65
+ %68 = OpFunctionCall %24 %28 %67 %66
+ OpSelectionMerge %70 None
+ OpBranchConditional %68 %69 %83
+ %69 = OpLabel
+ %77 = OpAccessChain %76 %74 %43
+ %78 = OpLoad %6 %77
+ OpStore %75 %78
+ %80 = OpAccessChain %76 %74 %49
+ %81 = OpLoad %6 %80
+ OpStore %79 %81
+ %82 = OpFunctionCall %8 %12 %79 %75
+ OpStore %72 %82
+ OpBranch %70
+ %83 = OpLabel
+ OpStore %85 %84
+ OpStore %86 %65
+ %87 = OpFunctionCall %24 %28 %86 %85
+ OpSelectionMerge %89 None
+ OpBranchConditional %87 %88 %97
+ %88 = OpLabel
+ OpStore %91 %90
+ OpStore %92 %64
+ %94 = OpLoad %8 %74
+ %95 = OpVectorShuffle %16 %94 %94 0 1
+ OpStore %93 %95
+ %96 = OpFunctionCall %8 %22 %91 %92 %93
+ OpStore %72 %96
+ OpBranch %89
+ %97 = OpLabel
+ OpStore %99 %98
+ OpStore %100 %84
+ %102 = OpLoad %8 %74
+ %103 = OpVectorShuffle %16 %102 %102 0 1
+ OpStore %101 %103
+ %104 = OpFunctionCall %8 %22 %99 %100 %101
+ OpStore %72 %104
+ OpBranch %89
+ %89 = OpLabel
+ OpBranch %70
+ %70 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %12 = OpFunction %8 None %9
+ %11 = OpFunctionParameter %7
+ %10 = OpFunctionParameter %7
+ %13 = OpLabel
+ %30 = OpLoad %6 %10
+ %32 = OpFDiv %6 %30 %31
+ %35 = OpLoad %6 %11
+ %36 = OpFDiv %6 %35 %31
+ %37 = OpFSub %6 %34 %36
+ %38 = OpCompositeConstruct %8 %32 %33 %37 %34
+ OpReturnValue %38
+ OpFunctionEnd
+ %22 = OpFunction %8 None %18
+ %19 = OpFunctionParameter %7
+ %20 = OpFunctionParameter %15
+ %21 = OpFunctionParameter %17
+ %23 = OpLabel
+ %53 = OpVariable %7 Function
+ %54 = OpVariable %7 Function
+ %41 = OpLoad %6 %19
+ %44 = OpAccessChain %7 %21 %43
+ %45 = OpLoad %6 %44
+ %46 = OpFAdd %6 %41 %45
+ %47 = OpLoad %14 %20
+ %48 = OpConvertSToF %6 %47
+ %50 = OpAccessChain %7 %21 %49
+ %51 = OpLoad %6 %50
+ %52 = OpFAdd %6 %48 %51
+ OpStore %53 %46
+ OpStore %54 %52
+ %55 = OpFunctionCall %8 %12 %54 %53
+ OpReturnValue %55
+ OpFunctionEnd
+ %28 = OpFunction %24 None %105
+ %27 = OpFunctionParameter %7
+ %26 = OpFunctionParameter %15
+ %29 = OpLabel
+ %58 = OpLoad %14 %26
+ %59 = OpConvertSToF %6 %58
+ %60 = OpLoad %6 %27
+ %61 = OpFOrdLessThan %24 %59 %60
+ OpReturnValue %61
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_swap_commutable_operands_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_swap_commutable_operands_test.cpp
new file mode 100644
index 0000000..f0591cf
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_swap_commutable_operands_test.cpp
@@ -0,0 +1,427 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_swap_commutable_operands.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationSwapCommutableOperandsTest, IsApplicableTest) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypeInt 32 0
+ %8 = OpConstant %7 2
+ %9 = OpTypeArray %6 %8
+ %10 = OpTypePointer Function %9
+ %12 = OpConstant %6 1
+ %13 = OpConstant %6 2
+ %14 = OpConstantComposite %9 %12 %13
+ %15 = OpTypePointer Function %6
+ %17 = OpConstant %6 0
+ %29 = OpTypeFloat 32
+ %30 = OpTypeArray %29 %8
+ %31 = OpTypePointer Function %30
+ %33 = OpConstant %29 1
+ %34 = OpConstant %29 2
+ %35 = OpConstantComposite %30 %33 %34
+ %36 = OpTypePointer Function %29
+ %49 = OpTypeVector %29 3
+ %50 = OpTypeArray %49 %8
+ %51 = OpTypePointer Function %50
+ %53 = OpConstant %29 3
+ %54 = OpConstantComposite %49 %33 %34 %53
+ %55 = OpConstant %29 4
+ %56 = OpConstant %29 5
+ %57 = OpConstant %29 6
+ %58 = OpConstantComposite %49 %55 %56 %57
+ %59 = OpConstantComposite %50 %54 %58
+ %61 = OpTypePointer Function %49
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %11 = OpVariable %10 Function
+ %16 = OpVariable %15 Function
+ %23 = OpVariable %15 Function
+ %32 = OpVariable %31 Function
+ %37 = OpVariable %36 Function
+ %43 = OpVariable %36 Function
+ %52 = OpVariable %51 Function
+ %60 = OpVariable %36 Function
+ OpStore %11 %14
+ %18 = OpAccessChain %15 %11 %17
+ %19 = OpLoad %6 %18
+ %20 = OpAccessChain %15 %11 %12
+ %21 = OpLoad %6 %20
+ %22 = OpIAdd %6 %19 %21
+ OpStore %16 %22
+ %24 = OpAccessChain %15 %11 %17
+ %25 = OpLoad %6 %24
+ %26 = OpAccessChain %15 %11 %12
+ %27 = OpLoad %6 %26
+ %28 = OpIMul %6 %25 %27
+ OpStore %23 %28
+ OpStore %32 %35
+ %38 = OpAccessChain %36 %32 %17
+ %39 = OpLoad %29 %38
+ %40 = OpAccessChain %36 %32 %12
+ %41 = OpLoad %29 %40
+ %42 = OpFAdd %29 %39 %41
+ OpStore %37 %42
+ %44 = OpAccessChain %36 %32 %17
+ %45 = OpLoad %29 %44
+ %46 = OpAccessChain %36 %32 %12
+ %47 = OpLoad %29 %46
+ %48 = OpFMul %29 %45 %47
+ OpStore %43 %48
+ OpStore %52 %59
+ %62 = OpAccessChain %61 %52 %17
+ %63 = OpLoad %49 %62
+ %64 = OpAccessChain %61 %52 %12
+ %65 = OpLoad %49 %64
+ %66 = OpDot %29 %63 %65
+ OpStore %60 %66
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_5;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager factManager;
+
+ // Tests existing commutative instructions
+ auto instructionDescriptor = MakeInstructionDescriptor(22, SpvOpIAdd, 0);
+ auto transformation =
+ TransformationSwapCommutableOperands(instructionDescriptor);
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager));
+
+ instructionDescriptor = MakeInstructionDescriptor(28, SpvOpIMul, 0);
+ transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager));
+
+ instructionDescriptor = MakeInstructionDescriptor(42, SpvOpFAdd, 0);
+ transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager));
+
+ instructionDescriptor = MakeInstructionDescriptor(48, SpvOpFMul, 0);
+ transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager));
+
+ instructionDescriptor = MakeInstructionDescriptor(66, SpvOpDot, 0);
+ transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager));
+
+ // Tests existing non-commutative instructions
+ instructionDescriptor = MakeInstructionDescriptor(1, SpvOpExtInstImport, 0);
+ transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+ instructionDescriptor = MakeInstructionDescriptor(5, SpvOpLabel, 0);
+ transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+ instructionDescriptor = MakeInstructionDescriptor(8, SpvOpConstant, 0);
+ transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+ instructionDescriptor = MakeInstructionDescriptor(11, SpvOpVariable, 0);
+ transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+ instructionDescriptor =
+ MakeInstructionDescriptor(14, SpvOpConstantComposite, 0);
+ transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+ // Tests the base instruction id not existing
+ instructionDescriptor = MakeInstructionDescriptor(67, SpvOpIAddCarry, 0);
+ transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+ instructionDescriptor = MakeInstructionDescriptor(68, SpvOpIEqual, 0);
+ transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+ instructionDescriptor = MakeInstructionDescriptor(69, SpvOpINotEqual, 0);
+ transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+ instructionDescriptor = MakeInstructionDescriptor(70, SpvOpFOrdEqual, 0);
+ transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+ instructionDescriptor = MakeInstructionDescriptor(71, SpvOpPtrEqual, 0);
+ transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+ // Tests there being no instruction with the desired opcode after the base
+ // instruction id
+ instructionDescriptor = MakeInstructionDescriptor(24, SpvOpIAdd, 0);
+ transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+ instructionDescriptor = MakeInstructionDescriptor(38, SpvOpIMul, 0);
+ transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+ instructionDescriptor = MakeInstructionDescriptor(45, SpvOpFAdd, 0);
+ transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+ instructionDescriptor = MakeInstructionDescriptor(66, SpvOpFMul, 0);
+ transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+ // Tests there being an instruction with the desired opcode after the base
+ // instruction id, but the skip count associated with the instruction
+ // descriptor being so high.
+ instructionDescriptor = MakeInstructionDescriptor(11, SpvOpIAdd, 100);
+ transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+ instructionDescriptor = MakeInstructionDescriptor(16, SpvOpIMul, 100);
+ transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+ instructionDescriptor = MakeInstructionDescriptor(23, SpvOpFAdd, 100);
+ transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+ instructionDescriptor = MakeInstructionDescriptor(32, SpvOpFMul, 100);
+ transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+ instructionDescriptor = MakeInstructionDescriptor(37, SpvOpDot, 100);
+ transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+}
+
+TEST(TransformationSwapCommutableOperandsTest, ApplyTest) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypeInt 32 0
+ %8 = OpConstant %7 2
+ %9 = OpTypeArray %6 %8
+ %10 = OpTypePointer Function %9
+ %12 = OpConstant %6 1
+ %13 = OpConstant %6 2
+ %14 = OpConstantComposite %9 %12 %13
+ %15 = OpTypePointer Function %6
+ %17 = OpConstant %6 0
+ %29 = OpTypeFloat 32
+ %30 = OpTypeArray %29 %8
+ %31 = OpTypePointer Function %30
+ %33 = OpConstant %29 1
+ %34 = OpConstant %29 2
+ %35 = OpConstantComposite %30 %33 %34
+ %36 = OpTypePointer Function %29
+ %49 = OpTypeVector %29 3
+ %50 = OpTypeArray %49 %8
+ %51 = OpTypePointer Function %50
+ %53 = OpConstant %29 3
+ %54 = OpConstantComposite %49 %33 %34 %53
+ %55 = OpConstant %29 4
+ %56 = OpConstant %29 5
+ %57 = OpConstant %29 6
+ %58 = OpConstantComposite %49 %55 %56 %57
+ %59 = OpConstantComposite %50 %54 %58
+ %61 = OpTypePointer Function %49
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %11 = OpVariable %10 Function
+ %16 = OpVariable %15 Function
+ %23 = OpVariable %15 Function
+ %32 = OpVariable %31 Function
+ %37 = OpVariable %36 Function
+ %43 = OpVariable %36 Function
+ %52 = OpVariable %51 Function
+ %60 = OpVariable %36 Function
+ OpStore %11 %14
+ %18 = OpAccessChain %15 %11 %17
+ %19 = OpLoad %6 %18
+ %20 = OpAccessChain %15 %11 %12
+ %21 = OpLoad %6 %20
+ %22 = OpIAdd %6 %19 %21
+ OpStore %16 %22
+ %24 = OpAccessChain %15 %11 %17
+ %25 = OpLoad %6 %24
+ %26 = OpAccessChain %15 %11 %12
+ %27 = OpLoad %6 %26
+ %28 = OpIMul %6 %25 %27
+ OpStore %23 %28
+ OpStore %32 %35
+ %38 = OpAccessChain %36 %32 %17
+ %39 = OpLoad %29 %38
+ %40 = OpAccessChain %36 %32 %12
+ %41 = OpLoad %29 %40
+ %42 = OpFAdd %29 %39 %41
+ OpStore %37 %42
+ %44 = OpAccessChain %36 %32 %17
+ %45 = OpLoad %29 %44
+ %46 = OpAccessChain %36 %32 %12
+ %47 = OpLoad %29 %46
+ %48 = OpFMul %29 %45 %47
+ OpStore %43 %48
+ OpStore %52 %59
+ %62 = OpAccessChain %61 %52 %17
+ %63 = OpLoad %49 %62
+ %64 = OpAccessChain %61 %52 %12
+ %65 = OpLoad %49 %64
+ %66 = OpDot %29 %63 %65
+ OpStore %60 %66
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_5;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager factManager;
+
+ auto instructionDescriptor = MakeInstructionDescriptor(22, SpvOpIAdd, 0);
+ auto transformation =
+ TransformationSwapCommutableOperands(instructionDescriptor);
+ transformation.Apply(context.get(), &factManager);
+
+ instructionDescriptor = MakeInstructionDescriptor(28, SpvOpIMul, 0);
+ transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+ transformation.Apply(context.get(), &factManager);
+
+ instructionDescriptor = MakeInstructionDescriptor(42, SpvOpFAdd, 0);
+ transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+ transformation.Apply(context.get(), &factManager);
+
+ instructionDescriptor = MakeInstructionDescriptor(48, SpvOpFMul, 0);
+ transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+ transformation.Apply(context.get(), &factManager);
+
+ instructionDescriptor = MakeInstructionDescriptor(66, SpvOpDot, 0);
+ transformation = TransformationSwapCommutableOperands(instructionDescriptor);
+ transformation.Apply(context.get(), &factManager);
+
+ std::string variantShader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypeInt 32 0
+ %8 = OpConstant %7 2
+ %9 = OpTypeArray %6 %8
+ %10 = OpTypePointer Function %9
+ %12 = OpConstant %6 1
+ %13 = OpConstant %6 2
+ %14 = OpConstantComposite %9 %12 %13
+ %15 = OpTypePointer Function %6
+ %17 = OpConstant %6 0
+ %29 = OpTypeFloat 32
+ %30 = OpTypeArray %29 %8
+ %31 = OpTypePointer Function %30
+ %33 = OpConstant %29 1
+ %34 = OpConstant %29 2
+ %35 = OpConstantComposite %30 %33 %34
+ %36 = OpTypePointer Function %29
+ %49 = OpTypeVector %29 3
+ %50 = OpTypeArray %49 %8
+ %51 = OpTypePointer Function %50
+ %53 = OpConstant %29 3
+ %54 = OpConstantComposite %49 %33 %34 %53
+ %55 = OpConstant %29 4
+ %56 = OpConstant %29 5
+ %57 = OpConstant %29 6
+ %58 = OpConstantComposite %49 %55 %56 %57
+ %59 = OpConstantComposite %50 %54 %58
+ %61 = OpTypePointer Function %49
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %11 = OpVariable %10 Function
+ %16 = OpVariable %15 Function
+ %23 = OpVariable %15 Function
+ %32 = OpVariable %31 Function
+ %37 = OpVariable %36 Function
+ %43 = OpVariable %36 Function
+ %52 = OpVariable %51 Function
+ %60 = OpVariable %36 Function
+ OpStore %11 %14
+ %18 = OpAccessChain %15 %11 %17
+ %19 = OpLoad %6 %18
+ %20 = OpAccessChain %15 %11 %12
+ %21 = OpLoad %6 %20
+ %22 = OpIAdd %6 %21 %19
+ OpStore %16 %22
+ %24 = OpAccessChain %15 %11 %17
+ %25 = OpLoad %6 %24
+ %26 = OpAccessChain %15 %11 %12
+ %27 = OpLoad %6 %26
+ %28 = OpIMul %6 %27 %25
+ OpStore %23 %28
+ OpStore %32 %35
+ %38 = OpAccessChain %36 %32 %17
+ %39 = OpLoad %29 %38
+ %40 = OpAccessChain %36 %32 %12
+ %41 = OpLoad %29 %40
+ %42 = OpFAdd %29 %41 %39
+ OpStore %37 %42
+ %44 = OpAccessChain %36 %32 %17
+ %45 = OpLoad %29 %44
+ %46 = OpAccessChain %36 %32 %12
+ %47 = OpLoad %29 %46
+ %48 = OpFMul %29 %47 %45
+ OpStore %43 %48
+ OpStore %52 %59
+ %62 = OpAccessChain %61 %52 %17
+ %63 = OpLoad %49 %62
+ %64 = OpAccessChain %61 %52 %12
+ %65 = OpLoad %49 %64
+ %66 = OpDot %29 %65 %63
+ OpStore %60 %66
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, variantShader, context.get()));
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/fuzz/transformation_toggle_access_chain_instruction_test.cpp b/third_party/SPIRV-Tools/test/fuzz/transformation_toggle_access_chain_instruction_test.cpp
new file mode 100644
index 0000000..98e0a64
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/fuzz/transformation_toggle_access_chain_instruction_test.cpp
@@ -0,0 +1,413 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_toggle_access_chain_instruction.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationToggleAccessChainInstructionTest, IsApplicableTest) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypeInt 32 0
+ %8 = OpConstant %7 2
+ %9 = OpTypeArray %6 %8
+ %10 = OpTypePointer Function %9
+ %12 = OpConstant %6 1
+ %13 = OpConstant %6 2
+ %14 = OpConstantComposite %9 %12 %13
+ %15 = OpTypePointer Function %6
+ %17 = OpConstant %6 0
+ %29 = OpTypeFloat 32
+ %30 = OpTypeArray %29 %8
+ %31 = OpTypePointer Function %30
+ %33 = OpConstant %29 1
+ %34 = OpConstant %29 2
+ %35 = OpConstantComposite %30 %33 %34
+ %36 = OpTypePointer Function %29
+ %49 = OpTypeVector %29 3
+ %50 = OpTypeArray %49 %8
+ %51 = OpTypePointer Function %50
+ %53 = OpConstant %29 3
+ %54 = OpConstantComposite %49 %33 %34 %53
+ %55 = OpConstant %29 4
+ %56 = OpConstant %29 5
+ %57 = OpConstant %29 6
+ %58 = OpConstantComposite %49 %55 %56 %57
+ %59 = OpConstantComposite %50 %54 %58
+ %61 = OpTypePointer Function %49
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %11 = OpVariable %10 Function
+ %16 = OpVariable %15 Function
+ %23 = OpVariable %15 Function
+ %32 = OpVariable %31 Function
+ %37 = OpVariable %36 Function
+ %43 = OpVariable %36 Function
+ %52 = OpVariable %51 Function
+ %60 = OpVariable %36 Function
+ OpStore %11 %14
+ %18 = OpAccessChain %15 %11 %17
+ %19 = OpLoad %6 %18
+ %20 = OpInBoundsAccessChain %15 %11 %12
+ %21 = OpLoad %6 %20
+ %22 = OpIAdd %6 %19 %21
+ OpStore %16 %22
+ %24 = OpAccessChain %15 %11 %17
+ %25 = OpLoad %6 %24
+ %26 = OpInBoundsAccessChain %15 %11 %12
+ %27 = OpLoad %6 %26
+ %28 = OpIMul %6 %25 %27
+ OpStore %23 %28
+ OpStore %32 %35
+ %38 = OpAccessChain %36 %32 %17
+ %39 = OpLoad %29 %38
+ %40 = OpAccessChain %36 %32 %12
+ %41 = OpLoad %29 %40
+ %42 = OpFAdd %29 %39 %41
+ OpStore %37 %42
+ %44 = OpAccessChain %36 %32 %17
+ %45 = OpLoad %29 %44
+ %46 = OpAccessChain %36 %32 %12
+ %47 = OpLoad %29 %46
+ %48 = OpFMul %29 %45 %47
+ OpStore %43 %48
+ OpStore %52 %59
+ %62 = OpAccessChain %61 %52 %17
+ %63 = OpLoad %49 %62
+ %64 = OpAccessChain %61 %52 %12
+ %65 = OpLoad %49 %64
+ %66 = OpDot %29 %63 %65
+ OpStore %60 %66
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_5;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager factManager;
+
+ // Tests existing access chain instructions
+ auto instructionDescriptor =
+ MakeInstructionDescriptor(18, SpvOpAccessChain, 0);
+ auto transformation =
+ TransformationToggleAccessChainInstruction(instructionDescriptor);
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager));
+
+ instructionDescriptor =
+ MakeInstructionDescriptor(20, SpvOpInBoundsAccessChain, 0);
+ transformation =
+ TransformationToggleAccessChainInstruction(instructionDescriptor);
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager));
+
+ instructionDescriptor = MakeInstructionDescriptor(24, SpvOpAccessChain, 0);
+ transformation =
+ TransformationToggleAccessChainInstruction(instructionDescriptor);
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager));
+
+ instructionDescriptor =
+ MakeInstructionDescriptor(26, SpvOpInBoundsAccessChain, 0);
+ transformation =
+ TransformationToggleAccessChainInstruction(instructionDescriptor);
+ ASSERT_TRUE(transformation.IsApplicable(context.get(), factManager));
+
+ // Tests existing non-access chain instructions
+ instructionDescriptor = MakeInstructionDescriptor(1, SpvOpExtInstImport, 0);
+ transformation =
+ TransformationToggleAccessChainInstruction(instructionDescriptor);
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+ instructionDescriptor = MakeInstructionDescriptor(5, SpvOpLabel, 0);
+ transformation =
+ TransformationToggleAccessChainInstruction(instructionDescriptor);
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+ instructionDescriptor =
+ MakeInstructionDescriptor(14, SpvOpConstantComposite, 0);
+ transformation =
+ TransformationToggleAccessChainInstruction(instructionDescriptor);
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+ // Tests the base instruction id not existing
+ instructionDescriptor = MakeInstructionDescriptor(67, SpvOpAccessChain, 0);
+ transformation =
+ TransformationToggleAccessChainInstruction(instructionDescriptor);
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+ instructionDescriptor = MakeInstructionDescriptor(68, SpvOpAccessChain, 0);
+ transformation =
+ TransformationToggleAccessChainInstruction(instructionDescriptor);
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+ instructionDescriptor =
+ MakeInstructionDescriptor(69, SpvOpInBoundsAccessChain, 0);
+ transformation =
+ TransformationToggleAccessChainInstruction(instructionDescriptor);
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+ // Tests there being no instruction with the desired opcode after the base
+ // instruction id
+ instructionDescriptor = MakeInstructionDescriptor(65, SpvOpAccessChain, 0);
+ transformation =
+ TransformationToggleAccessChainInstruction(instructionDescriptor);
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+ instructionDescriptor =
+ MakeInstructionDescriptor(66, SpvOpInBoundsAccessChain, 0);
+ transformation =
+ TransformationToggleAccessChainInstruction(instructionDescriptor);
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+ // Tests there being an instruction with the desired opcode after the base
+ // instruction id, but the skip count associated with the instruction
+ // descriptor being so high.
+ instructionDescriptor = MakeInstructionDescriptor(11, SpvOpAccessChain, 100);
+ transformation =
+ TransformationToggleAccessChainInstruction(instructionDescriptor);
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+
+ instructionDescriptor =
+ MakeInstructionDescriptor(16, SpvOpInBoundsAccessChain, 100);
+ transformation =
+ TransformationToggleAccessChainInstruction(instructionDescriptor);
+ ASSERT_FALSE(transformation.IsApplicable(context.get(), factManager));
+}
+
+TEST(TransformationToggleAccessChainInstructionTest, ApplyTest) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypeInt 32 0
+ %8 = OpConstant %7 2
+ %9 = OpTypeArray %6 %8
+ %10 = OpTypePointer Function %9
+ %12 = OpConstant %6 1
+ %13 = OpConstant %6 2
+ %14 = OpConstantComposite %9 %12 %13
+ %15 = OpTypePointer Function %6
+ %17 = OpConstant %6 0
+ %29 = OpTypeFloat 32
+ %30 = OpTypeArray %29 %8
+ %31 = OpTypePointer Function %30
+ %33 = OpConstant %29 1
+ %34 = OpConstant %29 2
+ %35 = OpConstantComposite %30 %33 %34
+ %36 = OpTypePointer Function %29
+ %49 = OpTypeVector %29 3
+ %50 = OpTypeArray %49 %8
+ %51 = OpTypePointer Function %50
+ %53 = OpConstant %29 3
+ %54 = OpConstantComposite %49 %33 %34 %53
+ %55 = OpConstant %29 4
+ %56 = OpConstant %29 5
+ %57 = OpConstant %29 6
+ %58 = OpConstantComposite %49 %55 %56 %57
+ %59 = OpConstantComposite %50 %54 %58
+ %61 = OpTypePointer Function %49
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %11 = OpVariable %10 Function
+ %16 = OpVariable %15 Function
+ %23 = OpVariable %15 Function
+ %32 = OpVariable %31 Function
+ %37 = OpVariable %36 Function
+ %43 = OpVariable %36 Function
+ %52 = OpVariable %51 Function
+ %60 = OpVariable %36 Function
+ OpStore %11 %14
+ %18 = OpAccessChain %15 %11 %17
+ %19 = OpLoad %6 %18
+ %20 = OpInBoundsAccessChain %15 %11 %12
+ %21 = OpLoad %6 %20
+ %22 = OpIAdd %6 %19 %21
+ OpStore %16 %22
+ %24 = OpAccessChain %15 %11 %17
+ %25 = OpLoad %6 %24
+ %26 = OpInBoundsAccessChain %15 %11 %12
+ %27 = OpLoad %6 %26
+ %28 = OpIMul %6 %25 %27
+ OpStore %23 %28
+ OpStore %32 %35
+ %38 = OpAccessChain %36 %32 %17
+ %39 = OpLoad %29 %38
+ %40 = OpAccessChain %36 %32 %12
+ %41 = OpLoad %29 %40
+ %42 = OpFAdd %29 %39 %41
+ OpStore %37 %42
+ %44 = OpAccessChain %36 %32 %17
+ %45 = OpLoad %29 %44
+ %46 = OpAccessChain %36 %32 %12
+ %47 = OpLoad %29 %46
+ %48 = OpFMul %29 %45 %47
+ OpStore %43 %48
+ OpStore %52 %59
+ %62 = OpAccessChain %61 %52 %17
+ %63 = OpLoad %49 %62
+ %64 = OpAccessChain %61 %52 %12
+ %65 = OpLoad %49 %64
+ %66 = OpDot %29 %63 %65
+ OpStore %60 %66
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_5;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager factManager;
+
+ auto instructionDescriptor =
+ MakeInstructionDescriptor(18, SpvOpAccessChain, 0);
+ auto transformation =
+ TransformationToggleAccessChainInstruction(instructionDescriptor);
+ transformation.Apply(context.get(), &factManager);
+
+ instructionDescriptor =
+ MakeInstructionDescriptor(20, SpvOpInBoundsAccessChain, 0);
+ transformation =
+ TransformationToggleAccessChainInstruction(instructionDescriptor);
+ transformation.Apply(context.get(), &factManager);
+
+ instructionDescriptor = MakeInstructionDescriptor(24, SpvOpAccessChain, 0);
+ transformation =
+ TransformationToggleAccessChainInstruction(instructionDescriptor);
+ transformation.Apply(context.get(), &factManager);
+
+ instructionDescriptor =
+ MakeInstructionDescriptor(26, SpvOpInBoundsAccessChain, 0);
+ transformation =
+ TransformationToggleAccessChainInstruction(instructionDescriptor);
+ transformation.Apply(context.get(), &factManager);
+
+ instructionDescriptor = MakeInstructionDescriptor(38, SpvOpAccessChain, 0);
+ transformation =
+ TransformationToggleAccessChainInstruction(instructionDescriptor);
+ transformation.Apply(context.get(), &factManager);
+
+ std::string variantShader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeInt 32 1
+ %7 = OpTypeInt 32 0
+ %8 = OpConstant %7 2
+ %9 = OpTypeArray %6 %8
+ %10 = OpTypePointer Function %9
+ %12 = OpConstant %6 1
+ %13 = OpConstant %6 2
+ %14 = OpConstantComposite %9 %12 %13
+ %15 = OpTypePointer Function %6
+ %17 = OpConstant %6 0
+ %29 = OpTypeFloat 32
+ %30 = OpTypeArray %29 %8
+ %31 = OpTypePointer Function %30
+ %33 = OpConstant %29 1
+ %34 = OpConstant %29 2
+ %35 = OpConstantComposite %30 %33 %34
+ %36 = OpTypePointer Function %29
+ %49 = OpTypeVector %29 3
+ %50 = OpTypeArray %49 %8
+ %51 = OpTypePointer Function %50
+ %53 = OpConstant %29 3
+ %54 = OpConstantComposite %49 %33 %34 %53
+ %55 = OpConstant %29 4
+ %56 = OpConstant %29 5
+ %57 = OpConstant %29 6
+ %58 = OpConstantComposite %49 %55 %56 %57
+ %59 = OpConstantComposite %50 %54 %58
+ %61 = OpTypePointer Function %49
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %11 = OpVariable %10 Function
+ %16 = OpVariable %15 Function
+ %23 = OpVariable %15 Function
+ %32 = OpVariable %31 Function
+ %37 = OpVariable %36 Function
+ %43 = OpVariable %36 Function
+ %52 = OpVariable %51 Function
+ %60 = OpVariable %36 Function
+ OpStore %11 %14
+ %18 = OpInBoundsAccessChain %15 %11 %17
+ %19 = OpLoad %6 %18
+ %20 = OpAccessChain %15 %11 %12
+ %21 = OpLoad %6 %20
+ %22 = OpIAdd %6 %19 %21
+ OpStore %16 %22
+ %24 = OpInBoundsAccessChain %15 %11 %17
+ %25 = OpLoad %6 %24
+ %26 = OpAccessChain %15 %11 %12
+ %27 = OpLoad %6 %26
+ %28 = OpIMul %6 %25 %27
+ OpStore %23 %28
+ OpStore %32 %35
+ %38 = OpInBoundsAccessChain %36 %32 %17
+ %39 = OpLoad %29 %38
+ %40 = OpAccessChain %36 %32 %12
+ %41 = OpLoad %29 %40
+ %42 = OpFAdd %29 %39 %41
+ OpStore %37 %42
+ %44 = OpAccessChain %36 %32 %17
+ %45 = OpLoad %29 %44
+ %46 = OpAccessChain %36 %32 %12
+ %47 = OpLoad %29 %46
+ %48 = OpFMul %29 %45 %47
+ OpStore %43 %48
+ OpStore %52 %59
+ %62 = OpAccessChain %61 %52 %17
+ %63 = OpLoad %49 %62
+ %64 = OpAccessChain %61 %52 %12
+ %65 = OpLoad %49 %64
+ %66 = OpDot %29 %63 %65
+ OpStore %60 %66
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, variantShader, context.get()));
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/operand_capabilities_test.cpp b/third_party/SPIRV-Tools/test/operand_capabilities_test.cpp
index 1195597..addb08a 100644
--- a/third_party/SPIRV-Tools/test/operand_capabilities_test.cpp
+++ b/third_party/SPIRV-Tools/test/operand_capabilities_test.cpp
@@ -82,6 +82,13 @@
SpvCapability##CAP1, SpvCapability##CAP2, SpvCapability##CAP3 \
} \
}
+#define CASE4(TYPE, VALUE, CAP1, CAP2, CAP3, CAP4) \
+ { \
+ SPV_OPERAND_TYPE_##TYPE, uint32_t(Spv##VALUE), CapabilitySet { \
+ SpvCapability##CAP1, SpvCapability##CAP2, SpvCapability##CAP3, \
+ SpvCapability##CAP4 \
+ } \
+ }
#define CASE5(TYPE, VALUE, CAP1, CAP2, CAP3, CAP4, CAP5) \
{ \
SPV_OPERAND_TYPE_##TYPE, uint32_t(Spv##VALUE), CapabilitySet { \
@@ -491,8 +498,8 @@
CASE1(BUILT_IN, BuiltInCullDistance, CullDistance), // Bug 1407, 15234
CASE1(BUILT_IN, BuiltInVertexId, Shader),
CASE1(BUILT_IN, BuiltInInstanceId, Shader),
- CASE3(BUILT_IN, BuiltInPrimitiveId, Geometry, Tessellation,
- RayTracingNV),
+ CASE4(BUILT_IN, BuiltInPrimitiveId, Geometry, Tessellation,
+ RayTracingNV, RayTracingProvisionalKHR),
CASE2(BUILT_IN, BuiltInInvocationId, Geometry, Tessellation),
CASE2(BUILT_IN, BuiltInLayer, Geometry, ShaderViewportIndexLayerEXT),
CASE2(BUILT_IN, BuiltInViewportIndex, MultiViewport, ShaderViewportIndexLayerEXT), // Bug 15234
diff --git a/third_party/SPIRV-Tools/test/opt/CMakeLists.txt b/third_party/SPIRV-Tools/test/opt/CMakeLists.txt
index 327f265..3954338 100644
--- a/third_party/SPIRV-Tools/test/opt/CMakeLists.txt
+++ b/third_party/SPIRV-Tools/test/opt/CMakeLists.txt
@@ -55,6 +55,7 @@
insert_extract_elim_test.cpp
inst_bindless_check_test.cpp
inst_buff_addr_check_test.cpp
+ inst_debug_printf_test.cpp
instruction_list_test.cpp
instruction_test.cpp
ir_builder.cpp
diff --git a/third_party/SPIRV-Tools/test/opt/inst_debug_printf_test.cpp b/third_party/SPIRV-Tools/test/opt/inst_debug_printf_test.cpp
new file mode 100644
index 0000000..8123ffb
--- /dev/null
+++ b/third_party/SPIRV-Tools/test/opt/inst_debug_printf_test.cpp
@@ -0,0 +1,215 @@
+// Copyright (c) 2020 Valve Corporation
+// Copyright (c) 2020 LunarG Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Debug Printf Instrumentation Tests.
+
+#include <string>
+#include <vector>
+
+#include "test/opt/assembly_builder.h"
+#include "test/opt/pass_fixture.h"
+#include "test/opt/pass_utils.h"
+
+namespace spvtools {
+namespace opt {
+namespace {
+
+using InstDebugPrintfTest = PassTest<::testing::Test>;
+
+TEST_F(InstDebugPrintfTest, V4Float32) {
+ // SamplerState g_sDefault;
+ // Texture2D g_tColor;
+ //
+ // struct PS_INPUT
+ // {
+ // float2 vBaseTexCoord : TEXCOORD0;
+ // };
+ //
+ // struct PS_OUTPUT
+ // {
+ // float4 vDiffuse : SV_Target0;
+ // };
+ //
+ // PS_OUTPUT MainPs(PS_INPUT i)
+ // {
+ // PS_OUTPUT o;
+ //
+ // o.vDiffuse.rgba = g_tColor.Sample(g_sDefault, (i.vBaseTexCoord.xy).xy);
+ // debugPrintfEXT("diffuse: %v4f", o.vDiffuse.rgba);
+ // return o;
+ // }
+
+ const std::string defs =
+ R"(OpCapability Shader
+OpExtension "SPV_KHR_non_semantic_info"
+%1 = OpExtInstImport "NonSemantic.DebugPrintf"
+; CHECK-NOT: OpExtension "SPV_KHR_non_semantic_info"
+; CHECK-NOT: %1 = OpExtInstImport "NonSemantic.DebugPrintf"
+; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %2 "MainPs" %3 %4
+; CHECK: OpEntryPoint Fragment %2 "MainPs" %3 %4 %gl_FragCoord
+OpExecutionMode %2 OriginUpperLeft
+%5 = OpString "Color is %vn"
+)";
+
+ const std::string decorates =
+ R"(OpDecorate %6 DescriptorSet 0
+OpDecorate %6 Binding 1
+OpDecorate %7 DescriptorSet 0
+OpDecorate %7 Binding 0
+OpDecorate %3 Location 0
+OpDecorate %4 Location 0
+; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4
+; CHECK: OpDecorate %_struct_47 Block
+; CHECK: OpMemberDecorate %_struct_47 0 Offset 0
+; CHECK: OpMemberDecorate %_struct_47 1 Offset 4
+; CHECK: OpDecorate %49 DescriptorSet 7
+; CHECK: OpDecorate %49 Binding 3
+; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord
+)";
+
+ const std::string globals =
+ R"(%void = OpTypeVoid
+%9 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%v2float = OpTypeVector %float 2
+%v4float = OpTypeVector %float 4
+%13 = OpTypeImage %float 2D 0 0 0 1 Unknown
+%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
+%6 = OpVariable %_ptr_UniformConstant_13 UniformConstant
+%15 = OpTypeSampler
+%_ptr_UniformConstant_15 = OpTypePointer UniformConstant %15
+%7 = OpVariable %_ptr_UniformConstant_15 UniformConstant
+%17 = OpTypeSampledImage %13
+%_ptr_Input_v2float = OpTypePointer Input %v2float
+%3 = OpVariable %_ptr_Input_v2float Input
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%4 = OpVariable %_ptr_Output_v4float Output
+; CHECK: %uint = OpTypeInt 32 0
+; CHECK: %38 = OpTypeFunction %void %uint %uint %uint %uint %uint %uint
+; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
+; CHECK: %_struct_47 = OpTypeStruct %uint %_runtimearr_uint
+; CHECK: %_ptr_StorageBuffer__struct_47 = OpTypePointer StorageBuffer %_struct_47
+; CHECK: %49 = OpVariable %_ptr_StorageBuffer__struct_47 StorageBuffer
+; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+; CHECK: %bool = OpTypeBool
+; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float
+; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input
+; CHECK: %v4uint = OpTypeVector %uint 4
+)";
+
+ const std::string main =
+ R"(%2 = OpFunction %void None %9
+%20 = OpLabel
+%21 = OpLoad %v2float %3
+%22 = OpLoad %13 %6
+%23 = OpLoad %15 %7
+%24 = OpSampledImage %17 %22 %23
+%25 = OpImageSampleImplicitLod %v4float %24 %21
+%26 = OpExtInst %void %1 1 %5 %25
+; CHECK-NOT: %26 = OpExtInst %void %1 1 %5 %25
+; CHECK: %29 = OpCompositeExtract %float %25 0
+; CHECK: %30 = OpBitcast %uint %29
+; CHECK: %31 = OpCompositeExtract %float %25 1
+; CHECK: %32 = OpBitcast %uint %31
+; CHECK: %33 = OpCompositeExtract %float %25 2
+; CHECK: %34 = OpBitcast %uint %33
+; CHECK: %35 = OpCompositeExtract %float %25 3
+; CHECK: %36 = OpBitcast %uint %35
+; CHECK: %101 = OpFunctionCall %void %37 %uint_36 %uint_5 %30 %32 %34 %36
+; CHECK: OpBranch %102
+; CHECK: %102 = OpLabel
+OpStore %4 %25
+OpReturn
+OpFunctionEnd
+)";
+
+ const std::string output_func =
+ R"(; CHECK: %37 = OpFunction %void None %38
+; CHECK: %39 = OpFunctionParameter %uint
+; CHECK: %40 = OpFunctionParameter %uint
+; CHECK: %41 = OpFunctionParameter %uint
+; CHECK: %42 = OpFunctionParameter %uint
+; CHECK: %43 = OpFunctionParameter %uint
+; CHECK: %44 = OpFunctionParameter %uint
+; CHECK: %45 = OpLabel
+; CHECK: %52 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_0
+; CHECK: %55 = OpAtomicIAdd %uint %52 %uint_4 %uint_0 %uint_12
+; CHECK: %56 = OpIAdd %uint %55 %uint_12
+; CHECK: %57 = OpArrayLength %uint %49 1
+; CHECK: %59 = OpULessThanEqual %bool %56 %57
+; CHECK: OpSelectionMerge %60 None
+; CHECK: OpBranchConditional %59 %61 %60
+; CHECK: %61 = OpLabel
+; CHECK: %62 = OpIAdd %uint %55 %uint_0
+; CHECK: %64 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %62
+; CHECK: OpStore %64 %uint_12
+; CHECK: %66 = OpIAdd %uint %55 %uint_1
+; CHECK: %67 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %66
+; CHECK: OpStore %67 %uint_23
+; CHECK: %69 = OpIAdd %uint %55 %uint_2
+; CHECK: %70 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %69
+; CHECK: OpStore %70 %39
+; CHECK: %72 = OpIAdd %uint %55 %uint_3
+; CHECK: %73 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %72
+; CHECK: OpStore %73 %uint_4
+; CHECK: %76 = OpLoad %v4float %gl_FragCoord
+; CHECK: %78 = OpBitcast %v4uint %76
+; CHECK: %79 = OpCompositeExtract %uint %78 0
+; CHECK: %80 = OpIAdd %uint %55 %uint_4
+; CHECK: %81 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %80
+; CHECK: OpStore %81 %79
+; CHECK: %82 = OpCompositeExtract %uint %78 1
+; CHECK: %83 = OpIAdd %uint %55 %uint_5
+; CHECK: %84 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %83
+; CHECK: OpStore %84 %82
+; CHECK: %86 = OpIAdd %uint %55 %uint_7
+; CHECK: %87 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %86
+; CHECK: OpStore %87 %40
+; CHECK: %89 = OpIAdd %uint %55 %uint_8
+; CHECK: %90 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %89
+; CHECK: OpStore %90 %41
+; CHECK: %92 = OpIAdd %uint %55 %uint_9
+; CHECK: %93 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %92
+; CHECK: OpStore %93 %42
+; CHECK: %95 = OpIAdd %uint %55 %uint_10
+; CHECK: %96 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %95
+; CHECK: OpStore %96 %43
+; CHECK: %98 = OpIAdd %uint %55 %uint_11
+; CHECK: %99 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %98
+; CHECK: OpStore %99 %44
+; CHECK: OpBranch %60
+; CHECK: %60 = OpLabel
+; CHECK: OpReturn
+; CHECK: OpFunctionEnd
+)";
+
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SinglePassRunAndMatch<InstDebugPrintfPass>(
+ defs + decorates + globals + main + output_func, true);
+}
+
+// TODO(greg-lunarg): Add tests to verify handling of these cases:
+//
+// Compute shader
+// Geometry shader
+// Tesselation control shader
+// Tesselation eval shader
+// Vertex shader
+
+} // namespace
+} // namespace opt
+} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/opt/instruction_test.cpp b/third_party/SPIRV-Tools/test/opt/instruction_test.cpp
index a697201..1995c5b 100644
--- a/third_party/SPIRV-Tools/test/opt/instruction_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/instruction_test.cpp
@@ -14,6 +14,7 @@
#include <memory>
#include <string>
+#include <utility>
#include <vector>
#include "gmock/gmock.h"
@@ -34,6 +35,7 @@
using OpaqueTypeTest = PassTest<::testing::Test>;
using GetBaseTest = PassTest<::testing::Test>;
using ValidBasePointerTest = PassTest<::testing::Test>;
+using VulkanBufferTest = PassTest<::testing::Test>;
TEST(InstructionTest, CreateTrivial) {
Instruction empty;
@@ -60,6 +62,18 @@
EXPECT_EQ(inst.end(), inst.begin());
}
+TEST(InstructionTest, OperandAsCString) {
+ Operand::OperandData abcde{0x64636261, 0x65};
+ Operand operand(SPV_OPERAND_TYPE_LITERAL_STRING, std::move(abcde));
+ EXPECT_STREQ("abcde", operand.AsCString());
+}
+
+TEST(InstructionTest, OperandAsString) {
+ Operand::OperandData abcde{0x64636261, 0x65};
+ Operand operand(SPV_OPERAND_TYPE_LITERAL_STRING, std::move(abcde));
+ EXPECT_EQ("abcde", operand.AsString());
+}
+
// The words for an OpTypeInt for 32-bit signed integer resulting in Id 44.
uint32_t kSampleInstructionWords[] = {(4 << 16) | uint32_t(SpvOpTypeInt), 44,
32, 1};
@@ -1130,6 +1144,260 @@
EXPECT_TRUE(null_inst->IsValidBasePointer());
}
+TEST_F(VulkanBufferTest, VulkanStorageBuffer) {
+ const std::string text = R"(
+OpCapability Shader
+OpCapability RuntimeDescriptorArray
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %1 "main"
+OpExecutionMode %1 LocalSize 1 1 1
+OpDecorate %2 Block
+OpMemberDecorate %2 0 Offset 0
+OpDecorate %3 BufferBlock
+OpMemberDecorate %3 0 Offset 0
+%4 = OpTypeVoid
+%5 = OpTypeInt 32 0
+%2 = OpTypeStruct %5
+%3 = OpTypeStruct %5
+
+%6 = OpTypePointer StorageBuffer %2
+%7 = OpTypePointer Uniform %2
+%8 = OpTypePointer Uniform %3
+
+%9 = OpConstant %5 1
+%10 = OpTypeArray %2 %9
+%11 = OpTypeArray %3 %9
+%12 = OpTypePointer StorageBuffer %10
+%13 = OpTypePointer Uniform %10
+%14 = OpTypePointer Uniform %11
+
+%15 = OpTypeRuntimeArray %2
+%16 = OpTypeRuntimeArray %3
+%17 = OpTypePointer StorageBuffer %15
+%18 = OpTypePointer Uniform %15
+%19 = OpTypePointer Uniform %16
+
+%50 = OpTypeFunction %4
+%1 = OpFunction %4 None %50
+%51 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ std::unique_ptr<IRContext> context =
+ BuildModule(SPV_ENV_UNIVERSAL_1_3, nullptr, text);
+ EXPECT_NE(context, nullptr);
+
+ // Standard SSBO and UBO
+ Instruction* inst = context->get_def_use_mgr()->GetDef(6);
+ EXPECT_EQ(true, inst->IsVulkanStorageBuffer());
+ inst = context->get_def_use_mgr()->GetDef(7);
+ EXPECT_EQ(false, inst->IsVulkanStorageBuffer());
+ inst = context->get_def_use_mgr()->GetDef(8);
+ EXPECT_EQ(true, inst->IsVulkanStorageBuffer());
+
+ // Arrayed SSBO and UBO
+ inst = context->get_def_use_mgr()->GetDef(12);
+ EXPECT_EQ(true, inst->IsVulkanStorageBuffer());
+ inst = context->get_def_use_mgr()->GetDef(13);
+ EXPECT_EQ(false, inst->IsVulkanStorageBuffer());
+ inst = context->get_def_use_mgr()->GetDef(14);
+ EXPECT_EQ(true, inst->IsVulkanStorageBuffer());
+
+ // Runtime arrayed SSBO and UBO
+ inst = context->get_def_use_mgr()->GetDef(17);
+ EXPECT_EQ(true, inst->IsVulkanStorageBuffer());
+ inst = context->get_def_use_mgr()->GetDef(18);
+ EXPECT_EQ(false, inst->IsVulkanStorageBuffer());
+ inst = context->get_def_use_mgr()->GetDef(19);
+ EXPECT_EQ(true, inst->IsVulkanStorageBuffer());
+}
+
+TEST_F(VulkanBufferTest, VulkanUniformBuffer) {
+ const std::string text = R"(
+OpCapability Shader
+OpCapability RuntimeDescriptorArray
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %1 "main"
+OpExecutionMode %1 LocalSize 1 1 1
+OpDecorate %2 Block
+OpMemberDecorate %2 0 Offset 0
+OpDecorate %3 BufferBlock
+OpMemberDecorate %3 0 Offset 0
+%4 = OpTypeVoid
+%5 = OpTypeInt 32 0
+%2 = OpTypeStruct %5
+%3 = OpTypeStruct %5
+
+%6 = OpTypePointer StorageBuffer %2
+%7 = OpTypePointer Uniform %2
+%8 = OpTypePointer Uniform %3
+
+%9 = OpConstant %5 1
+%10 = OpTypeArray %2 %9
+%11 = OpTypeArray %3 %9
+%12 = OpTypePointer StorageBuffer %10
+%13 = OpTypePointer Uniform %10
+%14 = OpTypePointer Uniform %11
+
+%15 = OpTypeRuntimeArray %2
+%16 = OpTypeRuntimeArray %3
+%17 = OpTypePointer StorageBuffer %15
+%18 = OpTypePointer Uniform %15
+%19 = OpTypePointer Uniform %16
+
+%50 = OpTypeFunction %4
+%1 = OpFunction %4 None %50
+%51 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ std::unique_ptr<IRContext> context =
+ BuildModule(SPV_ENV_UNIVERSAL_1_3, nullptr, text);
+ EXPECT_NE(context, nullptr);
+
+ // Standard SSBO and UBO
+ Instruction* inst = context->get_def_use_mgr()->GetDef(6);
+ EXPECT_EQ(false, inst->IsVulkanUniformBuffer());
+ inst = context->get_def_use_mgr()->GetDef(7);
+ EXPECT_EQ(true, inst->IsVulkanUniformBuffer());
+ inst = context->get_def_use_mgr()->GetDef(8);
+ EXPECT_EQ(false, inst->IsVulkanUniformBuffer());
+
+ // Arrayed SSBO and UBO
+ inst = context->get_def_use_mgr()->GetDef(12);
+ EXPECT_EQ(false, inst->IsVulkanUniformBuffer());
+ inst = context->get_def_use_mgr()->GetDef(13);
+ EXPECT_EQ(true, inst->IsVulkanUniformBuffer());
+ inst = context->get_def_use_mgr()->GetDef(14);
+ EXPECT_EQ(false, inst->IsVulkanUniformBuffer());
+
+ // Runtime arrayed SSBO and UBO
+ inst = context->get_def_use_mgr()->GetDef(17);
+ EXPECT_EQ(false, inst->IsVulkanUniformBuffer());
+ inst = context->get_def_use_mgr()->GetDef(18);
+ EXPECT_EQ(true, inst->IsVulkanUniformBuffer());
+ inst = context->get_def_use_mgr()->GetDef(19);
+ EXPECT_EQ(false, inst->IsVulkanUniformBuffer());
+}
+
+TEST_F(VulkanBufferTest, ImageQueries) {
+ const std::string text = R"(
+OpCapability Shader
+OpCapability ImageBuffer
+OpCapability RuntimeDescriptorArray
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %1 "main"
+OpExecutionMode %1 LocalSize 1 1 1
+%2 = OpTypeVoid
+%3 = OpTypeFloat 32
+
+%4 = OpTypeImage %3 Buffer 0 0 0 1 Rgba32f
+%5 = OpTypeImage %3 Buffer 0 0 0 2 Rgba32f
+%6 = OpTypeImage %3 2D 0 0 0 1 Rgba32f
+%7 = OpTypeImage %3 2D 0 0 0 2 Rgba32f
+
+%8 = OpTypePointer UniformConstant %4
+%9 = OpTypePointer UniformConstant %5
+%10 = OpTypePointer UniformConstant %6
+%11 = OpTypePointer UniformConstant %7
+
+%12 = OpTypeInt 32 0
+%13 = OpConstant %12 1
+%14 = OpTypeArray %4 %13
+%15 = OpTypeArray %5 %13
+%16 = OpTypeArray %6 %13
+%17 = OpTypeArray %7 %13
+%18 = OpTypePointer UniformConstant %14
+%19 = OpTypePointer UniformConstant %15
+%20 = OpTypePointer UniformConstant %16
+%21 = OpTypePointer UniformConstant %17
+
+%22 = OpTypeRuntimeArray %4
+%23 = OpTypeRuntimeArray %5
+%24 = OpTypeRuntimeArray %6
+%25 = OpTypeRuntimeArray %7
+%26 = OpTypePointer UniformConstant %22
+%27 = OpTypePointer UniformConstant %23
+%28 = OpTypePointer UniformConstant %24
+%29 = OpTypePointer UniformConstant %25
+
+%50 = OpTypeFunction %4
+%1 = OpFunction %4 None %50
+%51 = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ std::unique_ptr<IRContext> context =
+ BuildModule(SPV_ENV_UNIVERSAL_1_3, nullptr, text);
+ EXPECT_NE(context, nullptr);
+
+ // Bare pointers
+ Instruction* inst = context->get_def_use_mgr()->GetDef(8);
+ EXPECT_EQ(false, inst->IsVulkanStorageImage());
+ EXPECT_EQ(false, inst->IsVulkanSampledImage());
+ EXPECT_EQ(false, inst->IsVulkanStorageTexelBuffer());
+
+ inst = context->get_def_use_mgr()->GetDef(9);
+ EXPECT_EQ(false, inst->IsVulkanStorageImage());
+ EXPECT_EQ(false, inst->IsVulkanSampledImage());
+ EXPECT_EQ(true, inst->IsVulkanStorageTexelBuffer());
+
+ inst = context->get_def_use_mgr()->GetDef(10);
+ EXPECT_EQ(false, inst->IsVulkanStorageImage());
+ EXPECT_EQ(true, inst->IsVulkanSampledImage());
+ EXPECT_EQ(false, inst->IsVulkanStorageTexelBuffer());
+
+ inst = context->get_def_use_mgr()->GetDef(11);
+ EXPECT_EQ(true, inst->IsVulkanStorageImage());
+ EXPECT_EQ(false, inst->IsVulkanSampledImage());
+ EXPECT_EQ(false, inst->IsVulkanStorageTexelBuffer());
+
+ // Array pointers
+ inst = context->get_def_use_mgr()->GetDef(18);
+ EXPECT_EQ(false, inst->IsVulkanStorageImage());
+ EXPECT_EQ(false, inst->IsVulkanSampledImage());
+ EXPECT_EQ(false, inst->IsVulkanStorageTexelBuffer());
+
+ inst = context->get_def_use_mgr()->GetDef(19);
+ EXPECT_EQ(false, inst->IsVulkanStorageImage());
+ EXPECT_EQ(false, inst->IsVulkanSampledImage());
+ EXPECT_EQ(true, inst->IsVulkanStorageTexelBuffer());
+
+ inst = context->get_def_use_mgr()->GetDef(20);
+ EXPECT_EQ(false, inst->IsVulkanStorageImage());
+ EXPECT_EQ(true, inst->IsVulkanSampledImage());
+ EXPECT_EQ(false, inst->IsVulkanStorageTexelBuffer());
+
+ inst = context->get_def_use_mgr()->GetDef(21);
+ EXPECT_EQ(true, inst->IsVulkanStorageImage());
+ EXPECT_EQ(false, inst->IsVulkanSampledImage());
+ EXPECT_EQ(false, inst->IsVulkanStorageTexelBuffer());
+
+ // Runtime array pointers
+ inst = context->get_def_use_mgr()->GetDef(26);
+ EXPECT_EQ(false, inst->IsVulkanStorageImage());
+ EXPECT_EQ(false, inst->IsVulkanSampledImage());
+ EXPECT_EQ(false, inst->IsVulkanStorageTexelBuffer());
+
+ inst = context->get_def_use_mgr()->GetDef(27);
+ EXPECT_EQ(false, inst->IsVulkanStorageImage());
+ EXPECT_EQ(false, inst->IsVulkanSampledImage());
+ EXPECT_EQ(true, inst->IsVulkanStorageTexelBuffer());
+
+ inst = context->get_def_use_mgr()->GetDef(28);
+ EXPECT_EQ(false, inst->IsVulkanStorageImage());
+ EXPECT_EQ(true, inst->IsVulkanSampledImage());
+ EXPECT_EQ(false, inst->IsVulkanStorageTexelBuffer());
+
+ inst = context->get_def_use_mgr()->GetDef(29);
+ EXPECT_EQ(true, inst->IsVulkanStorageImage());
+ EXPECT_EQ(false, inst->IsVulkanSampledImage());
+ EXPECT_EQ(false, inst->IsVulkanStorageTexelBuffer());
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/opt/ir_builder.cpp b/third_party/SPIRV-Tools/test/opt/ir_builder.cpp
index f800ca4..cb234e0 100644
--- a/third_party/SPIRV-Tools/test/opt/ir_builder.cpp
+++ b/third_party/SPIRV-Tools/test/opt/ir_builder.cpp
@@ -408,7 +408,7 @@
TEST_F(IRBuilderTest, AccelerationStructureNV) {
const std::string text = R"(
-; CHECK: OpTypeAccelerationStructureNV
+; CHECK: OpTypeAccelerationStructureKHR
OpCapability Shader
OpCapability RayTracingNV
OpExtension "SPV_NV_ray_tracing"
diff --git a/third_party/SPIRV-Tools/test/opt/ir_loader_test.cpp b/third_party/SPIRV-Tools/test/opt/ir_loader_test.cpp
index c60e853..50e3a08 100644
--- a/third_party/SPIRV-Tools/test/opt/ir_loader_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/ir_loader_test.cpp
@@ -234,34 +234,33 @@
%44 = OpExtInst %void %1 DebugLocalVariable %16 %40 %34 6 16 %43 FlagIsLocal 0
%45 = OpExtInst %void %1 DebugLocalVariable %17 %40 %34 7 16 %43 FlagIsLocal 1
%46 = OpExtInst %void %1 DebugLocalVariable %18 %36 %34 8 3 %43 FlagIsLocal
-%47 = OpExtInst %void %1 DebugDeclare %44 %pos %42
-%48 = OpExtInst %void %1 DebugDeclare %45 %color %42
OpLine %7 6 1
%main = OpFunction %void None %31
-%49 = OpLabel
-%50 = OpExtInst %void %1 DebugScope %43
+%47 = OpLabel
+%60 = OpExtInst %void %1 DebugScope %43
OpLine %7 8 13
%vout = OpVariable %_ptr_Function_VS_OUTPUT Function
-%51 = OpExtInst %void %1 DebugDeclare %46 %vout %42
+%49 = OpExtInst %void %1 DebugDeclare %46 %vout %42
OpLine %7 9 14
-%52 = OpLoad %v4float %pos
+%50 = OpLoad %v4float %pos
OpLine %7 9 3
-%53 = OpAccessChain %_ptr_Function_v4float %vout %int_0
-%54 = OpExtInst %void %1 DebugValue %46 %53 %42 %int_0
-OpStore %53 %52
+%51 = OpAccessChain %_ptr_Function_v4float %vout %int_0
+%52 = OpExtInst %void %1 DebugValue %46 %51 %42 %int_0
+OpStore %51 %50
OpLine %7 10 16
-%55 = OpLoad %v4float %color
+%53 = OpLoad %v4float %color
OpLine %7 10 3
-%56 = OpAccessChain %_ptr_Function_v4float %vout %int_1
-%57 = OpExtInst %void %1 DebugValue %46 %56 %42 %int_1
-OpStore %56 %55
+%54 = OpAccessChain %_ptr_Function_v4float %vout %int_1
+%55 = OpExtInst %void %1 DebugValue %46 %54 %42 %int_1
+OpStore %54 %53
OpLine %7 11 10
-%58 = OpLoad %VS_OUTPUT %vout
+%56 = OpLoad %VS_OUTPUT %vout
OpLine %7 11 3
-%59 = OpCompositeExtract %v4float %58 0
-OpStore %gl_Position %59
-%60 = OpCompositeExtract %v4float %58 1
-OpStore %out_var_COLOR %60
+%57 = OpCompositeExtract %v4float %56 0
+OpStore %gl_Position %57
+%58 = OpCompositeExtract %v4float %56 1
+OpStore %out_var_COLOR %58
+%61 = OpExtInst %void %1 DebugNoScope
OpReturn
OpFunctionEnd
)");
@@ -377,7 +376,7 @@
OpLine %5 12 1
%main = OpFunction %void None %36
%bb_entry = OpLabel
-%52 = OpExtInst %void %1 DebugScope %47
+%70 = OpExtInst %void %1 DebugScope %47
OpLine %5 13 16
%param_var_arg1 = OpVariable %_ptr_Function_float Function
%53 = OpLoad %float %pos
@@ -386,6 +385,7 @@
%54 = OpFunctionCall %v4float %func1 %param_var_arg1
OpLine %5 13 3
OpStore %gl_Position %54
+%71 = OpExtInst %void %1 DebugNoScope
OpReturn
OpFunctionEnd
OpLine %5 5 1
@@ -393,41 +393,45 @@
OpLine %5 5 20
%arg1 = OpFunctionParameter %_ptr_Function_float
%bb_entry_0 = OpLabel
-%55 = OpExtInst %void %1 DebugScope %48
+%72 = OpExtInst %void %1 DebugScope %48
OpLine %5 9 16
%param_var_arg2 = OpVariable %_ptr_Function_float Function
OpLine %5 6 7
-%56 = OpLoad %float %arg1
+%57 = OpLoad %float %arg1
OpLine %5 6 12
-%57 = OpFOrdGreaterThan %bool %56 %float_1
+%58 = OpFOrdGreaterThan %bool %57 %float_1
OpLine %5 6 17
+%73 = OpExtInst %void %1 DebugNoScope
OpSelectionMerge %if_merge None
-OpBranchConditional %57 %if_true %if_merge
+OpBranchConditional %58 %if_true %if_merge
%if_true = OpLabel
-%58 = OpExtInst %void %1 DebugScope %50
+%74 = OpExtInst %void %1 DebugScope %50
OpLine %5 7 5
+%75 = OpExtInst %void %1 DebugNoScope
OpReturnValue %32
%if_merge = OpLabel
-%59 = OpExtInst %void %1 DebugScope %51
+%76 = OpExtInst %void %1 DebugScope %51
OpLine %5 9 16
-%60 = OpLoad %float %arg1
-OpStore %param_var_arg2 %60
+%63 = OpLoad %float %arg1
+OpStore %param_var_arg2 %63
OpLine %5 9 10
-%61 = OpFunctionCall %v4float %func2 %param_var_arg2
+%64 = OpFunctionCall %v4float %func2 %param_var_arg2
OpLine %5 9 3
-OpReturnValue %61
+%77 = OpExtInst %void %1 DebugNoScope
+OpReturnValue %64
OpFunctionEnd
OpLine %5 1 1
%func2 = OpFunction %v4float None %38
OpLine %5 1 20
%arg2 = OpFunctionParameter %_ptr_Function_float
%bb_entry_1 = OpLabel
-%62 = OpExtInst %void %1 DebugScope %49
+%78 = OpExtInst %void %1 DebugScope %49
OpLine %5 2 17
-%63 = OpLoad %float %arg2
-%64 = OpCompositeConstruct %v4float %63 %float_0 %float_0 %float_0
+%67 = OpLoad %float %arg2
+%68 = OpCompositeConstruct %v4float %67 %float_0 %float_0 %float_0
OpLine %5 2 3
-OpReturnValue %64
+%79 = OpExtInst %void %1 DebugNoScope
+OpReturnValue %68
OpFunctionEnd
)");
}
@@ -547,28 +551,31 @@
OpLine %5 12 1
%main = OpFunction %void None %28
%bb_entry = OpLabel
-%51 = OpExtInst %void %1 DebugScope %44 %49
+%62 = OpExtInst %void %1 DebugScope %44 %49
OpLine %5 6 7
%52 = OpLoad %float %pos
OpLine %5 6 12
%53 = OpFOrdGreaterThan %bool %52 %float_1
OpLine %5 6 17
+%63 = OpExtInst %void %1 DebugNoScope
OpSelectionMerge %if_merge None
OpBranchConditional %53 %if_true %if_merge
%if_true = OpLabel
-%54 = OpExtInst %void %1 DebugScope %46 %49
+%64 = OpExtInst %void %1 DebugScope %46 %49
OpLine %5 7 5
OpStore %gl_Position %24
+%65 = OpExtInst %void %1 DebugNoScope
OpReturn
%if_merge = OpLabel
-%55 = OpExtInst %void %1 DebugScope %45 %50
+%66 = OpExtInst %void %1 DebugScope %45 %50
OpLine %5 2 17
-%56 = OpLoad %float %pos
+%58 = OpLoad %float %pos
OpLine %5 2 10
-%57 = OpCompositeConstruct %v4float %56 %float_0 %float_0 %float_0
-%58 = OpExtInst %void %1 DebugScope %43
+%59 = OpCompositeConstruct %v4float %58 %float_0 %float_0 %float_0
+%67 = OpExtInst %void %1 DebugScope %43
OpLine %5 13 3
-OpStore %gl_Position %57
+OpStore %gl_Position %59
+%68 = OpExtInst %void %1 DebugNoScope
OpReturn
OpFunctionEnd
)");
@@ -683,8 +690,8 @@
%51 = OpExtInst %void %1 DebugLexicalBlock %40 9 3 %48
OpLine %5 12 1
%main = OpFunction %void None %36
-%52 = OpExtInst %void %1 DebugScope %47
%bb_entry = OpLabel
+%70 = OpExtInst %void %1 DebugScope %47
OpLine %5 13 16
%param_var_arg1 = OpVariable %_ptr_Function_float Function
%53 = OpLoad %float %pos
@@ -693,6 +700,7 @@
%54 = OpFunctionCall %v4float %func1 %param_var_arg1
OpLine %5 13 3
OpStore %gl_Position %54
+%71 = OpExtInst %void %1 DebugNoScope
OpReturn
OpFunctionEnd
OpLine %5 5 1
@@ -700,48 +708,244 @@
OpLine %5 5 20
%arg1 = OpFunctionParameter %_ptr_Function_float
%bb_entry_0 = OpLabel
-%55 = OpExtInst %void %1 DebugScope %48
+%72 = OpExtInst %void %1 DebugScope %48
OpLine %5 9 16
%param_var_arg2 = OpVariable %_ptr_Function_float Function
OpLine %5 6 7
-%56 = OpLoad %float %arg1
+%57 = OpLoad %float %arg1
OpLine %5 6 12
-%57 = OpFOrdGreaterThan %bool %56 %float_1
+%58 = OpFOrdGreaterThan %bool %57 %float_1
OpLine %5 6 17
+%73 = OpExtInst %void %1 DebugNoScope
OpSelectionMerge %if_merge None
-OpBranchConditional %57 %if_true %if_merge
+OpBranchConditional %58 %if_true %if_merge
%if_true = OpLabel
-%58 = OpExtInst %void %1 DebugScope %50
+%74 = OpExtInst %void %1 DebugScope %50
OpLine %5 7 5
+%75 = OpExtInst %void %1 DebugNoScope
OpReturnValue %32
%if_merge = OpLabel
-%59 = OpExtInst %void %1 DebugScope %51
+%76 = OpExtInst %void %1 DebugScope %51
OpLine %5 9 16
-%60 = OpLoad %float %arg1
-OpStore %param_var_arg2 %60
+%63 = OpLoad %float %arg1
+OpStore %param_var_arg2 %63
OpLine %5 9 10
-%61 = OpFunctionCall %v4float %func2 %param_var_arg2
+%64 = OpFunctionCall %v4float %func2 %param_var_arg2
OpLine %5 9 3
-OpReturnValue %61
+%77 = OpExtInst %void %1 DebugNoScope
+OpReturnValue %64
OpFunctionEnd
OpLine %5 1 1
%func2 = OpFunction %v4float None %38
OpLine %5 1 20
%arg2 = OpFunctionParameter %_ptr_Function_float
%bb_entry_1 = OpLabel
-%62 = OpExtInst %void %1 DebugScope %49
+%78 = OpExtInst %void %1 DebugScope %49
OpLine %5 2 17
-%63 = OpLoad %float %arg2
-%64 = OpCompositeConstruct %v4float %63 %float_0 %float_0 %float_0
+%67 = OpLoad %float %arg2
+%68 = OpCompositeConstruct %v4float %67 %float_0 %float_0 %float_0
OpLine %5 2 3
-OpReturnValue %64
+%79 = OpExtInst %void %1 DebugNoScope
+OpReturnValue %68
OpFunctionEnd
)";
SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
std::unique_ptr<IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text);
- ASSERT_EQ(nullptr, context);
+ ASSERT_NE(nullptr, context);
+
+ std::vector<uint32_t> binary;
+ context->module()->ToBinary(&binary, /* skip_nop = */ false);
+
+ std::string disassembled_text;
+ EXPECT_TRUE(t.Disassemble(binary, &disassembled_text));
+ EXPECT_EQ(text, disassembled_text);
+}
+
+TEST(IrBuilder, DebugInfoInstInFunctionOutOfBlock2) {
+ // /* HLSL */
+ //
+ // struct VS_OUTPUT {
+ // float4 pos : SV_POSITION;
+ // float4 color : COLOR;
+ // };
+ //
+ // VS_OUTPUT main(float4 pos : POSITION,
+ // float4 color : COLOR) {
+ // VS_OUTPUT vout;
+ // vout.pos = pos;
+ // vout.color = color;
+ // return vout;
+ // }
+ const std::string text = R"(OpCapability Shader
+%1 = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %main "main" %in_var_POSITION %in_var_COLOR %gl_Position %out_var_COLOR
+%7 = OpString "vs.hlsl"
+OpSource HLSL 600 %7 "#line 1 \"vs.hlsl\"
+struct VS_OUTPUT {
+ float4 pos : SV_POSITION;
+ float4 color : COLOR;
+};
+
+VS_OUTPUT main(float4 pos : POSITION,
+ float4 color : COLOR) {
+ VS_OUTPUT vout;
+ vout.pos = pos;
+ vout.color = color;
+ return vout;
+}
+"
+%8 = OpString "#line 1 \"vs.hlsl\"
+struct VS_OUTPUT {
+ float4 pos : SV_POSITION;
+ float4 color : COLOR;
+};
+
+VS_OUTPUT main(float4 pos : POSITION,
+ float4 color : COLOR) {
+ VS_OUTPUT vout;
+ vout.pos = pos;
+ vout.color = color;
+ return vout;
+}
+"
+%9 = OpString "VS_OUTPUT"
+%10 = OpString "float"
+%11 = OpString "src.main"
+%12 = OpString "pos"
+%13 = OpString "color"
+%14 = OpString "vout"
+OpName %in_var_POSITION "in.var.POSITION"
+OpName %in_var_COLOR "in.var.COLOR"
+OpName %out_var_COLOR "out.var.COLOR"
+OpName %main "main"
+OpName %param_var_pos "param.var.pos"
+OpName %param_var_color "param.var.color"
+OpName %VS_OUTPUT "VS_OUTPUT"
+OpMemberName %VS_OUTPUT 0 "pos"
+OpMemberName %VS_OUTPUT 1 "color"
+OpName %src_main "src.main"
+OpName %pos "pos"
+OpName %color "color"
+OpName %bb_entry "bb.entry"
+OpName %vout "vout"
+OpDecorate %gl_Position BuiltIn Position
+OpDecorate %in_var_POSITION Location 0
+OpDecorate %in_var_COLOR Location 1
+OpDecorate %out_var_COLOR Location 0
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%uint = OpTypeInt 32 0
+%uint_32 = OpConstant %uint 32
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%void = OpTypeVoid
+%uint_256 = OpConstant %uint 256
+%uint_0 = OpConstant %uint 0
+%uint_128 = OpConstant %uint 128
+%36 = OpTypeFunction %void
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%VS_OUTPUT = OpTypeStruct %v4float %v4float
+%38 = OpTypeFunction %VS_OUTPUT %_ptr_Function_v4float %_ptr_Function_v4float
+%_ptr_Function_VS_OUTPUT = OpTypePointer Function %VS_OUTPUT
+OpLine %7 6 29
+%in_var_POSITION = OpVariable %_ptr_Input_v4float Input
+OpLine %7 7 31
+%in_var_COLOR = OpVariable %_ptr_Input_v4float Input
+OpLine %7 2 16
+%gl_Position = OpVariable %_ptr_Output_v4float Output
+OpLine %7 3 18
+%out_var_COLOR = OpVariable %_ptr_Output_v4float Output
+%40 = OpExtInst %void %1 DebugExpression
+%41 = OpExtInst %void %1 DebugSource %7 %8
+%42 = OpExtInst %void %1 DebugCompilationUnit 1 4 %41 HLSL
+%43 = OpExtInst %void %1 DebugTypeComposite %9 Structure %41 1 1 %42 %9 %uint_256 FlagIsProtected|FlagIsPrivate %44 %45
+%46 = OpExtInst %void %1 DebugTypeBasic %10 %uint_32 Float
+%47 = OpExtInst %void %1 DebugTypeVector %46 4
+%48 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %43 %47 %47
+%49 = OpExtInst %void %1 DebugFunction %11 %48 %41 6 1 %42 %11 FlagIsProtected|FlagIsPrivate 7 %src_main
+%50 = OpExtInst %void %1 DebugLocalVariable %12 %47 %41 6 23 %49 FlagIsLocal 0
+%51 = OpExtInst %void %1 DebugLocalVariable %13 %47 %41 7 23 %49 FlagIsLocal 1
+%52 = OpExtInst %void %1 DebugLexicalBlock %41 7 38 %49
+%53 = OpExtInst %void %1 DebugLocalVariable %14 %43 %41 8 13 %52 FlagIsLocal
+%44 = OpExtInst %void %1 DebugTypeMember %12 %47 %41 2 3 %43 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate
+%45 = OpExtInst %void %1 DebugTypeMember %13 %47 %41 3 3 %43 %uint_128 %uint_128 FlagIsProtected|FlagIsPrivate
+OpLine %7 6 1
+%main = OpFunction %void None %36
+%54 = OpLabel
+%74 = OpExtInst %void %1 DebugScope %42
+OpLine %7 6 23
+%param_var_pos = OpVariable %_ptr_Function_v4float Function
+OpLine %7 7 23
+%param_var_color = OpVariable %_ptr_Function_v4float Function
+OpLine %7 6 23
+%56 = OpLoad %v4float %in_var_POSITION
+OpStore %param_var_pos %56
+OpLine %7 7 23
+%57 = OpLoad %v4float %in_var_COLOR
+OpStore %param_var_color %57
+OpLine %7 6 1
+%58 = OpFunctionCall %VS_OUTPUT %src_main %param_var_pos %param_var_color
+OpLine %7 6 11
+%59 = OpCompositeExtract %v4float %58 0
+OpLine %7 2 16
+OpStore %gl_Position %59
+OpLine %7 6 11
+%60 = OpCompositeExtract %v4float %58 1
+OpLine %7 3 18
+OpStore %out_var_COLOR %60
+%75 = OpExtInst %void %1 DebugNoScope
+OpReturn
+OpFunctionEnd
+OpLine %7 6 1
+%src_main = OpFunction %VS_OUTPUT None %38
+%76 = OpExtInst %void %1 DebugScope %49
+OpLine %7 6 23
+%pos = OpFunctionParameter %_ptr_Function_v4float
+OpLine %7 7 23
+%color = OpFunctionParameter %_ptr_Function_v4float
+%63 = OpExtInst %void %1 DebugDeclare %50 %pos %40
+%64 = OpExtInst %void %1 DebugDeclare %51 %color %40
+%77 = OpExtInst %void %1 DebugNoScope
+%bb_entry = OpLabel
+%78 = OpExtInst %void %1 DebugScope %52
+OpLine %7 8 13
+%vout = OpVariable %_ptr_Function_VS_OUTPUT Function
+%67 = OpExtInst %void %1 DebugDeclare %53 %vout %40
+OpLine %7 9 14
+%68 = OpLoad %v4float %pos
+OpLine %7 9 3
+%69 = OpAccessChain %_ptr_Function_v4float %vout %int_0
+OpStore %69 %68
+OpLine %7 10 16
+%70 = OpLoad %v4float %color
+OpLine %7 10 3
+%71 = OpAccessChain %_ptr_Function_v4float %vout %int_1
+OpStore %71 %70
+OpLine %7 11 10
+%72 = OpLoad %VS_OUTPUT %vout
+OpLine %7 11 3
+%79 = OpExtInst %void %1 DebugNoScope
+OpReturnValue %72
+OpFunctionEnd
+)";
+
+ SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
+ std::unique_ptr<IRContext> context =
+ BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text);
+ ASSERT_NE(nullptr, context);
+
+ std::vector<uint32_t> binary;
+ context->module()->ToBinary(&binary, /* skip_nop = */ false);
+
+ std::string disassembled_text;
+ EXPECT_TRUE(t.Disassemble(binary, &disassembled_text));
+ EXPECT_EQ(text, disassembled_text);
}
TEST(IrBuilder, LocalGlobalVariables) {
diff --git a/third_party/SPIRV-Tools/test/opt/local_redundancy_elimination_test.cpp b/third_party/SPIRV-Tools/test/opt/local_redundancy_elimination_test.cpp
index bc4635e..291e1bc 100644
--- a/third_party/SPIRV-Tools/test/opt/local_redundancy_elimination_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/local_redundancy_elimination_test.cpp
@@ -154,6 +154,50 @@
SinglePassRunAndMatch<LocalRedundancyEliminationPass>(text, false);
}
+TEST_F(LocalRedundancyEliminationTest, StorageBufferIdentification) {
+ const std::string text = R"(
+; CHECK: [[gep:%\w+]] = OpAccessChain
+; CHECK: [[ld:%\w+]] = OpLoad {{%\w+}} [[gep]]
+; CHECK: [[add:%\w+]] = OpIAdd {{%\w+}} [[ld]]
+; CHECK: OpStore [[gep]] [[add]]
+; CHECK: [[ld:%\w+]] = OpLoad {{%\w+}} [[gep]]
+; CHECK: [[add:%\w+]] = OpIAdd {{%\w+}} [[ld]]
+; CHECK: OpStore [[gep]] [[add]]
+
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %block BufferBlock
+OpMemberDecorate %block 0 Offset 0
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%block = OpTypeStruct %int
+%array = OpTypeArray %block %int_1
+%ptr_ssbo_array = OpTypePointer Uniform %array
+%ptr_ssbo_int = OpTypePointer Uniform %int
+%var = OpVariable %ptr_ssbo_array Uniform
+%void_fn = OpTypeFunction %void
+%fn = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep1 = OpAccessChain %ptr_ssbo_int %var %int_0 %int_0
+%ld1 = OpLoad %int %gep1
+%add1 = OpIAdd %int %ld1 %int_1
+%gep2 = OpAccessChain %ptr_ssbo_int %var %int_0 %int_0
+OpStore %gep2 %add1
+%gep3 = OpAccessChain %ptr_ssbo_int %var %int_0 %int_0
+%ld3 = OpLoad %int %gep3
+%add3 = OpIAdd %int %ld3 %int_1
+%gep4 = OpAccessChain %ptr_ssbo_int %var %int_0 %int_0
+OpStore %gep4 %add3
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<LocalRedundancyEliminationPass>(text, true);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/opt/type_manager_test.cpp b/third_party/SPIRV-Tools/test/opt/type_manager_test.cpp
index 743d0b6..fdae2ef 100644
--- a/third_party/SPIRV-Tools/test/opt/type_manager_test.cpp
+++ b/third_party/SPIRV-Tools/test/opt/type_manager_test.cpp
@@ -1063,7 +1063,7 @@
; CHECK: OpTypeForwardPointer [[uniform_ptr]] Uniform
; CHECK: OpTypePipeStorage
; CHECK: OpTypeNamedBarrier
-; CHECK: OpTypeAccelerationStructureNV
+; CHECK: OpTypeAccelerationStructureKHR
; CHECK: OpTypeCooperativeMatrixNV [[f32]] [[uint24]] [[uint24]] [[uint24]]
OpCapability Shader
OpCapability Int64
diff --git a/third_party/SPIRV-Tools/test/val/val_barriers_test.cpp b/third_party/SPIRV-Tools/test/val/val_barriers_test.cpp
index ae166d9..fa2b153 100644
--- a/third_party/SPIRV-Tools/test/val/val_barriers_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_barriers_test.cpp
@@ -120,7 +120,7 @@
execution_model, memory_model);
}
-std::string GenerateWebGPUShaderCode(
+std::string GenerateWebGPUComputeShaderCode(
const std::string& body,
const std::string& capabilities_and_extensions = "",
const std::string& execution_model = "GLCompute") {
@@ -138,6 +138,24 @@
"", execution_model, memory_model);
}
+std::string GenerateWebGPUVertexShaderCode(
+ const std::string& body,
+ const std::string& capabilities_and_extensions = "",
+ const std::string& execution_model = "Vertex") {
+ const std::string vulkan_memory_capability = R"(
+OpCapability VulkanMemoryModelKHR
+)";
+ const std::string vulkan_memory_extension = R"(
+OpExtension "SPV_KHR_vulkan_memory_model"
+)";
+ const std::string memory_model = "OpMemoryModel Logical VulkanKHR";
+ return GenerateShaderCodeImpl(body,
+ vulkan_memory_capability +
+ capabilities_and_extensions +
+ vulkan_memory_extension,
+ "", execution_model, memory_model);
+}
+
std::string GenerateKernelCode(
const std::string& body,
const std::string& capabilities_and_extensions = "") {
@@ -257,7 +275,7 @@
OpControlBarrier %workgroup %workgroup %acquire_release_workgroup
)";
- CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
+ CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
}
@@ -266,7 +284,7 @@
OpControlBarrier %workgroup %workgroup %workgroup
)";
- CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
+ CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("For WebGPU, AcquireRelease must be set for Memory "
@@ -278,7 +296,7 @@
OpControlBarrier %workgroup %workgroup %acquire_release
)";
- CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
+ CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("For WebGPU, WorkgroupMemory must be set for Memory "
@@ -290,7 +308,7 @@
OpControlBarrier %workgroup %workgroup %acquire_release_uniform_workgroup
)";
- CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
+ CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
EXPECT_THAT(
getDiagnosticString(),
@@ -303,7 +321,7 @@
OpControlBarrier %workgroup %workgroup %release_uniform_workgroup
)";
- CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
+ CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("For WebGPU, AcquireRelease must be set for Memory "
@@ -421,7 +439,7 @@
OpControlBarrier %device %workgroup %none
)";
- CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
+ CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("ControlBarrier: in WebGPU environment Execution Scope "
@@ -433,13 +451,27 @@
OpControlBarrier %subgroup %workgroup %none
)";
- CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
+ CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("ControlBarrier: in WebGPU environment Execution Scope "
"is limited to Workgroup"));
}
+TEST_F(ValidateBarriers,
+ OpControlBarrierWebGPUExecutionScopeWorkgroupNonComputeBad) {
+ const std::string body = R"(
+OpControlBarrier %workgroup %workgroup %acquire_release_workgroup
+)";
+
+ CompileSuccessfully(GenerateWebGPUVertexShaderCode(body), SPV_ENV_WEBGPU_0);
+ ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "Workgroup Execution Scope is limited to GLCompute execution model"));
+}
+
TEST_F(ValidateBarriers, OpControlBarrierVulkanMemoryScopeSubgroup) {
const std::string body = R"(
OpControlBarrier %subgroup %subgroup %none
@@ -479,7 +511,7 @@
OpControlBarrier %workgroup %subgroup %acquire_release_workgroup
)";
- CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
+ CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("ControlBarrier: in WebGPU environment Memory Scope is "
@@ -710,7 +742,7 @@
OpMemoryBarrier %workgroup %image_memory
)";
- CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
+ CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
}
@@ -719,7 +751,7 @@
OpMemoryBarrier %subgroup %image_memory
)";
- CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
+ CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("in WebGPU environment Memory Scope is limited to "
@@ -731,7 +763,7 @@
OpMemoryBarrier %workgroup %acquire_release_uniform_workgroup
)";
- CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
+ CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("ImageMemory must be set for Memory Semantics of "
@@ -743,7 +775,7 @@
OpMemoryBarrier %workgroup %uniform_workgroup
)";
- CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
+ CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("ImageMemory must be set for Memory Semantics of "
@@ -755,7 +787,7 @@
OpMemoryBarrier %workgroup %acquire_uniform_workgroup
)";
- CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
+ CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("ImageMemory must be set for Memory Semantics of "
@@ -767,7 +799,7 @@
OpMemoryBarrier %workgroup %release_uniform_workgroup
)";
- CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
+ CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("ImageMemory must be set for Memory Semantics of "
@@ -779,13 +811,26 @@
OpMemoryBarrier %workgroup %uniform_image_memory
)";
- CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
+ CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("only ImageMemory may be set for Memory Semantics of "
"OpMemoryBarrier"));
}
+TEST_F(ValidateBarriers, OpMemoryBarrierWebGPUWorkgroupNonComputeFailure) {
+ const std::string body = R"(
+OpMemoryBarrier %workgroup %image_memory
+)";
+
+ CompileSuccessfully(GenerateWebGPUVertexShaderCode(body), SPV_ENV_WEBGPU_0);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "Workgroup Memory Scope is limited to GLCompute execution model"));
+}
+
TEST_F(ValidateBarriers, OpMemoryBarrierFloatMemoryScope) {
const std::string body = R"(
OpMemoryBarrier %f32_1 %acquire_release_uniform_workgroup
diff --git a/third_party/SPIRV-Tools/test/val/val_cfg_test.cpp b/third_party/SPIRV-Tools/test/val/val_cfg_test.cpp
index 4cf4029..0d09642 100644
--- a/third_party/SPIRV-Tools/test/val/val_cfg_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_cfg_test.cpp
@@ -4296,6 +4296,213 @@
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
+TEST_F(ValidateCFG, PhiResultInvalidSampler) {
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%bool = OpTypeBool
+%f32 = OpTypeFloat 32
+%sampler = OpTypeSampler
+%ptr_uc_sampler = OpTypePointer UniformConstant %sampler
+%sampler_var = OpVariable %ptr_uc_sampler UniformConstant
+%undef_bool = OpUndef %bool
+%undef_sampler = OpUndef %sampler
+%void_fn = OpTypeFunction %void
+%fn = OpFunction %void None %void_fn
+%entry = OpLabel
+%ld_sampler = OpLoad %sampler %sampler_var
+OpBranch %loop
+%loop = OpLabel
+%phi = OpPhi %sampler %undef_sampler %entry %ld_sampler %loop
+OpLoopMerge %exit %loop None
+OpBranchConditional %undef_bool %exit %loop
+%exit = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text);
+ ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Result type cannot be OpTypeSampler"));
+}
+
+TEST_F(ValidateCFG, PhiResultInvalidImage) {
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%bool = OpTypeBool
+%f32 = OpTypeFloat 32
+%image = OpTypeImage %f32 2D 0 0 0 1 Rgba32f
+%ptr_uc_image = OpTypePointer UniformConstant %image
+%image_var = OpVariable %ptr_uc_image UniformConstant
+%undef_bool = OpUndef %bool
+%undef_image = OpUndef %image
+%void_fn = OpTypeFunction %void
+%fn = OpFunction %void None %void_fn
+%entry = OpLabel
+%ld_image = OpLoad %image %image_var
+OpBranch %loop
+%loop = OpLabel
+%phi = OpPhi %image %undef_image %entry %ld_image %loop
+OpLoopMerge %exit %loop None
+OpBranchConditional %undef_bool %exit %loop
+%exit = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text);
+ ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Result type cannot be OpTypeImage"));
+}
+
+TEST_F(ValidateCFG, PhiResultInvalidSampledImage) {
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%bool = OpTypeBool
+%f32 = OpTypeFloat 32
+%sampler = OpTypeSampler
+%ptr_uc_sampler = OpTypePointer UniformConstant %sampler
+%sampler_var = OpVariable %ptr_uc_sampler UniformConstant
+%image = OpTypeImage %f32 2D 0 0 0 1 Rgba32f
+%ptr_uc_image = OpTypePointer UniformConstant %image
+%image_var = OpVariable %ptr_uc_image UniformConstant
+%sampled_image = OpTypeSampledImage %image
+%undef_bool = OpUndef %bool
+%undef_sampled_image = OpUndef %sampled_image
+%void_fn = OpTypeFunction %void
+%fn = OpFunction %void None %void_fn
+%entry = OpLabel
+%ld_image = OpLoad %image %image_var
+%ld_sampler = OpLoad %sampler %sampler_var
+OpBranch %loop
+%loop = OpLabel
+%phi = OpPhi %sampled_image %undef_sampled_image %entry %sample %loop
+%sample = OpSampledImage %sampled_image %ld_image %ld_sampler
+OpLoopMerge %exit %loop None
+OpBranchConditional %undef_bool %exit %loop
+%exit = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ CompileSuccessfully(text);
+ ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Result type cannot be OpTypeSampledImage"));
+}
+
+TEST_F(ValidateCFG, PhiResultValidPreLegalizationSampler) {
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%bool = OpTypeBool
+%f32 = OpTypeFloat 32
+%sampler = OpTypeSampler
+%ptr_uc_sampler = OpTypePointer UniformConstant %sampler
+%sampler_var = OpVariable %ptr_uc_sampler UniformConstant
+%undef_bool = OpUndef %bool
+%undef_sampler = OpUndef %sampler
+%void_fn = OpTypeFunction %void
+%fn = OpFunction %void None %void_fn
+%entry = OpLabel
+%ld_sampler = OpLoad %sampler %sampler_var
+OpBranch %loop
+%loop = OpLabel
+%phi = OpPhi %sampler %undef_sampler %entry %ld_sampler %loop
+OpLoopMerge %exit %loop None
+OpBranchConditional %undef_bool %exit %loop
+%exit = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ options_->before_hlsl_legalization = true;
+ CompileSuccessfully(text);
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateCFG, PhiResultValidPreLegalizationImage) {
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%bool = OpTypeBool
+%f32 = OpTypeFloat 32
+%image = OpTypeImage %f32 2D 0 0 0 1 Rgba32f
+%ptr_uc_image = OpTypePointer UniformConstant %image
+%image_var = OpVariable %ptr_uc_image UniformConstant
+%undef_bool = OpUndef %bool
+%undef_image = OpUndef %image
+%void_fn = OpTypeFunction %void
+%fn = OpFunction %void None %void_fn
+%entry = OpLabel
+%ld_image = OpLoad %image %image_var
+OpBranch %loop
+%loop = OpLabel
+%phi = OpPhi %image %undef_image %entry %ld_image %loop
+OpLoopMerge %exit %loop None
+OpBranchConditional %undef_bool %exit %loop
+%exit = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ options_->before_hlsl_legalization = true;
+ CompileSuccessfully(text);
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateCFG, PhiResultValidPreLegalizationSampledImage) {
+ const std::string text = R"(
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+%void = OpTypeVoid
+%bool = OpTypeBool
+%f32 = OpTypeFloat 32
+%sampler = OpTypeSampler
+%ptr_uc_sampler = OpTypePointer UniformConstant %sampler
+%sampler_var = OpVariable %ptr_uc_sampler UniformConstant
+%image = OpTypeImage %f32 2D 0 0 0 1 Rgba32f
+%ptr_uc_image = OpTypePointer UniformConstant %image
+%image_var = OpVariable %ptr_uc_image UniformConstant
+%sampled_image = OpTypeSampledImage %image
+%undef_bool = OpUndef %bool
+%undef_sampled_image = OpUndef %sampled_image
+%void_fn = OpTypeFunction %void
+%fn = OpFunction %void None %void_fn
+%entry = OpLabel
+%ld_image = OpLoad %image %image_var
+%ld_sampler = OpLoad %sampler %sampler_var
+OpBranch %loop
+%loop = OpLabel
+%phi = OpPhi %sampled_image %undef_sampled_image %entry %sample %loop
+%sample = OpSampledImage %sampled_image %ld_image %ld_sampler
+OpLoopMerge %exit %loop None
+OpBranchConditional %undef_bool %exit %loop
+%exit = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+ options_->before_hlsl_legalization = true;
+ CompileSuccessfully(text);
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
} // namespace
} // namespace val
} // namespace spvtools
diff --git a/third_party/SPIRV-Tools/test/val/val_ext_inst_test.cpp b/third_party/SPIRV-Tools/test/val/val_ext_inst_test.cpp
index 01df796..aa73989 100644
--- a/third_party/SPIRV-Tools/test/val/val_ext_inst_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_ext_inst_test.cpp
@@ -36,6 +36,26 @@
using ValidateOldDebugInfo = spvtest::ValidateBase<std::string>;
using ValidateOpenCL100DebugInfo = spvtest::ValidateBase<std::string>;
using ValidateLocalDebugInfoOutOfFunction = spvtest::ValidateBase<std::string>;
+using ValidateOpenCL100DebugInfoDebugTypedef =
+ spvtest::ValidateBase<std::pair<std::string, std::string>>;
+using ValidateOpenCL100DebugInfoDebugTypeEnum =
+ spvtest::ValidateBase<std::pair<std::string, std::string>>;
+using ValidateOpenCL100DebugInfoDebugTypeComposite =
+ spvtest::ValidateBase<std::pair<std::string, std::string>>;
+using ValidateOpenCL100DebugInfoDebugTypeMember =
+ spvtest::ValidateBase<std::pair<std::string, std::string>>;
+using ValidateOpenCL100DebugInfoDebugTypeInheritance =
+ spvtest::ValidateBase<std::pair<std::string, std::string>>;
+using ValidateOpenCL100DebugInfoDebugFunction =
+ spvtest::ValidateBase<std::pair<std::string, std::string>>;
+using ValidateOpenCL100DebugInfoDebugFunctionDeclaration =
+ spvtest::ValidateBase<std::pair<std::string, std::string>>;
+using ValidateOpenCL100DebugInfoDebugLexicalBlock =
+ spvtest::ValidateBase<std::pair<std::string, std::string>>;
+using ValidateOpenCL100DebugInfoDebugLocalVariable =
+ spvtest::ValidateBase<std::pair<std::string, std::string>>;
+using ValidateOpenCL100DebugInfoDebugDeclare =
+ spvtest::ValidateBase<std::pair<std::string, std::string>>;
using ValidateGlslStd450SqrtLike = spvtest::ValidateBase<std::string>;
using ValidateGlslStd450FMinLike = spvtest::ValidateBase<std::string>;
using ValidateGlslStd450FClampLike = spvtest::ValidateBase<std::string>;
@@ -798,6 +818,37 @@
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
}
+TEST_F(ValidateOpenCL100DebugInfo, DebugFunctionMissingOpFunction) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() {}"
+%void_name = OpString "void"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v_main"
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbgNone = OpExtInst %void %DbgExt DebugInfoNone
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%void_info = OpExtInst %void %DbgExt DebugTypeBasic %void_name %u32_0 Unspecified
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void_info %void_info
+%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %dbgNone
+)";
+
+ const std::string body = R"(
+%main_scope = OpExtInst %void %DbgExt DebugScope %main_info
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, "", dbg_inst_header, body, extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
TEST_F(ValidateOpenCL100DebugInfo, DebugScopeBeforeOpVariableInFunction) {
const std::string src = R"(
%src = OpString "simple.hlsl"
@@ -919,6 +970,1097 @@
HasSubstr("forward referenced IDs have not been defined"));
}
+TEST_F(ValidateOpenCL100DebugInfo, DebugInstructionWrongResultType) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+)";
+
+ const std::string dbg_inst = R"(
+%dbg_src = OpExtInst %bool %DbgExt DebugSource %src %code
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "",
+ extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected result type must be a result id of "
+ "OpTypeVoid"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugCompilationUnit) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+)";
+
+ const std::string dbg_inst = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "",
+ extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugCompilationUnitFail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+)";
+
+ const std::string dbg_inst = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %src HLSL
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "",
+ extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand Source must be a result id of "
+ "DebugSource"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugSourceFailFile) {
+ const std::string src = R"(
+%code = OpString "main() {}"
+)";
+
+ const std::string dbg_inst = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %DbgExt %code
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "",
+ extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand File must be a result id of "
+ "OpString"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugSourceFailSource) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+)";
+
+ const std::string dbg_inst = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %DbgExt
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "",
+ extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand Text must be a result id of "
+ "OpString"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugSourceNoText) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+)";
+
+ const std::string dbg_inst = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "",
+ extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeBasicFailName) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "float4 main(float arg) {
+ float foo;
+ return float4(0, 0, 0, 0);
+}
+"
+%float_name = OpString "float"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %int_32 %int_32 Float
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand Name must be a result id of "
+ "OpString"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeBasicFailSize) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "float4 main(float arg) {
+ float foo;
+ return float4(0, 0, 0, 0);
+}
+"
+%float_name = OpString "float"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %float_name Float
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand Size must be a result id of "
+ "OpConstant"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypePointer) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "float4 main(float arg) {
+ float foo;
+ return float4(0, 0, 0, 0);
+}
+"
+%float_name = OpString "float"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%pfloat_info = OpExtInst %void %DbgExt DebugTypePointer %float_info Function FlagIsLocal
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypePointerFail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "float4 main(float arg) {
+ float foo;
+ return float4(0, 0, 0, 0);
+}
+"
+%float_name = OpString "float"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%pfloat_info = OpExtInst %void %DbgExt DebugTypePointer %dbg_src Function FlagIsLocal
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand Base Type must be a result id of "
+ "DebugTypeBasic"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeQualifier) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "float4 main(float arg) {
+ float foo;
+ return float4(0, 0, 0, 0);
+}
+"
+%float_name = OpString "float"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%cfloat_info = OpExtInst %void %DbgExt DebugTypeQualifier %float_info ConstType
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeQualifierFail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "float4 main(float arg) {
+ float foo;
+ return float4(0, 0, 0, 0);
+}
+"
+%float_name = OpString "float"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%cfloat_info = OpExtInst %void %DbgExt DebugTypeQualifier %comp_unit ConstType
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand Base Type must be a result id of "
+ "DebugTypeBasic"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArray) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %int_32
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailBaseType) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %comp_unit %int_32
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand Base Type is not a valid debug "
+ "type"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailComponentCount) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %float_info
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand Component Count must be a result id "
+ "of OpConstant"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailComponentCountFloat) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %f32_4
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Component Count must be positive integer"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailComponentCountZero) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %u32_0
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Component Count must be positive integer"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeVector) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeVectorFail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %dbg_src 4
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand Base Type must be a result id of "
+ "DebugTypeBasic"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeVectorFailComponentZero) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %dbg_src 0
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand Base Type must be a result id of "
+ "DebugTypeBasic"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeVectorFailComponentFive) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %dbg_src 5
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand Base Type must be a result id of "
+ "DebugTypeBasic"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypedef) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%foo_info = OpExtInst %void %DbgExt DebugTypedef %foo_name %float_info %dbg_src 1 1 %comp_unit
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateOpenCL100DebugInfoDebugTypedef, Fail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const auto& param = GetParam();
+
+ std::ostringstream ss;
+ ss << R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%foo_info = OpExtInst %void %DbgExt DebugTypedef )";
+ ss << param.first;
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(),
+ "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand " + param.second +
+ " must be a result id of "));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypedef,
+ ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
+ std::make_pair(R"(%dbg_src %float_info %dbg_src 1 1 %comp_unit)",
+ "Name"),
+ std::make_pair(R"(%foo_name %dbg_src %dbg_src 1 1 %comp_unit)",
+ "Base Type"),
+ std::make_pair(R"(%foo_name %float_info %comp_unit 1 1 %comp_unit)",
+ "Source"),
+ std::make_pair(R"(%foo_name %float_info %dbg_src 1 1 %dbg_src)",
+ "Parent"),
+ }));
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeFunction) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v_main"
+%float_name = OpString "float"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%main_type_info1 = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void
+%main_type_info2 = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %float_info
+%main_type_info3 = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %float_info %float_info
+%main_type_info4 = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void %float_info %float_info
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeFunctionFailReturn) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v_main"
+%float_name = OpString "float"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %dbg_src %float_info
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("expected operand Return Type is not a valid debug type"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeFunctionFailParam) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v_main"
+%float_name = OpString "float"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %float_info %void
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("expected operand Parameter Types is not a valid debug type"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeEnum) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%none = OpExtInst %void %DbgExt DebugInfoNone
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%foo_info1 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %float_info %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name %u32_1 %foo_name
+%foo_info2 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %none %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name %u32_1 %foo_name
+%foo_info3 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %none %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateOpenCL100DebugInfoDebugTypeEnum, Fail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const auto& param = GetParam();
+
+ std::ostringstream ss;
+ ss << R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%foo_info = OpExtInst %void %DbgExt DebugTypeEnum )";
+ ss << param.first;
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(),
+ "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand " + param.second));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypeEnum,
+ ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
+ std::make_pair(
+ R"(%dbg_src %float_info %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name)",
+ "Name"),
+ std::make_pair(
+ R"(%foo_name %dbg_src %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name)",
+ "Underlying Types"),
+ std::make_pair(
+ R"(%foo_name %float_info %comp_unit 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name)",
+ "Source"),
+ std::make_pair(
+ R"(%foo_name %float_info %dbg_src 1 1 %dbg_src %int_32 FlagIsPublic %u32_0 %foo_name)",
+ "Parent"),
+ std::make_pair(
+ R"(%foo_name %float_info %dbg_src 1 1 %comp_unit %void FlagIsPublic %u32_0 %foo_name)",
+ "Size"),
+ std::make_pair(
+ R"(%foo_name %float_info %dbg_src 1 1 %comp_unit %u32_0 FlagIsPublic %u32_0 %foo_name)",
+ "Size"),
+ std::make_pair(
+ R"(%foo_name %float_info %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %foo_name %foo_name)",
+ "Value"),
+ std::make_pair(
+ R"(%foo_name %float_info %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %u32_1)",
+ "Name"),
+ }));
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeCompositeFunctionAndInheritance) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "struct VS_OUTPUT {
+ float4 pos : SV_POSITION;
+};
+struct foo : VS_OUTPUT {
+};
+main() {}
+"
+%VS_OUTPUT_name = OpString "struct VS_OUTPUT"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION"
+%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v4f_main_f"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+%int_128 = OpConstant %u32 128
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4
+%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_128 FlagIsPublic
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %v4float_info %float_info
+%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main
+%foo_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Structure %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic
+%child = OpExtInst %void %DbgExt DebugTypeInheritance %foo_info %VS_OUTPUT_info %int_128 %int_128 FlagIsPublic
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateOpenCL100DebugInfoDebugTypeComposite, Fail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "struct VS_OUTPUT {
+ float4 pos : SV_POSITION;
+};
+struct foo : VS_OUTPUT {
+};
+main() {}
+"
+%VS_OUTPUT_name = OpString "struct VS_OUTPUT"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION"
+%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v4f_main_f"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+%int_128 = OpConstant %u32 128
+)";
+
+ const auto& param = GetParam();
+
+ std::ostringstream ss;
+ ss << R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite )";
+ ss << param.first;
+ ss << R"(
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4
+%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_128 FlagIsPublic
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %v4float_info %float_info
+%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main
+%foo_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Structure %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic
+%child = OpExtInst %void %DbgExt DebugTypeInheritance %foo_info %VS_OUTPUT_info %int_128 %int_128 FlagIsPublic
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(),
+ "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand " + param.second + " must be "));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypeComposite,
+ ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
+ std::make_pair(
+ R"(%dbg_src Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)",
+ "Name"),
+ std::make_pair(
+ R"(%VS_OUTPUT_name Structure %comp_unit 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)",
+ "Source"),
+ std::make_pair(
+ R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %dbg_src %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)",
+ "Parent"),
+ std::make_pair(
+ R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %int_128 %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)",
+ "Linkage Name"),
+ std::make_pair(
+ R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %dbg_src FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)",
+ "Size"),
+ std::make_pair(
+ R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %dbg_src %main_info %child)",
+ "Members"),
+ std::make_pair(
+ R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %dbg_src %child)",
+ "Members"),
+ std::make_pair(
+ R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %dbg_src)",
+ "Members"),
+ }));
+
+TEST_P(ValidateOpenCL100DebugInfoDebugTypeMember, Fail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "struct VS_OUTPUT {
+ float pos : SV_POSITION;
+};
+main() {}
+"
+%VS_OUTPUT_name = OpString "struct VS_OUTPUT"
+%float_name = OpString "float"
+%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION"
+%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const auto& param = GetParam();
+
+ std::ostringstream ss;
+ ss << R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_32 FlagIsPublic %VS_OUTPUT_pos_info
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember )";
+ ss << param.first;
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(),
+ "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ if (!param.second.empty()) {
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand " + param.second +
+ " must be a result id of "));
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypeMember,
+ ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
+ std::make_pair(
+ R"(%dbg_src %float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_32 FlagIsPublic)",
+ "Name"),
+ std::make_pair(
+ R"(%VS_OUTPUT_pos_name %dbg_src %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_32 FlagIsPublic)",
+ ""),
+ std::make_pair(
+ R"(%VS_OUTPUT_pos_name %float_info %float_info 2 3 %VS_OUTPUT_info %u32_0 %int_32 FlagIsPublic)",
+ "Source"),
+ std::make_pair(
+ R"(%VS_OUTPUT_pos_name %float_info %dbg_src 2 3 %float_info %u32_0 %int_32 FlagIsPublic)",
+ "Parent"),
+ std::make_pair(
+ R"(%VS_OUTPUT_pos_name %float_info %dbg_src 2 3 %VS_OUTPUT_info %void %int_32 FlagIsPublic)",
+ "Offset"),
+ std::make_pair(
+ R"(%VS_OUTPUT_pos_name %float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %void FlagIsPublic)",
+ "Size"),
+ }));
+
+TEST_P(ValidateOpenCL100DebugInfoDebugTypeInheritance, Fail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "struct VS_OUTPUT {};
+struct foo : VS_OUTPUT {};
+"
+%VS_OUTPUT_name = OpString "struct VS_OUTPUT"
+%foo_name = OpString "foo"
+)";
+
+ const auto& param = GetParam();
+
+ std::ostringstream ss;
+ ss << R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_name %u32_0 FlagIsPublic %child
+%foo_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Structure %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic
+%bar_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Union %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic
+%child = OpExtInst %void %DbgExt DebugTypeInheritance )"
+ << param.first;
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", ss.str(), "",
+ extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand " + param.second));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypeInheritance,
+ ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
+ std::make_pair(R"(%dbg_src %VS_OUTPUT_info %u32_0 %u32_0 FlagIsPublic)",
+ "Child must be a result id of"),
+ std::make_pair(R"(%foo_info %dbg_src %u32_0 %u32_0 FlagIsPublic)",
+ "Parent must be a result id of"),
+ std::make_pair(
+ R"(%bar_info %VS_OUTPUT_info %u32_0 %u32_0 FlagIsPublic)",
+ "Child must be class or struct debug type"),
+ std::make_pair(R"(%foo_info %bar_info %u32_0 %u32_0 FlagIsPublic)",
+ "Parent must be class or struct debug type"),
+ std::make_pair(R"(%foo_info %VS_OUTPUT_info %void %u32_0 FlagIsPublic)",
+ "Offset"),
+ std::make_pair(R"(%foo_info %VS_OUTPUT_info %u32_0 %void FlagIsPublic)",
+ "Size"),
+ }));
TEST_P(ValidateGlslStd450SqrtLike, Success) {
const std::string ext_inst_name = GetParam();
std::ostringstream ss;
@@ -930,6 +2072,511 @@
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
}
+TEST_F(ValidateOpenCL100DebugInfo, DebugFunctionDeclaration) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "struct VS_OUTPUT {
+ float4 pos : SV_POSITION;
+};
+main() {}
+"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v4f_main_f"
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void
+%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic
+%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst_header,
+ "", extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateOpenCL100DebugInfoDebugFunction, Fail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "struct VS_OUTPUT {
+ float4 pos : SV_POSITION;
+};
+main() {}
+"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v4f_main_f"
+)";
+
+ const auto& param = GetParam();
+
+ std::ostringstream ss;
+ ss << R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void
+%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic
+%main_info = OpExtInst %void %DbgExt DebugFunction )"
+ << param.first;
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", ss.str(), "",
+ extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand " + param.second));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugFunction,
+ ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
+ std::make_pair(
+ R"(%u32_0 %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main)",
+ "Name"),
+ std::make_pair(
+ R"(%main_name %dbg_src %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main)",
+ "Type"),
+ std::make_pair(
+ R"(%main_name %main_type_info %comp_unit 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main)",
+ "Source"),
+ std::make_pair(
+ R"(%main_name %main_type_info %dbg_src 12 1 %dbg_src %main_linkage_name FlagIsPublic 13 %main)",
+ "Parent"),
+ std::make_pair(
+ R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %void FlagIsPublic 13 %main)",
+ "Linkage Name"),
+ std::make_pair(
+ R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %void)",
+ "Function"),
+ std::make_pair(
+ R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main %dbg_src)",
+ "Declaration"),
+ }));
+
+TEST_P(ValidateOpenCL100DebugInfoDebugFunctionDeclaration, Fail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "struct VS_OUTPUT {
+ float4 pos : SV_POSITION;
+};
+main() {}
+"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v4f_main_f"
+)";
+
+ const auto& param = GetParam();
+
+ std::ostringstream ss;
+ ss << R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void
+%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration )"
+ << param.first;
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", ss.str(), "",
+ extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand " + param.second));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AllOpenCL100DebugInfoFail,
+ ValidateOpenCL100DebugInfoDebugFunctionDeclaration,
+ ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
+ std::make_pair(
+ R"(%u32_0 %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic)",
+ "Name"),
+ std::make_pair(
+ R"(%main_name %dbg_src %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic)",
+ "Type"),
+ std::make_pair(
+ R"(%main_name %main_type_info %comp_unit 12 1 %comp_unit %main_linkage_name FlagIsPublic)",
+ "Source"),
+ std::make_pair(
+ R"(%main_name %main_type_info %dbg_src 12 1 %dbg_src %main_linkage_name FlagIsPublic)",
+ "Parent"),
+ std::make_pair(
+ R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %void FlagIsPublic)",
+ "Linkage Name"),
+ }));
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugLexicalBlock) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%main_name = OpString "main"
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%main_block = OpExtInst %void %DbgExt DebugLexicalBlock %dbg_src 1 1 %comp_unit %main_name)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst_header,
+ "", extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateOpenCL100DebugInfoDebugLexicalBlock, Fail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+%main_name = OpString "main"
+)";
+
+ const auto& param = GetParam();
+
+ std::ostringstream ss;
+ ss << R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%main_block = OpExtInst %void %DbgExt DebugLexicalBlock )"
+ << param.first;
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", ss.str(), "",
+ extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand " + param.second));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugLexicalBlock,
+ ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
+ std::make_pair(R"(%comp_unit 1 1 %comp_unit %main_name)", "Source"),
+ std::make_pair(R"(%dbg_src 1 1 %dbg_src %main_name)", "Parent"),
+ std::make_pair(R"(%dbg_src 1 1 %comp_unit %void)", "Name"),
+ }));
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugScopeFailScope) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() {}"
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+)";
+
+ const std::string body = R"(
+%main_scope = OpExtInst %void %DbgExt DebugScope %dbg_src
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, "", dbg_inst_header, body, extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Scope"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugScopeFailInlinedAt) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() {}"
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+)";
+
+ const std::string body = R"(
+%main_scope = OpExtInst %void %DbgExt DebugScope %comp_unit %dbg_src
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, "", dbg_inst_header, body, extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Inlined At"));
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugLocalVariable) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() { float foo; }"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%foo = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, "", extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateOpenCL100DebugInfoDebugLocalVariable, Fail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() { float foo; }"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const auto& param = GetParam();
+
+ std::ostringstream ss;
+ ss << R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%foo = OpExtInst %void %DbgExt DebugLocalVariable )"
+ << param.first;
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(),
+ "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand " + param.second));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugLocalVariable,
+ ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
+ std::make_pair(
+ R"(%void %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0)",
+ "Name"),
+ std::make_pair(
+ R"(%foo_name %dbg_src %dbg_src 1 10 %comp_unit FlagIsLocal 0)",
+ "Type"),
+ std::make_pair(
+ R"(%foo_name %float_info %comp_unit 1 10 %comp_unit FlagIsLocal 0)",
+ "Source"),
+ std::make_pair(
+ R"(%foo_name %float_info %dbg_src 1 10 %dbg_src FlagIsLocal 0)",
+ "Parent"),
+ }));
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugDeclare) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() { float foo; }"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%null_expr = OpExtInst %void %DbgExt DebugExpression
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0
+)";
+
+ const std::string body = R"(
+%foo = OpVariable %f32_ptr_function Function
+%decl = OpExtInst %void %DbgExt DebugDeclare %foo_info %foo %null_expr
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, body, extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugDeclareParam) {
+ CompileSuccessfully(R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "OpenCL.DebugInfo.100"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %main "main" %in_var_COLOR
+ %4 = OpString "test.hlsl"
+ OpSource HLSL 620 %4 "#line 1 \"test.hlsl\"
+void main(float foo:COLOR) {}
+"
+ %11 = OpString "#line 1 \"test.hlsl\"
+void main(float foo:COLOR) {}
+"
+ %14 = OpString "float"
+ %17 = OpString "src.main"
+ %20 = OpString "foo"
+ OpName %in_var_COLOR "in.var.COLOR"
+ OpName %main "main"
+ OpName %param_var_foo "param.var.foo"
+ OpName %src_main "src.main"
+ OpName %foo "foo"
+ OpName %bb_entry "bb.entry"
+ OpDecorate %in_var_COLOR Location 0
+ %uint = OpTypeInt 32 0
+ %uint_32 = OpConstant %uint 32
+ %float = OpTypeFloat 32
+%_ptr_Input_float = OpTypePointer Input %float
+ %void = OpTypeVoid
+ %23 = OpTypeFunction %void
+%_ptr_Function_float = OpTypePointer Function %float
+ %29 = OpTypeFunction %void %_ptr_Function_float
+ OpLine %4 1 21
+%in_var_COLOR = OpVariable %_ptr_Input_float Input
+ %10 = OpExtInst %void %1 DebugExpression
+ %12 = OpExtInst %void %1 DebugSource %4 %11
+ %13 = OpExtInst %void %1 DebugCompilationUnit 1 4 %12 HLSL
+ %15 = OpExtInst %void %1 DebugTypeBasic %14 %uint_32 Float
+ %16 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %void %15
+ %18 = OpExtInst %void %1 DebugFunction %17 %16 %12 1 1 %13 %17 FlagIsProtected|FlagIsPrivate 1 %src_main
+ %21 = OpExtInst %void %1 DebugLocalVariable %20 %15 %12 1 17 %18 FlagIsLocal 0
+ %22 = OpExtInst %void %1 DebugLexicalBlock %12 1 28 %18
+ OpLine %4 1 1
+ %main = OpFunction %void None %23
+ %24 = OpLabel
+ OpLine %4 1 17
+%param_var_foo = OpVariable %_ptr_Function_float Function
+ %27 = OpLoad %float %in_var_COLOR
+ OpLine %4 1 1
+ %28 = OpFunctionCall %void %src_main %param_var_foo
+ OpReturn
+ OpFunctionEnd
+ %src_main = OpFunction %void None %29
+ OpLine %4 1 17
+ %foo = OpFunctionParameter %_ptr_Function_float
+ %31 = OpExtInst %void %1 DebugDeclare %21 %foo %10
+ %bb_entry = OpLabel
+ OpLine %4 1 29
+ OpReturn
+ OpFunctionEnd
+)");
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateOpenCL100DebugInfoDebugDeclare, Fail) {
+ const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() { float foo; }"
+%float_name = OpString "float"
+%foo_name = OpString "foo"
+)";
+
+ const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+ const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%null_expr = OpExtInst %void %DbgExt DebugExpression
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0
+)";
+
+ const auto& param = GetParam();
+
+ std::ostringstream ss;
+ ss << R"(
+%foo = OpVariable %f32_ptr_function Function
+%decl = OpExtInst %void %DbgExt DebugDeclare )"
+ << param.first;
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+ src, size_const, dbg_inst_header, ss.str(), extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("expected operand " + param.second));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugDeclare,
+ ::testing::ValuesIn(std::vector<std::pair<std::string, std::string>>{
+ std::make_pair(R"(%dbg_src %foo %null_expr)", "Local Variable"),
+ std::make_pair(R"(%foo_info %void %null_expr)", "Variable"),
+ std::make_pair(R"(%foo_info %foo %dbg_src)", "Expression"),
+ }));
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugExpression) {
+ const std::string dbg_inst_header = R"(
+%op0 = OpExtInst %void %DbgExt DebugOperation Deref
+%op1 = OpExtInst %void %DbgExt DebugOperation Plus
+%null_expr = OpExtInst %void %DbgExt DebugExpression %op0 %op1
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo("", "", dbg_inst_header,
+ "", extension, "Vertex"));
+ ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugExpressionFail) {
+ const std::string dbg_inst_header = R"(
+%op = OpExtInst %void %DbgExt DebugOperation Deref
+%null_expr = OpExtInst %void %DbgExt DebugExpression %op %void
+)";
+
+ const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+ CompileSuccessfully(GenerateShaderCodeForDebugInfo("", "", dbg_inst_header,
+ "", extension, "Vertex"));
+ ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr(
+ "expected operand Operation must be a result id of DebugOperation"));
+}
+
TEST_P(ValidateGlslStd450SqrtLike, IntResultType) {
const std::string ext_inst_name = GetParam();
const std::string body =
diff --git a/third_party/SPIRV-Tools/test/val/val_id_test.cpp b/third_party/SPIRV-Tools/test/val/val_id_test.cpp
index 019d91a..adea563 100644
--- a/third_party/SPIRV-Tools/test/val/val_id_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_id_test.cpp
@@ -4355,6 +4355,17 @@
"'23' as an operand of <id> '24'."));
}
+TEST_F(ValidateIdWithMessage, OpCopyObjectSampledImageGood) {
+ std::string spirv = kGLSL450MemoryModel + sampledImageSetup + R"(
+%smpld_img = OpSampledImage %sampled_image_type %image_inst %sampler_inst
+%smpld_img2 = OpCopyObject %sampled_image_type %smpld_img
+%image_inst2 = OpCopyObject %image_type %image_inst
+OpReturn
+OpFunctionEnd)";
+ CompileSuccessfully(spirv.c_str());
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
// Valid: Get a float in a matrix using CompositeExtract.
// Valid: Insert float into a matrix using CompositeInsert.
TEST_F(ValidateIdWithMessage, CompositeExtractInsertGood) {
diff --git a/third_party/SPIRV-Tools/test/val/val_memory_test.cpp b/third_party/SPIRV-Tools/test/val/val_memory_test.cpp
index 22761cc..b32867b 100644
--- a/third_party/SPIRV-Tools/test/val/val_memory_test.cpp
+++ b/third_party/SPIRV-Tools/test/val/val_memory_test.cpp
@@ -57,8 +57,9 @@
"Variables identified with the UniformConstant storage class "
"are used only as handles to refer to opaque resources. Such "
"variables must be typed as OpTypeImage, OpTypeSampler, "
- "OpTypeSampledImage, OpTypeAccelerationStructureNV, or an "
- "array of one of these types."));
+ "OpTypeSampledImage, OpTypeAccelerationStructureNV, "
+ "OpTypeAccelerationStructureKHR, OpTypeRayQueryProvisionalKHR, "
+ "or an array of one of these types."));
}
TEST_F(ValidateMemory, VulkanUniformConstantOnOpaqueResourceGood) {
@@ -110,8 +111,9 @@
"Variables identified with the UniformConstant storage class "
"are used only as handles to refer to opaque resources. Such "
"variables must be typed as OpTypeImage, OpTypeSampler, "
- "OpTypeSampledImage, OpTypeAccelerationStructureNV, or an "
- "array of one of these types."));
+ "OpTypeSampledImage, OpTypeAccelerationStructureNV, "
+ "OpTypeAccelerationStructureKHR, OpTypeRayQueryProvisionalKHR, "
+ "or an array of one of these types."));
}
TEST_F(ValidateMemory, VulkanUniformConstantOnOpaqueResourceArrayGood) {
diff --git a/third_party/SPIRV-Tools/utils/check_copyright.py b/third_party/SPIRV-Tools/utils/check_copyright.py
index 2d288a1..4467a32 100755
--- a/third_party/SPIRV-Tools/utils/check_copyright.py
+++ b/third_party/SPIRV-Tools/utils/check_copyright.py
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+# coding=utf-8
# Copyright (c) 2016 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -31,10 +32,13 @@
'Google Inc.',
'Google LLC',
'Pierre Moreau',
- 'Samsung Inc']
+ 'Samsung Inc',
+ 'André Perez Maselco',
+ 'Vasyl Teliman',
+ 'Advanced Micro Devices, Inc.']
CURRENT_YEAR='2020'
-YEARS = '(2014-2016|2015-2016|2016|2016-2017|2017|2017-2019|2018|2019|2020)'
+YEARS = '(2014-2016|2015-2016|2015-2020|2016|2016-2017|2017|2017-2019|2018|2019|2020)'
COPYRIGHT_RE = re.compile(
'Copyright \(c\) {} ({})'.format(YEARS, '|'.join(AUTHORS)))
@@ -167,7 +171,7 @@
has_apache2 = False
line_num = 0
apache_expected_end = 0
- with open(file) as contents:
+ with open(file, encoding='utf-8') as contents:
for line in contents:
line_num += 1
if COPYRIGHT_RE.search(line):
diff --git a/third_party/SPIRV-Tools/utils/generate_language_headers.py b/third_party/SPIRV-Tools/utils/generate_language_headers.py
index 0296163..83fa99e 100755
--- a/third_party/SPIRV-Tools/utils/generate_language_headers.py
+++ b/third_party/SPIRV-Tools/utils/generate_language_headers.py
@@ -159,27 +159,25 @@
import argparse
parser = argparse.ArgumentParser(description='Generate language headers from a JSON grammar')
- parser.add_argument('--extinst-name',
- type=str, required=True,
- help='The name to use in tokens')
parser.add_argument('--extinst-grammar', metavar='<path>',
type=str, required=True,
help='input JSON grammar file for extended instruction set')
- parser.add_argument('--extinst-output-base', metavar='<path>',
+ parser.add_argument('--extinst-output-path', metavar='<path>',
type=str, required=True,
- help='Basename of the language-specific output file.')
+ help='Path of the language-specific output file.')
args = parser.parse_args()
with open(args.extinst_grammar) as json_file:
grammar_json = json.loads(json_file.read())
- grammar = ExtInstGrammar(name = args.extinst_name,
+ grammar_name = os.path.splitext(os.path.basename(args.extinst_output_path))[0]
+ grammar = ExtInstGrammar(name = grammar_name,
copyright = grammar_json['copyright'],
instructions = grammar_json['instructions'],
operand_kinds = grammar_json['operand_kinds'],
version = grammar_json['version'],
revision = grammar_json['revision'])
- make_path_to_file(args.extinst_output_base)
- with open(args.extinst_output_base + '.h', 'w') as f:
+ make_path_to_file(args.extinst_output_path)
+ with open(args.extinst_output_path, 'w') as f:
f.write(CGenerator().generate(grammar))
diff --git a/third_party/SPIRV-Tools/utils/generate_registry_tables.py b/third_party/SPIRV-Tools/utils/generate_registry_tables.py
index e662ba9..28152ef 100755
--- a/third_party/SPIRV-Tools/utils/generate_registry_tables.py
+++ b/third_party/SPIRV-Tools/utils/generate_registry_tables.py
@@ -14,11 +14,30 @@
# limitations under the License.
"""Generates the vendor tool table from the SPIR-V XML registry."""
-import distutils.dir_util
+import errno
import os.path
import xml.etree.ElementTree
+def mkdir_p(directory):
+ """Make the directory, and all its ancestors as required. Any of the
+ directories are allowed to already exist.
+ This is compatible with Python down to 3.0.
+ """
+
+ if directory == "":
+ # We're being asked to make the current directory.
+ return
+
+ try:
+ os.makedirs(directory)
+ except OSError as e:
+ if e.errno == errno.EEXIST and os.path.isdir(directory):
+ pass
+ else:
+ raise
+
+
def generate_vendor_table(registry):
"""Returns a list of C style initializers for the registered vendors
and their tools.
@@ -62,7 +81,7 @@
with open(args.xml) as xml_in:
registry = xml.etree.ElementTree.fromstring(xml_in.read())
- distutils.dir_util.mkpath(os.path.dirname(args.generator_output))
+ mkdir_p(os.path.dirname(args.generator_output))
with open(args.generator_output, 'w') as f:
f.write(generate_vendor_table(registry))
diff --git a/third_party/SPIRV-Tools/utils/vscode/extension.js b/third_party/SPIRV-Tools/utils/vscode/extension.js
index f220172..e7fec28 100644
--- a/third_party/SPIRV-Tools/utils/vscode/extension.js
+++ b/third_party/SPIRV-Tools/utils/vscode/extension.js
@@ -63,4 +63,4 @@
// this method is called when your extension is deactivated
function deactivate() {
}
-exports.deactivate = deactivate;
\ No newline at end of file
+exports.deactivate = deactivate;