diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-04-28 03:05:41 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-04-28 03:05:41 +0000 |
commit | d1cf0210b24d17a9234c2fdca8c26d07884c3531 (patch) | |
tree | a0885b0caacd225d14600909f71969741fc4f3e2 | |
parent | 3fb7f0dbd5fb2598f62820dcb5e7b035d982a4be (diff) | |
parent | 31e364bbe017350c2ec473665ac25db095654add (diff) | |
download | spirv-tools-ndk-r25-release.tar.gz |
Snap for 8508678 from 31e364bbe017350c2ec473665ac25db095654add to ndk-r25-releasendk-r25cndk-r25bndk-r25ndk-r25-release
Change-Id: Ibe0eaaf901883a93e51af2b8de136c0742614413
407 files changed, 30542 insertions, 2796 deletions
@@ -2,7 +2,7 @@ .ycm_extra_conf.py* *.pyc compile_commands.json -/build/ +/build*/ /buildtools/ /external/googletest /external/SPIRV-Headers @@ -20,6 +20,7 @@ bazel-bin bazel-genfiles bazel-out bazel-spirv-tools +bazel-SPIRV-Tools bazel-testlogs # Vim @@ -106,6 +106,7 @@ SPVTOOLS_OPT_SRC_FILES := \ source/opt/eliminate_dead_constant_pass.cpp \ source/opt/eliminate_dead_functions_pass.cpp \ source/opt/eliminate_dead_functions_util.cpp \ + source/opt/eliminate_dead_input_components_pass.cpp \ source/opt/eliminate_dead_members_pass.cpp \ source/opt/feature_manager.cpp \ source/opt/fix_storage_class.cpp \ @@ -156,6 +157,7 @@ SPVTOOLS_OPT_SRC_FILES := \ source/opt/redundancy_elimination.cpp \ source/opt/register_pressure.cpp \ source/opt/relax_float_ops_pass.cpp \ + source/opt/remove_dontinline_pass.cpp \ source/opt/remove_duplicates_pass.cpp \ source/opt/remove_unused_interface_variables_pass.cpp \ source/opt/replace_desc_array_access_using_var_index.cpp \ @@ -165,10 +167,11 @@ SPVTOOLS_OPT_SRC_FILES := \ source/opt/scalar_replacement_pass.cpp \ source/opt/set_spec_constant_default_value_pass.cpp \ source/opt/simplification_pass.cpp \ + source/opt/spread_volatile_semantics.cpp \ source/opt/ssa_rewrite_pass.cpp \ source/opt/strength_reduction_pass.cpp \ source/opt/strip_debug_info_pass.cpp \ - source/opt/strip_reflect_info_pass.cpp \ + source/opt/strip_nonsemantic_info_pass.cpp \ source/opt/struct_cfg_analysis.cpp \ source/opt/type_manager.cpp \ source/opt/types.cpp \ diff --git a/BUILD.bazel b/BUILD.bazel index b2031ded..c86ebbef 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -81,6 +81,7 @@ genrule( srcs = ["@spirv_headers//:spirv_xml_registry"], outs = ["generators.inc"], cmd = "$(location generate_registry_tables) --xml=$(location @spirv_headers//:spirv_xml_registry) --generator-output=$(location generators.inc)", + cmd_bat = "$(location //:generate_registry_tables) --xml=$(location @spirv_headers//:spirv_xml_registry) --generator-output=$(location generators.inc)", tools = [":generate_registry_tables"], ) @@ -94,6 +95,7 @@ genrule( srcs = ["CHANGES"], outs = ["build-version.inc"], cmd = "SOURCE_DATE_EPOCH=0 $(location update_build_version) $$(dirname $(location CHANGES)) $(location build-version.inc)", + cmd_bat = "set SOURCE_DATE_EPOCH=0 && $(location //:update_build_version) \"$(location CHANGES)\\..\" $(location build-version.inc)", tools = [":update_build_version"], ) @@ -19,6 +19,17 @@ if (build_with_chromium) { import("//third_party/protobuf/proto_library.gni") } +# SPIRV-Tools may be part of multiple projects in the Chromium tree. +# Only enable building executables if this is the main copy. +abspath = get_path_info(".", "abspath") +spvtools_chromium_third_party = (abspath == "//third_party/vulkan-deps/spirv-tools/src/") +spvtools_build_executables = build_with_chromium && spvtools_chromium_third_party +# Fuchsia also requires building the executables. +# TODO(b/158002593): Avoid the use of dependent-specific variables. +if (defined(is_fuchsia_tree) && is_fuchsia_tree) { + spvtools_build_executables = true +} + spirv_headers = spirv_tools_spirv_headers_dir spirv_is_winuwp = is_win && target_os == "winuwp" @@ -448,12 +459,14 @@ static_library("spvtools") { "source/util/bit_vector.cpp", "source/util/bit_vector.h", "source/util/bitutils.h", + "source/util/hash_combine.h", "source/util/hex_float.h", "source/util/ilist.h", "source/util/ilist_node.h", "source/util/make_unique.h", "source/util/parse_number.cpp", "source/util/parse_number.h", + "source/util/pooled_linked_list.h", "source/util/small_vector.h", "source/util/string_utils.cpp", "source/util/string_utils.h", @@ -609,6 +622,8 @@ static_library("spvtools_opt") { "source/opt/eliminate_dead_functions_pass.h", "source/opt/eliminate_dead_functions_util.cpp", "source/opt/eliminate_dead_functions_util.h", + "source/opt/eliminate_dead_input_components_pass.cpp", + "source/opt/eliminate_dead_input_components_pass.h", "source/opt/eliminate_dead_members_pass.cpp", "source/opt/eliminate_dead_members_pass.h", "source/opt/empty_pass.h", @@ -714,6 +729,8 @@ static_library("spvtools_opt") { "source/opt/register_pressure.h", "source/opt/relax_float_ops_pass.cpp", "source/opt/relax_float_ops_pass.h", + "source/opt/remove_dontinline_pass.cpp", + "source/opt/remove_dontinline_pass.h", "source/opt/remove_duplicates_pass.cpp", "source/opt/remove_duplicates_pass.h", "source/opt/remove_unused_interface_variables_pass.cpp", @@ -732,14 +749,16 @@ static_library("spvtools_opt") { "source/opt/set_spec_constant_default_value_pass.h", "source/opt/simplification_pass.cpp", "source/opt/simplification_pass.h", + "source/opt/spread_volatile_semantics.cpp", + "source/opt/spread_volatile_semantics.h", "source/opt/ssa_rewrite_pass.cpp", "source/opt/ssa_rewrite_pass.h", "source/opt/strength_reduction_pass.cpp", "source/opt/strength_reduction_pass.h", "source/opt/strip_debug_info_pass.cpp", "source/opt/strip_debug_info_pass.h", - "source/opt/strip_reflect_info_pass.cpp", - "source/opt/strip_reflect_info_pass.h", + "source/opt/strip_nonsemantic_info_pass.cpp", + "source/opt/strip_nonsemantic_info_pass.h", "source/opt/struct_cfg_analysis.cpp", "source/opt/struct_cfg_analysis.h", "source/opt/tree_iterator.h", @@ -869,7 +888,7 @@ static_library("spvtools_reduce") { configs += [ ":spvtools_internal_config" ] } -if (build_with_chromium) { +if (build_with_chromium && spvtools_build_executables) { # The spirv-fuzz library is only built when in a Chromium checkout # due to its dependency on protobuf. @@ -1298,7 +1317,7 @@ group("SPIRV-Tools") { # The tests are scoped to Chromium to avoid needing to write gtest integration. # See Chromium's third_party/googletest/BUILD.gn for a complete integration. -if (build_with_chromium) { +if (build_with_chromium && spvtools_build_executables) { test("spvtools_test") { sources = [ "test/assembly_context_test.cpp", @@ -1407,73 +1426,75 @@ source_set("spvtools_software_version") { configs += [ ":spvtools_internal_config" ] } -executable("spirv-as") { - sources = [ "tools/as/as.cpp" ] - deps = [ - ":spvtools", - ":spvtools_software_version", - ] - configs += [ ":spvtools_internal_config" ] -} +if (spvtools_build_executables) { + executable("spirv-as") { + sources = [ "tools/as/as.cpp" ] + deps = [ + ":spvtools", + ":spvtools_software_version", + ] + configs += [ ":spvtools_internal_config" ] + } -executable("spirv-dis") { - sources = [ "tools/dis/dis.cpp" ] - deps = [ - ":spvtools", - ":spvtools_software_version", - ] - configs += [ ":spvtools_internal_config" ] -} + executable("spirv-dis") { + sources = [ "tools/dis/dis.cpp" ] + deps = [ + ":spvtools", + ":spvtools_software_version", + ] + configs += [ ":spvtools_internal_config" ] + } -executable("spirv-val") { - sources = [ "tools/val/val.cpp" ] - deps = [ - ":spvtools", - ":spvtools_software_version", - ":spvtools_util_cli_consumer", - ":spvtools_val", - ] - configs += [ ":spvtools_internal_config" ] -} + executable("spirv-val") { + sources = [ "tools/val/val.cpp" ] + deps = [ + ":spvtools", + ":spvtools_software_version", + ":spvtools_util_cli_consumer", + ":spvtools_val", + ] + configs += [ ":spvtools_internal_config" ] + } -executable("spirv-cfg") { - sources = [ - "tools/cfg/bin_to_dot.cpp", - "tools/cfg/bin_to_dot.h", - "tools/cfg/cfg.cpp", - ] - deps = [ - ":spvtools", - ":spvtools_software_version", - ] - configs += [ ":spvtools_internal_config" ] -} + executable("spirv-cfg") { + sources = [ + "tools/cfg/bin_to_dot.cpp", + "tools/cfg/bin_to_dot.h", + "tools/cfg/cfg.cpp", + ] + deps = [ + ":spvtools", + ":spvtools_software_version", + ] + configs += [ ":spvtools_internal_config" ] + } -executable("spirv-opt") { - sources = [ "tools/opt/opt.cpp" ] - deps = [ - ":spvtools", - ":spvtools_opt", - ":spvtools_software_version", - ":spvtools_util_cli_consumer", - ":spvtools_val", - ] - configs += [ ":spvtools_internal_config" ] -} + executable("spirv-opt") { + sources = [ "tools/opt/opt.cpp" ] + deps = [ + ":spvtools", + ":spvtools_opt", + ":spvtools_software_version", + ":spvtools_util_cli_consumer", + ":spvtools_val", + ] + configs += [ ":spvtools_internal_config" ] + } -executable("spirv-link") { - sources = [ "tools/link/linker.cpp" ] - deps = [ - ":spvtools", - ":spvtools_link", - ":spvtools_opt", - ":spvtools_software_version", - ":spvtools_val", - ] - configs += [ ":spvtools_internal_config" ] + executable("spirv-link") { + sources = [ "tools/link/linker.cpp" ] + deps = [ + ":spvtools", + ":spvtools_link", + ":spvtools_opt", + ":spvtools_software_version", + ":spvtools_val", + ] + configs += [ ":spvtools_internal_config" ] + } } -if (!is_ios && !spirv_is_winuwp && build_with_chromium) { +if (!is_ios && !spirv_is_winuwp && build_with_chromium && spvtools_build_executables) { # iOS and UWP do not allow std::system calls which spirv-fuzz # requires. Additionally, spirv-fuzz is only built when in a # Chromium checkout due to its dependency on protobuf. @@ -1494,7 +1515,7 @@ if (!is_ios && !spirv_is_winuwp && build_with_chromium) { } } -if (!is_ios && !spirv_is_winuwp) { +if (!is_ios && !spirv_is_winuwp && spvtools_build_executables) { # iOS and UWP do not allow std::system calls which spirv-reduce # requires. @@ -1512,19 +1533,21 @@ if (!is_ios && !spirv_is_winuwp) { } } -group("all_spirv_tools") { - deps = [ - ":spirv-as", - ":spirv-cfg", - ":spirv-dis", - ":spirv-link", - ":spirv-opt", - ":spirv-val", - ] - if (!is_ios && !spirv_is_winuwp && build_with_chromium) { - deps += [ ":spirv-fuzz" ] - } - if (!is_ios && !spirv_is_winuwp) { - deps += [ ":spirv-reduce" ] +if (spvtools_build_executables){ + group("all_spirv_tools") { + deps = [ + ":spirv-as", + ":spirv-cfg", + ":spirv-dis", + ":spirv-link", + ":spirv-opt", + ":spirv-val", + ] + if (!is_ios && !spirv_is_winuwp && build_with_chromium) { + deps += [ ":spirv-fuzz" ] + } + if (!is_ios && !spirv_is_winuwp) { + deps += [ ":spirv-reduce" ] + } } } @@ -1,5 +1,66 @@ Revision history for SPIRV-Tools +v2022.2-dev 2022-04-04 + - General + - Add OpModuleProcessed to debug opcode (#4694) + - Optimizer + - Complete handling of RayQueryKHR type (#4690) + - Have scalar replacement use undef instead of null (#4691) + - Optimize Instruction::Instruction (#4705) + - Handle propagation of arrays with decorations (#4717) + - spirv-opt: Add OpExecutionModeId support (#4719) + - Optimize Type::HashValue (#4707) + - Optimize DefUseManager allocations (#4709) + - Add pass to remove DontInline function control (#4747) + - Better handling of 0xFFFFFFFF when folding vector shuffle (#4743) + - Reset the id bound on the module in compact ids (#4744) + - spirv-opt: (WIP) Eliminate Dead Input Component Pass (#4720) + - Support SPV_KHR_uniform_group_instructions (#4734) + - Handle shaders without execution model in spread-volatile-semantics (#4766) + - Validator + - Fix handling of Nontemporal image operand (#4692) + - [spirv-val] Allow 0 Component Count for DebugTypeArray for Shader (#4706) + - spirv-val: Validate DebugTypeMatrix (#4732) + - spirv-val: Label Vulkan VUID 04734 (#4739) + - spirv-val: Label VUID 06491 (#4745) + - spirv-val: Disallow array of push constants (#4742) + - spirv-val: Label Vulkan RuntimeArray VUID (#4749) + - spirv-val: Add Vulkan Image VUID 06214 (#4750) + - spirv-val: Add Vulkan Dref not allowed 3D dim VUID (#4751) + - spirv-val: Label and add test for PSB Aligned (#4756) + - spirv-val: Add Vulkan 32-bit bit op Base (#4758) + - spirv-val: Add more Vulkan VUID labels (#4764) + - Diff + - Introduce spirv-diff (#4611) + - Stabilize the output of spirv-diff (#4698) + - spirv-diff: Handle OpSpecConstant array sizes (#4700) + - spirv-diff: Match OpSpecConstantComposite correctly (#4704) + - spirv-diff: Use GetSingleWord*Operand (#4768) + - spirv-diff: Basic support for OpTypeForwardPointer (#4761) + - spirv-diff: Fix OpTypeFunction matching w.r.t operand count (#4771) + +v2022.1 2022-01-26 + - General + - Add SPIR-V 1.6 support to wasm build (#4674) + - Improvements to disassembly within PassManager (#4677) + - Basic support for SPIR-V 1.6 (#4663) + - reflect debug (#4662) + - Fix endianness of string literals (#4622) + - Optimizer + - spirv-opt: add pass to Spread Volatile semantics (#4667) + - Fix constant propagation and folding of FClamp instructions (#4651) + - Manually fold floating point division by zero (#4637) + - Allow ADCE to remove dead inputs (#4629) + - Linker + - Linker improvements (#4679) + * test/linker: Code factorisation and small tweaks + * linker: Do not fail when going over limits + - Validator + - val: interface struct with builtins must be Block (#4665) + - Fuzzer + - Avoid id bound errors during opt fuzzing (#4658) + - Avoid uninitialised read when parsing hex float (#4646) + v2021.4 2021-11-11 - General - Add a WebAssembly build (#3752) diff --git a/CMakeLists.txt b/CMakeLists.txt index 70caf857..76b87d8c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -116,7 +116,7 @@ if(${COMPILER_IS_LIKE_GNU}) set(SPIRV_WARNINGS ${SPIRV_WARNINGS} -Werror) endif() elseif(MSVC) - set(SPIRV_WARNINGS -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS /wd4800) + set(SPIRV_WARNINGS -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS /wd4800 /wd4819) if(${SPIRV_WERROR}) set(SPIRV_WARNINGS ${SPIRV_WARNINGS} /WX) @@ -4,9 +4,9 @@ vars = { 'github': 'https://github.com', 'effcee_revision': 'ddf5e2bb92957dc8a12c5392f8495333d6844133', - 'googletest_revision': 'bf0701daa9f5b30e5882e2f8f9a5280bcba87e77', - 're2_revision': '4244cd1cb492fa1d10986ec67f862964c073f844', - 'spirv_headers_revision': '814e728b30ddd0f4509233099a3ad96fd4318c07', + 'googletest_revision': '25dcdc7e8bfac8967f20fb2c0a628f5cf442188d', + 're2_revision': '0c5616df9c0aaa44c9440d87422012423d91c7d1', + 'spirv_headers_revision': '4995a2f2723c401eb0ea3e10c81298906bf1422b', } deps = { @@ -212,6 +212,24 @@ issue](https://github.com/KhronosGroup/SPIRV-Tools/issues]) with "Fuzzer:" as the start of its title. +### Diff + +*Note:* The diff tool is still under development. + +The diff tool takes two SPIR-V files, either in binary or text format and +produces a diff-style comparison between the two. The instructions between the +src and dst modules are matched as best as the tool can, and output is produced +(in src id-space) that shows which instructions are removed in src, added in dst +or modified between them. The order of instructions are not retained. + +Matching instructions between two SPIR-V modules is not trivial, and thus a +number of heuristics are applied in this tool. In particular, without debug +information, match functions is nontrivial as they can be reordered. As such, +this tool is primarily useful to produce the diff of two SPIR-V modules derived +from the same source, for example before and after a modification to the shader, +before and after a transformation, or SPIR-V produced from different tools. + + ### Extras * [Utility filters](#utility-filters) @@ -399,7 +417,7 @@ targets, you need to install CMake Version 2.8.12 or later. - [Python 3](http://www.python.org/): for utility scripts and running the test suite. - [Bazel](https://bazel.build/) (optional): if building the source with Bazel, -you need to install Bazel Version 0.29.1 on your machine. Other versions may +you need to install Bazel Version 5.0.0 on your machine. Other versions may also work, but are not verified. - [Emscripten SDK](https://emscripten.org) (optional): if building the WebAssembly module. @@ -624,6 +642,15 @@ This is experimental. * `spirv-cfg` - the control flow graph dumper * `<spirv-dir>/tools/cfg` +### Diff tool + +*Warning:* This functionality is under development, and is incomplete. + +The diff tool produces a diff-style comparison between two SPIR-V modules. + +* `spirv-diff` - the standalone diff tool + * `<spirv-dir>`/tools/diff` + ### Utility filters * `spirv-lesspipe.sh` - Automatically disassembles `.spv` binary files for the diff --git a/build_defs.bzl b/build_defs.bzl index b2cd41b9..ef9a8294 100644 --- a/build_defs.bzl +++ b/build_defs.bzl @@ -68,6 +68,14 @@ def generate_core_tables(version = None): "--core-insts-output=$(location {3}) " + "--operand-kinds-output=$(location {4})" ).format(*fmtargs), + cmd_bat = ( + "$(location :generate_grammar_tables) " + + "--spirv-core-grammar=$(location {0}) " + + "--extinst-debuginfo-grammar=$(location {1}) " + + "--extinst-cldebuginfo100-grammar=$(location {2}) " + + "--core-insts-output=$(location {3}) " + + "--operand-kinds-output=$(location {4})" + ).format(*fmtargs), tools = [":generate_grammar_tables"], visibility = ["//visibility:private"], ) @@ -97,6 +105,14 @@ def generate_enum_string_mapping(version = None): "--extension-enum-output=$(location {3}) " + "--enum-string-mapping-output=$(location {4})" ).format(*fmtargs), + cmd_bat = ( + "$(location :generate_grammar_tables) " + + "--spirv-core-grammar=$(location {0}) " + + "--extinst-debuginfo-grammar=$(location {1}) " + + "--extinst-cldebuginfo100-grammar=$(location {2}) " + + "--extension-enum-output=$(location {3}) " + + "--enum-string-mapping-output=$(location {4})" + ).format(*fmtargs), tools = [":generate_grammar_tables"], visibility = ["//visibility:private"], ) @@ -118,6 +134,11 @@ def generate_opencl_tables(version = None): "--extinst-opencl-grammar=$(location {0}) " + "--opencl-insts-output=$(location {1})" ).format(*fmtargs), + cmd_bat = ( + "$(location :generate_grammar_tables) " + + "--extinst-opencl-grammar=$(location {0}) " + + "--opencl-insts-output=$(location {1})" + ).format(*fmtargs), tools = [":generate_grammar_tables"], visibility = ["//visibility:private"], ) @@ -139,6 +160,11 @@ def generate_glsl_tables(version = None): "--extinst-glsl-grammar=$(location {0}) " + "--glsl-insts-output=$(location {1})" ).format(*fmtargs), + cmd_bat = ( + "$(location :generate_grammar_tables) " + + "--extinst-glsl-grammar=$(location {0}) " + + "--glsl-insts-output=$(location {1})" + ).format(*fmtargs), tools = [":generate_grammar_tables"], visibility = ["//visibility:private"], ) @@ -161,6 +187,12 @@ def generate_vendor_tables(extension, operand_kind_prefix = ""): "--vendor-insts-output=$(location {1}) " + "--vendor-operand-kind-prefix={2}" ).format(*fmtargs), + cmd_bat = ( + "$(location :generate_grammar_tables) " + + "--extinst-vendor-grammar=$(location {0}) " + + "--vendor-insts-output=$(location {1}) " + + "--vendor-operand-kind-prefix={2}" + ).format(*fmtargs), tools = [":generate_grammar_tables"], visibility = ["//visibility:private"], ) @@ -179,6 +211,11 @@ def generate_extinst_lang_headers(name, grammar = None): "--extinst-grammar=$< " + "--extinst-output-path=$(location {0})" ).format(*fmtargs), + cmd_bat = ( + "$(location :generate_language_headers) " + + "--extinst-grammar=$< " + + "--extinst-output-path=$(location {0})" + ).format(*fmtargs), tools = [":generate_language_headers"], visibility = ["//visibility:private"], ) diff --git a/include/spirv-tools/libspirv.h b/include/spirv-tools/libspirv.h index 8df14f5f..e1b8890e 100644 --- a/include/spirv-tools/libspirv.h +++ b/include/spirv-tools/libspirv.h @@ -482,6 +482,7 @@ SPIRV_TOOLS_EXPORT const char* spvSoftwareVersionDetailsString(void); // SPV_ENV_VULKAN_1_1 -> SPIR-V 1.3 // SPV_ENV_VULKAN_1_1_SPIRV_1_4 -> SPIR-V 1.4 // SPV_ENV_VULKAN_1_2 -> SPIR-V 1.5 +// SPV_ENV_VULKAN_1_3 -> SPIR-V 1.6 // Consult the description of API entry points for specific rules. typedef enum { SPV_ENV_UNIVERSAL_1_0, // SPIR-V 1.0 latest revision, no other restrictions. @@ -516,7 +517,11 @@ typedef enum { SPV_ENV_UNIVERSAL_1_5, // SPIR-V 1.5 latest revision, no other restrictions. SPV_ENV_VULKAN_1_2, // Vulkan 1.2 latest revision. - SPV_ENV_MAX // Keep this as the last enum value. + + SPV_ENV_UNIVERSAL_1_6, // SPIR-V 1.6 latest revision, no other restrictions. + SPV_ENV_VULKAN_1_3, // Vulkan 1.3 latest revision. + + SPV_ENV_MAX // Keep this as the last enum value. } spv_target_env; // SPIR-V Validator can be parameterized with the following Universal Limits. @@ -555,7 +560,7 @@ SPIRV_TOOLS_EXPORT bool spvParseVulkanEnv(uint32_t vulkan_ver, // Creates a context object for most of the SPIRV-Tools API. // Returns null if env is invalid. // -// See specific API calls for how the target environment is interpeted +// See specific API calls for how the target environment is interpreted // (particularly assembly and validation). SPIRV_TOOLS_EXPORT spv_context spvContextCreate(spv_target_env env); @@ -607,7 +612,7 @@ SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetRelaxLogicalPointer( // set that option. // 2) Pointers that are pass as parameters to function calls do not have to // match the storage class of the formal parameter. -// 3) Pointers that are actaul parameters on function calls do not have to point +// 3) Pointers that are actual parameters on function calls do not have to point // to the same type pointed as the formal parameter. The types just need to // logically match. // 4) GLSLstd450 Interpolate* instructions can have a load of an interpolant @@ -633,7 +638,7 @@ SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetUniformBufferStandardLayout( // Records whether the validator should use "scalar" block layout rules. // Scalar layout rules are more permissive than relaxed block layout. // -// See Vulkan extnesion VK_EXT_scalar_block_layout. The scalar alignment is +// See Vulkan extension VK_EXT_scalar_block_layout. The scalar alignment is // defined as follows: // - scalar alignment of a scalar is the scalar size // - scalar alignment of a vector is the scalar alignment of its component diff --git a/include/spirv-tools/libspirv.hpp b/include/spirv-tools/libspirv.hpp index 8dfb46b7..25eb8a1d 100644 --- a/include/spirv-tools/libspirv.hpp +++ b/include/spirv-tools/libspirv.hpp @@ -36,7 +36,7 @@ class Context { public: // Constructs a context targeting the given environment |env|. // - // See specific API calls for how the target environment is interpeted + // See specific API calls for how the target environment is interpreted // (particularly assembly and validation). // // The constructed instance will have an empty message consumer, which just @@ -139,7 +139,7 @@ class ValidatorOptions { // set that option. // 2) Pointers that are pass as parameters to function calls do not have to // match the storage class of the formal parameter. - // 3) Pointers that are actaul parameters on function calls do not have to + // 3) Pointers that are actual parameters on function calls do not have to // point to the same type pointed as the formal parameter. The types just // need to logically match. // 4) GLSLstd450 Interpolate* instructions can have a load of an interpolant diff --git a/include/spirv-tools/optimizer.hpp b/include/spirv-tools/optimizer.hpp index 21059cbe..fbbd9bc0 100644 --- a/include/spirv-tools/optimizer.hpp +++ b/include/spirv-tools/optimizer.hpp @@ -43,7 +43,7 @@ class Optimizer { // consumed by the RegisterPass() method. Tokens are one-time objects that // only support move; copying is not allowed. struct PassToken { - struct Impl; // Opaque struct for holding inernal data. + struct Impl; // Opaque struct for holding internal data. PassToken(std::unique_ptr<Impl>); @@ -227,16 +227,17 @@ Optimizer::PassToken CreateNullPass(); // Creates a strip-debug-info pass. // A strip-debug-info pass removes all debug instructions (as documented in -// Section 3.32.2 of the SPIR-V spec) of the SPIR-V module to be optimized. +// Section 3.42.2 of the SPIR-V spec) of the SPIR-V module to be optimized. Optimizer::PassToken CreateStripDebugInfoPass(); -// Creates a strip-reflect-info pass. -// A strip-reflect-info pass removes all reflections instructions. -// For now, this is limited to removing decorations defined in -// SPV_GOOGLE_hlsl_functionality1. The coverage may expand in -// the future. +// [Deprecated] This will create a strip-nonsemantic-info pass. See below. Optimizer::PassToken CreateStripReflectInfoPass(); +// Creates a strip-nonsemantic-info pass. +// A strip-nonsemantic-info pass removes all reflections and explicitly +// non-semantic instructions. +Optimizer::PassToken CreateStripNonSemanticInfoPass(); + // Creates an eliminate-dead-functions pass. // An eliminate-dead-functions pass will remove all functions that are not in // the call trees rooted at entry points and exported functions. These @@ -295,11 +296,11 @@ Optimizer::PassToken CreateFreezeSpecConstantValuePass(); // and can be changed in future. A spec constant is foldable if all of its // value(s) can be determined from the module. E.g., an integer spec constant // defined with OpSpecConstantOp instruction can be folded if its value won't -// change later. This pass will replace the original OpSpecContantOp instruction -// with an OpConstant instruction. When folding composite spec constants, -// new instructions may be inserted to define the components of the composite -// constant first, then the original spec constants will be replaced by -// OpConstantComposite instructions. +// change later. This pass will replace the original OpSpecConstantOp +// instruction with an OpConstant instruction. When folding composite spec +// constants, new instructions may be inserted to define the components of the +// composite constant first, then the original spec constants will be replaced +// by OpConstantComposite instructions. // // There are some operations not supported yet: // OpSConvert, OpFConvert, OpQuantizeToF16 and @@ -325,7 +326,7 @@ Optimizer::PassToken CreateUnifyConstantPass(); // Creates a eliminate-dead-constant pass. // A eliminate-dead-constant pass removes dead constants, including normal -// contants defined by OpConstant, OpConstantComposite, OpConstantTrue, or +// constants defined by OpConstant, OpConstantComposite, OpConstantTrue, or // OpConstantFalse and spec constants defined by OpSpecConstant, // OpSpecConstantComposite, OpSpecConstantTrue, OpSpecConstantFalse or // OpSpecConstantOp. @@ -389,7 +390,7 @@ Optimizer::PassToken CreateInlineOpaquePass(); // Only modules with relaxed logical addressing (see opt/instruction.h) are // currently processed. // -// This pass is most effective if preceeded by Inlining and +// This pass is most effective if preceded by Inlining and // LocalAccessChainConvert. This pass will reduce the work needed to be done // by LocalSingleStoreElim and LocalMultiStoreElim. // @@ -407,7 +408,7 @@ Optimizer::PassToken CreateLocalSingleBlockLoadStoreElimPass(); // Note that some branches and blocks may be left to avoid creating invalid // control flow. Improving this is left to future work. // -// This pass is most effective when preceeded by passes which eliminate +// This pass is most effective when preceded by passes which eliminate // local loads and stores, effectively propagating constant values where // possible. Optimizer::PassToken CreateDeadBranchElimPass(); @@ -424,7 +425,7 @@ Optimizer::PassToken CreateDeadBranchElimPass(); // are currently processed. Currently modules with any extensions enabled are // not processed. This is left for future work. // -// This pass is most effective if preceeded by Inlining and +// This pass is most effective if preceded by Inlining and // LocalAccessChainConvert. LocalSingleStoreElim and LocalSingleBlockElim // will reduce the work that this pass has to do. Optimizer::PassToken CreateLocalMultiStoreElimPass(); @@ -519,7 +520,8 @@ Optimizer::PassToken CreateDeadInsertElimPass(); // interface are considered live and are not eliminated. This mode is needed // by GPU-Assisted validation instrumentation, where a change in the interface // is not allowed. -Optimizer::PassToken CreateAggressiveDCEPass(bool preserve_interface = false); +Optimizer::PassToken CreateAggressiveDCEPass(); +Optimizer::PassToken CreateAggressiveDCEPass(bool preserve_interface); // Creates a remove-unused-interface-variables pass. // Removes variables referenced on the |OpEntryPoint| instruction that are not @@ -628,7 +630,7 @@ Optimizer::PassToken CreateRedundancyEliminationPass(); Optimizer::PassToken CreateScalarReplacementPass(uint32_t size_limit = 100); // Create a private to local pass. -// This pass looks for variables delcared in the private storage class that are +// This pass looks for variables declared in the private storage class that are // used in only one function. Those variables are moved to the function storage // class in the function that they are used. Optimizer::PassToken CreatePrivateToLocalPass(); @@ -833,6 +835,19 @@ Optimizer::PassToken CreateFixStorageClassPass(); // inclusive. Optimizer::PassToken CreateGraphicsRobustAccessPass(); +// Create a pass to spread Volatile semantics to variables with SMIDNV, +// WarpIDNV, SubgroupSize, SubgroupLocalInvocationId, SubgroupEqMask, +// SubgroupGeMask, SubgroupGtMask, SubgroupLeMask, or SubgroupLtMask BuiltIn +// decorations or OpLoad for them when the shader model is the ray generation, +// closest hit, miss, intersection, or callable. This pass can be used for +// VUID-StandaloneSpirv-VulkanMemoryModel-04678 and +// VUID-StandaloneSpirv-VulkanMemoryModel-04679 (See "Standalone SPIR-V +// Validation" section of Vulkan spec "Appendix A: Vulkan Environment for +// SPIR-V"). When the SPIR-V version is 1.6 or above, the pass also spreads +// the Volatile semantics to a variable with HelperInvocation BuiltIn decoration +// in the fragement shader. +Optimizer::PassToken CreateSpreadVolatileSemanticsPass(); + // Create a pass to replace a descriptor access using variable index. // This pass replaces every access using a variable index to array variable // |desc| that has a DescriptorSet and Binding decorations with a constant @@ -871,6 +886,13 @@ Optimizer::PassToken CreateAmdExtToKhrPass(); // propagated into their final positions. Optimizer::PassToken CreateInterpolateFixupPass(); +// Removes unused components from composite input variables. Current +// implementation just removes trailing unused components from input arrays. +// The pass performs best after maximizing dead code removal. A subsequent dead +// code elimination pass would be beneficial in removing newly unused component +// types. +Optimizer::PassToken CreateEliminateDeadInputComponentsPass(); + // Creates a convert-to-sampled-image pass to convert images and/or // samplers with given pairs of descriptor set and binding to sampled image. // If a pair of an image and a sampler have the same pair of descriptor set and @@ -881,6 +903,10 @@ Optimizer::PassToken CreateConvertToSampledImagePass( const std::vector<opt::DescriptorSetAndBinding>& descriptor_set_binding_pairs); +// Creates a remove-dont-inline pass to remove the |DontInline| function control +// from every function in the module. This is useful if you want the inliner to +// inline these functions some reason. +Optimizer::PassToken CreateRemoveDontInlinePass(); } // namespace spvtools #endif // INCLUDE_SPIRV_TOOLS_OPTIMIZER_HPP_ diff --git a/kokoro/macos-clang-release-bazel/build.sh b/kokoro/macos-clang-release-bazel/build.sh index d2a516f5..c62611ab 100644 --- a/kokoro/macos-clang-release-bazel/build.sh +++ b/kokoro/macos-clang-release-bazel/build.sh @@ -31,14 +31,14 @@ cd external && cd googletest && git reset --hard 1fb1bb23bb8418dc73a5a9a82bbed31 git clone --depth=1 https://github.com/google/effcee external/effcee git clone --depth=1 https://github.com/google/re2 external/re2 -# Get bazel 0.29.1. -gsutil cp gs://bazel/0.29.1/release/bazel-0.29.1-darwin-x86_64 . -chmod +x bazel-0.29.1-darwin-x86_64 +# Get bazel 5.0.0 +gsutil cp gs://bazel/5.0.0/release/bazel-5.0.0-darwin-x86_64 . +chmod +x bazel-5.0.0-darwin-x86_64 echo $(date): Build everything... -./bazel-0.29.1-darwin-x86_64 build :all +./bazel-5.0.0-darwin-x86_64 build :all echo $(date): Build completed. echo $(date): Starting bazel test... -./bazel-0.29.1-darwin-x86_64 test :all +./bazel-5.0.0-darwin-x86_64 test :all echo $(date): Bazel test completed. diff --git a/kokoro/scripts/linux/build-docker.sh b/kokoro/scripts/linux/build-docker.sh index 8f76803c..80043b8a 100755 --- a/kokoro/scripts/linux/build-docker.sh +++ b/kokoro/scripts/linux/build-docker.sh @@ -195,7 +195,7 @@ elif [ $TOOL = "android-ndk-build" ]; then echo $(date): ndk-build completed. elif [ $TOOL = "bazel" ]; then - using bazel-3.1.0 + using bazel-5.0.0 echo $(date): Build everything... bazel build :all diff --git a/kokoro/scripts/linux/build.sh b/kokoro/scripts/linux/build.sh index 4731ebdc..85d4b61a 100644 --- a/kokoro/scripts/linux/build.sh +++ b/kokoro/scripts/linux/build.sh @@ -26,7 +26,9 @@ COMPILER=$2 TOOL=$3 BUILD_SHA=${KOKORO_GITHUB_COMMIT:-$KOKORO_GITHUB_PULL_REQUEST_COMMIT} +# "--privileged" is required to run ptrace in the asan builds. docker run --rm -i \ + --privileged \ --volume "${ROOT_DIR}:${ROOT_DIR}" \ --volume "${KOKORO_ARTIFACTS_DIR}:${KOKORO_ARTIFACTS_DIR}" \ --workdir "${ROOT_DIR}" \ diff --git a/kokoro/windows-msvc-2015-release-bazel/build.bat b/kokoro/windows-msvc-2015-release-bazel/build.bat index 2f721af4..de20b0aa 100644 --- a/kokoro/windows-msvc-2015-release-bazel/build.bat +++ b/kokoro/windows-msvc-2015-release-bazel/build.bat @@ -30,14 +30,13 @@ git clone --depth=1 https://github.com/google/effcee external/effce git clone --depth=1 https://github.com/google/re2 external/re2 :: REM Install Bazel. -wget -q https://github.com/bazelbuild/bazel/releases/download/0.29.1/bazel-0.29.1-windows-x86_64.zip -unzip -q bazel-0.29.1-windows-x86_64.zip +wget -q https://github.com/bazelbuild/bazel/releases/download/5.0.0/bazel-5.0.0-windows-x86_64.zip +unzip -q bazel-5.0.0-windows-x86_64.zip :: Set up MSVC call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64 set BAZEL_VS=C:\Program Files (x86)\Microsoft Visual Studio 14.0 set BAZEL_VC=C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC -set BAZEL_SH=c:\tools\msys64\usr\bin\bash.exe set BAZEL_PYTHON=c:\tools\python2\python.exe :: ######################################### diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 331ff675..f0dcaddd 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -217,15 +217,18 @@ add_subdirectory(reduce) add_subdirectory(fuzz) add_subdirectory(link) add_subdirectory(lint) +add_subdirectory(diff) set(SPIRV_SOURCES ${spirv-tools_SOURCE_DIR}/include/spirv-tools/libspirv.h ${CMAKE_CURRENT_SOURCE_DIR}/util/bitutils.h ${CMAKE_CURRENT_SOURCE_DIR}/util/bit_vector.h + ${CMAKE_CURRENT_SOURCE_DIR}/util/hash_combine.h ${CMAKE_CURRENT_SOURCE_DIR}/util/hex_float.h ${CMAKE_CURRENT_SOURCE_DIR}/util/make_unique.h ${CMAKE_CURRENT_SOURCE_DIR}/util/parse_number.h + ${CMAKE_CURRENT_SOURCE_DIR}/util/pooled_linked_list.h ${CMAKE_CURRENT_SOURCE_DIR}/util/small_vector.h ${CMAKE_CURRENT_SOURCE_DIR}/util/string_utils.h ${CMAKE_CURRENT_SOURCE_DIR}/util/timer.h diff --git a/source/assembly_grammar.cpp b/source/assembly_grammar.cpp index 79f18eee..4f5942ab 100644 --- a/source/assembly_grammar.cpp +++ b/source/assembly_grammar.cpp @@ -62,9 +62,9 @@ spv_result_t spvTextParseMaskOperand(spv_target_env env, end = std::find(begin, text_end, separator); spv_operand_desc entry = nullptr; - if (spvOperandTableNameLookup(env, operandTable, type, begin, end - begin, - &entry)) { - return SPV_ERROR_INVALID_TEXT; + if (auto error = spvOperandTableNameLookup(env, operandTable, type, begin, + end - begin, &entry)) { + return error; } value |= entry->value; diff --git a/source/binary.cpp b/source/binary.cpp index 93d5da7a..24d32f8c 100644 --- a/source/binary.cpp +++ b/source/binary.cpp @@ -33,6 +33,7 @@ #include "source/operand.h" #include "source/spirv_constant.h" #include "source/spirv_endian.h" +#include "source/util/string_utils.h" spv_result_t spvBinaryHeaderGet(const spv_const_binary binary, const spv_endianness_t endian, @@ -62,6 +63,15 @@ spv_result_t spvBinaryHeaderGet(const spv_const_binary binary, return SPV_SUCCESS; } +std::string spvDecodeLiteralStringOperand(const spv_parsed_instruction_t& inst, + const uint16_t operand_index) { + assert(operand_index < inst.num_operands); + const spv_parsed_operand_t& operand = inst.operands[operand_index]; + + return spvtools::utils::MakeString(inst.words + operand.offset, + operand.num_words); +} + namespace { // A SPIR-V binary parser. A parser instance communicates detailed parse @@ -205,7 +215,7 @@ class Parser { size_t word_index; // The current position in words. size_t instruction_count; // The count of processed instructions spv_endianness_t endian; // The endianness of the binary. - // Is the SPIR-V binary in a different endiannes from the host native + // Is the SPIR-V binary in a different endianness from the host native // endianness? bool requires_endian_conversion; @@ -290,7 +300,7 @@ spv_result_t Parser::parseInstruction() { const uint32_t first_word = peek(); // If the module's endianness is different from the host native endianness, - // then converted_words contains the the endian-translated words in the + // then converted_words contains the endian-translated words in the // instruction. _.endian_converted_words.clear(); _.endian_converted_words.push_back(first_word); @@ -577,27 +587,18 @@ spv_result_t Parser::parseOperand(size_t inst_offset, case SPV_OPERAND_TYPE_LITERAL_STRING: case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_STRING: { - convert_operand_endianness = false; - const char* string = - reinterpret_cast<const char*>(_.words + _.word_index); - // Compute the length of the string, but make sure we don't run off the - // end of the input. - const size_t remaining_input_bytes = - sizeof(uint32_t) * (_.num_words - _.word_index); - const size_t string_num_content_bytes = - spv_strnlen_s(string, remaining_input_bytes); - // If there was no terminating null byte, then that's an end-of-input - // error. - if (string_num_content_bytes == remaining_input_bytes) + const size_t max_words = _.num_words - _.word_index; + std::string string = + spvtools::utils::MakeString(_.words + _.word_index, max_words, false); + + if (string.length() == max_words * 4) return exhaustedInputDiagnostic(inst_offset, opcode, type); - // Account for null in the word length, so add 1 for null, then add 3 to - // make sure we round up. The following is equivalent to: - // (string_num_content_bytes + 1 + 3) / 4 - const size_t string_num_words = string_num_content_bytes / 4 + 1; + // Make sure we can record the word count without overflow. // // This error can't currently be triggered because of validity // checks elsewhere. + const size_t string_num_words = string.length() / 4 + 1; if (string_num_words > std::numeric_limits<uint16_t>::max()) { return diagnostic() << "Literal string is longer than " << std::numeric_limits<uint16_t>::max() @@ -611,7 +612,7 @@ spv_result_t Parser::parseOperand(size_t inst_offset, // There is only one string literal argument to OpExtInstImport, // so it's sufficient to guard this just on the opcode. const spv_ext_inst_type_t ext_inst_type = - spvExtInstImportTypeGet(string); + spvExtInstImportTypeGet(string.c_str()); if (SPV_EXT_INST_TYPE_NONE == ext_inst_type) { return diagnostic() << "Invalid extended instruction import '" << string << "'"; diff --git a/source/binary.h b/source/binary.h index 66d24c7e..eb3beaca 100644 --- a/source/binary.h +++ b/source/binary.h @@ -15,6 +15,8 @@ #ifndef SOURCE_BINARY_H_ #define SOURCE_BINARY_H_ +#include <string> + #include "source/spirv_definition.h" #include "spirv-tools/libspirv.h" @@ -33,4 +35,9 @@ spv_result_t spvBinaryHeaderGet(const spv_const_binary binary, // replacement for C11's strnlen_s which might not exist in all environments. size_t spv_strnlen_s(const char* str, size_t strsz); +// Decode the string literal operand with index operand_index from instruction +// inst. +std::string spvDecodeLiteralStringOperand(const spv_parsed_instruction_t& inst, + const uint16_t operand_index); + #endif // SOURCE_BINARY_H_ diff --git a/source/cfa.h b/source/cfa.h index 0d09014c..7cadf55f 100644 --- a/source/cfa.h +++ b/source/cfa.h @@ -42,7 +42,7 @@ class CFA { /// Returns true if a block with @p id is found in the @p work_list vector /// - /// @param[in] work_list Set of blocks visited in the the depth first + /// @param[in] work_list Set of blocks visited in the depth first /// traversal /// of the CFG /// @param[in] id The ID of the block being checked diff --git a/source/diff/CMakeLists.txt b/source/diff/CMakeLists.txt new file mode 100644 index 00000000..1328699a --- /dev/null +++ b/source/diff/CMakeLists.txt @@ -0,0 +1,54 @@ +# Copyright (c) 2022 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. +set(SPIRV_TOOLS_DIFF_SOURCES + diff.h + lcs.h + + diff.cpp +) + +add_library(SPIRV-Tools-diff ${SPIRV_TOOLS_LIBRARY_TYPE} ${SPIRV_TOOLS_DIFF_SOURCES}) + +spvtools_default_compile_options(SPIRV-Tools-diff) +target_include_directories(SPIRV-Tools-diff + PUBLIC + $<BUILD_INTERFACE:${spirv-tools_SOURCE_DIR}/include> + $<BUILD_INTERFACE:${SPIRV_HEADER_INCLUDE_DIR}> + $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> + PRIVATE ${spirv-tools_BINARY_DIR} +) +# We need the assembling and disassembling functionalities in the main library. +target_link_libraries(SPIRV-Tools-diff + PUBLIC ${SPIRV_TOOLS_FULL_VISIBILITY}) +# We need the internals of spirv-opt. +target_link_libraries(SPIRV-Tools-diff + PUBLIC SPIRV-Tools-opt) + +set_property(TARGET SPIRV-Tools-diff PROPERTY FOLDER "SPIRV-Tools libraries") +spvtools_check_symbol_exports(SPIRV-Tools-diff) + +if(ENABLE_SPIRV_TOOLS_INSTALL) + install(TARGETS SPIRV-Tools-diff EXPORT SPIRV-Tools-diffTargets + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + export(EXPORT SPIRV-Tools-diffTargets FILE SPIRV-Tools-diffTargets.cmake) + + spvtools_config_package_dir(SPIRV-Tools-diff PACKAGE_DIR) + install(EXPORT SPIRV-Tools-diffTargets FILE SPIRV-Tools-diffTargets.cmake + DESTINATION ${PACKAGE_DIR}) + + spvtools_generate_config_file(SPIRV-Tools-diff) + install(FILES ${CMAKE_BINARY_DIR}/SPIRV-Tools-diffConfig.cmake DESTINATION ${PACKAGE_DIR}) +endif(ENABLE_SPIRV_TOOLS_INSTALL) diff --git a/source/diff/diff.cpp b/source/diff/diff.cpp new file mode 100644 index 00000000..bca31b06 --- /dev/null +++ b/source/diff/diff.cpp @@ -0,0 +1,2862 @@ +// Copyright (c) 2022 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/diff/diff.h" + +#include "source/diff/lcs.h" +#include "source/disassemble.h" +#include "source/ext_inst.h" +#include "source/latest_version_spirv_header.h" +#include "source/print.h" +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { +namespace diff { + +namespace { + +// A map from an id to the instruction that defines it. +using IdToInstructionMap = std::vector<const opt::Instruction*>; +// A map from an id to the instructions that decorate it, or name it, etc. +using IdToInfoMap = std::vector<std::vector<const opt::Instruction*>>; +// A map from an instruction to another, used for instructions without id. +using InstructionToInstructionMap = + std::unordered_map<const opt::Instruction*, const opt::Instruction*>; +// A flat list of instructions in a function for easier iteration. +using InstructionList = std::vector<const opt::Instruction*>; +// A map from a function to its list of instructions. +using FunctionInstMap = std::map<uint32_t, InstructionList>; +// A list of ids with some similar property, for example functions with the same +// name. +using IdGroup = std::vector<uint32_t>; +// A map of names to ids with the same name. This is an ordered map so +// different implementations produce identical results. +using IdGroupMapByName = std::map<std::string, IdGroup>; +using IdGroupMapByTypeId = std::map<uint32_t, IdGroup>; +using IdGroupMapByOp = std::map<SpvOp, IdGroup>; +using IdGroupMapByStorageClass = std::map<SpvStorageClass, IdGroup>; + +// A set of potential id mappings that haven't been resolved yet. Any id in src +// may map in any id in dst. Note that ids are added in the same order as they +// appear in src and dst to facilitate matching dependent instructions. For +// example, this guarantees that when matching OpTypeVector, the basic type of +// the vector is already (potentially) matched. +struct PotentialIdMap { + std::vector<uint32_t> src_ids; + std::vector<uint32_t> dst_ids; +}; + +void CompactIds(std::vector<uint32_t>& ids) { + size_t write_index = 0; + for (size_t i = 0; i < ids.size(); ++i) { + if (ids[i] != 0) { + ids[write_index++] = ids[i]; + } + } + ids.resize(write_index); +} + +// A mapping between src and dst ids. +class IdMap { + public: + IdMap(size_t id_bound) { id_map_.resize(id_bound, 0); } + + void MapIds(uint32_t from, uint32_t to) { + assert(from != 0); + assert(to != 0); + assert(from < id_map_.size()); + assert(id_map_[from] == 0); + + id_map_[from] = to; + } + + uint32_t MappedId(uint32_t from) const { + assert(from != 0); + return from < id_map_.size() ? id_map_[from] : 0; + } + const opt::Instruction* MappedInst(const opt::Instruction* from_inst) const { + assert(from_inst != nullptr); + assert(!from_inst->HasResultId()); + + auto mapped = inst_map_.find(from_inst); + if (mapped == inst_map_.end()) { + return nullptr; + } + return mapped->second; + } + + bool IsMapped(uint32_t from) const { + assert(from != 0); + return from < id_map_.size() && id_map_[from] != 0; + } + + // Map any ids in src and dst that have not been mapped to new ids in dst and + // src respectively. + void MapUnmatchedIds(IdMap& other_way); + + // Some instructions don't have result ids. Those are mapped by pointer. + void MapInsts(const opt::Instruction* from_inst, + const opt::Instruction* to_inst) { + assert(from_inst != nullptr); + assert(to_inst != nullptr); + assert(inst_map_.find(from_inst) == inst_map_.end()); + + inst_map_[from_inst] = to_inst; + } + + uint32_t IdBound() const { return static_cast<uint32_t>(id_map_.size()); } + + private: + // Given an id, returns the corresponding id in the other module, or 0 if not + // matched yet. + std::vector<uint32_t> id_map_; + + // Same for instructions that don't have an id. + InstructionToInstructionMap inst_map_; +}; + +// Two way mapping of ids. +class SrcDstIdMap { + public: + SrcDstIdMap(size_t src_id_bound, size_t dst_id_bound) + : src_to_dst_(src_id_bound), dst_to_src_(dst_id_bound) {} + + void MapIds(uint32_t src, uint32_t dst) { + src_to_dst_.MapIds(src, dst); + dst_to_src_.MapIds(dst, src); + } + + uint32_t MappedDstId(uint32_t src) { + uint32_t dst = src_to_dst_.MappedId(src); + assert(dst == 0 || dst_to_src_.MappedId(dst) == src); + return dst; + } + uint32_t MappedSrcId(uint32_t dst) { + uint32_t src = dst_to_src_.MappedId(dst); + assert(src == 0 || src_to_dst_.MappedId(src) == dst); + return src; + } + + bool IsSrcMapped(uint32_t src) { return src_to_dst_.IsMapped(src); } + bool IsDstMapped(uint32_t dst) { return dst_to_src_.IsMapped(dst); } + + // Map any ids in src and dst that have not been mapped to new ids in dst and + // src respectively. + void MapUnmatchedIds(); + + // Some instructions don't have result ids. Those are mapped by pointer. + void MapInsts(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst) { + assert(src_inst->HasResultId() == dst_inst->HasResultId()); + if (src_inst->HasResultId()) { + MapIds(src_inst->result_id(), dst_inst->result_id()); + } else { + src_to_dst_.MapInsts(src_inst, dst_inst); + dst_to_src_.MapInsts(dst_inst, src_inst); + } + } + + const IdMap& SrcToDstMap() const { return src_to_dst_; } + const IdMap& DstToSrcMap() const { return dst_to_src_; } + + private: + IdMap src_to_dst_; + IdMap dst_to_src_; +}; + +struct IdInstructions { + IdInstructions(const opt::Module* module) + : inst_map_(module->IdBound(), nullptr), + name_map_(module->IdBound()), + decoration_map_(module->IdBound()), + forward_pointer_map_(module->IdBound()) { + // Map ids from all sections to instructions that define them. + MapIdsToInstruction(module->ext_inst_imports()); + MapIdsToInstruction(module->types_values()); + for (const opt::Function& function : *module) { + function.ForEachInst( + [this](const opt::Instruction* inst) { + if (inst->HasResultId()) { + MapIdToInstruction(inst->result_id(), inst); + } + }, + true, true); + } + + // Gather decorations applied to ids that could be useful in matching them + // between src and dst modules. + MapIdsToInfos(module->debugs2()); + MapIdsToInfos(module->annotations()); + MapIdsToInfos(module->types_values()); + } + + void MapIdToInstruction(uint32_t id, const opt::Instruction* inst); + + void MapIdsToInstruction( + opt::IteratorRange<opt::Module::const_inst_iterator> section); + void MapIdsToInfos( + opt::IteratorRange<opt::Module::const_inst_iterator> section); + + IdToInstructionMap inst_map_; + IdToInfoMap name_map_; + IdToInfoMap decoration_map_; + IdToInstructionMap forward_pointer_map_; +}; + +class Differ { + public: + Differ(opt::IRContext* src, opt::IRContext* dst, std::ostream& out, + Options options) + : src_context_(src), + dst_context_(dst), + src_(src->module()), + dst_(dst->module()), + options_(options), + out_(out), + src_id_to_(src_), + dst_id_to_(dst_), + id_map_(src_->IdBound(), dst_->IdBound()) { + // Cache function bodies in canonicalization order. + GetFunctionBodies(src_context_, &src_funcs_, &src_func_insts_); + GetFunctionBodies(dst_context_, &dst_funcs_, &dst_func_insts_); + } + + // Match ids or instructions of different sections. + void MatchCapabilities(); + void MatchExtensions(); + void MatchExtInstImportIds(); + void MatchMemoryModel(); + void MatchEntryPointIds(); + void MatchExecutionModes(); + void MatchTypeForwardPointers(); + void MatchTypeIds(); + void MatchConstants(); + void MatchVariableIds(); + void MatchFunctions(); + + // Debug info and annotations are matched only after ids are matched. + void MatchDebugs1(); + void MatchDebugs2(); + void MatchDebugs3(); + void MatchExtInstDebugInfo(); + void MatchAnnotations(); + + // Output the diff. + spv_result_t Output(); + + void DumpIdMap() { + if (!options_.dump_id_map) { + return; + } + + out_ << " Src -> Dst\n"; + for (uint32_t src_id = 1; src_id < src_->IdBound(); ++src_id) { + uint32_t dst_id = id_map_.MappedDstId(src_id); + if (src_id_to_.inst_map_[src_id] != nullptr && dst_id != 0) + out_ << std::setw(4) << src_id << " -> " << std::setw(4) << dst_id + << " [" << spvOpcodeString(src_id_to_.inst_map_[src_id]->opcode()) + << "]\n"; + } + } + + private: + // Helper functions that match ids between src and dst + void PoolPotentialIds( + opt::IteratorRange<opt::Module::const_inst_iterator> section, + std::vector<uint32_t>& ids, bool is_src, + std::function<bool(const opt::Instruction&)> filter, + std::function<uint32_t(const opt::Instruction&)> get_id); + void MatchIds( + PotentialIdMap& potential, + std::function<bool(const opt::Instruction*, const opt::Instruction*)> + match); + // Helper functions that match id-less instructions between src and dst. + void MatchPreambleInstructions( + opt::IteratorRange<opt::Module::const_inst_iterator> src_insts, + opt::IteratorRange<opt::Module::const_inst_iterator> dst_insts); + InstructionList SortPreambleInstructions( + const opt::Module* module, + opt::IteratorRange<opt::Module::const_inst_iterator> insts); + int ComparePreambleInstructions(const opt::Instruction* a, + const opt::Instruction* b, + const opt::Module* src_inst_module, + const opt::Module* dst_inst_module); + // Helper functions that match debug and annotation instructions of already + // matched ids. + void MatchDebugAndAnnotationInstructions( + opt::IteratorRange<opt::Module::const_inst_iterator> src_insts, + opt::IteratorRange<opt::Module::const_inst_iterator> dst_insts); + + // Get various properties from an id. These Helper functions are passed to + // `GroupIds` and `GroupIdsAndMatch` below (as the `get_group` argument). + uint32_t GroupIdsHelperGetTypeId(const IdInstructions& id_to, uint32_t id); + SpvStorageClass GroupIdsHelperGetTypePointerStorageClass( + const IdInstructions& id_to, uint32_t id); + SpvOp GroupIdsHelperGetTypePointerTypeOp(const IdInstructions& id_to, + uint32_t id); + + // Given a list of ids, groups them based on some value. The `get_group` + // function extracts a piece of information corresponding to each id, and the + // ids are bucketed based on that (and output in `groups`). This is useful to + // attempt to match ids between src and dst only when said property is + // identical. + template <typename T> + void GroupIds(const IdGroup& ids, bool is_src, std::map<T, IdGroup>* groups, + T (Differ::*get_group)(const IdInstructions&, uint32_t)); + + // Calls GroupIds to bucket ids in src and dst based on a property returned by + // `get_group`. This function then calls `match_group` for each bucket (i.e. + // "group") with identical values for said property. + // + // For example, say src and dst ids have the following properties + // correspondingly: + // + // - src ids' properties: {id0: A, id1: A, id2: B, id3: C, id4: B} + // - dst ids' properties: {id0': B, id1': C, id2': B, id3': D, id4': B} + // + // Then `match_group` is called 2 times: + // + // - Once with: ([id2, id4], [id0', id2', id4']) corresponding to B + // - Once with: ([id3], [id2']) corresponding to C + // + // Ids corresponding to A and D cannot match based on this property. + template <typename T> + void GroupIdsAndMatch( + const IdGroup& src_ids, const IdGroup& dst_ids, T invalid_group_key, + T (Differ::*get_group)(const IdInstructions&, uint32_t), + std::function<void(const IdGroup& src_group, const IdGroup& dst_group)> + match_group); + + // Helper functions that determine if two instructions match + bool DoIdsMatch(uint32_t src_id, uint32_t dst_id); + bool DoesOperandMatch(const opt::Operand& src_operand, + const opt::Operand& dst_operand); + bool DoOperandsMatch(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst, + uint32_t in_operand_index_start, + uint32_t in_operand_count); + bool DoInstructionsMatch(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst); + bool DoIdsMatchFuzzy(uint32_t src_id, uint32_t dst_id); + bool DoesOperandMatchFuzzy(const opt::Operand& src_operand, + const opt::Operand& dst_operand); + bool DoInstructionsMatchFuzzy(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst); + bool AreIdenticalUintConstants(uint32_t src_id, uint32_t dst_id); + bool DoDebugAndAnnotationInstructionsMatch(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst); + bool AreVariablesMatchable(uint32_t src_id, uint32_t dst_id, + uint32_t flexibility); + bool MatchOpTypeStruct(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst, + uint32_t flexibility); + bool MatchOpConstant(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst, uint32_t flexibility); + bool MatchOpSpecConstant(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst); + bool MatchOpVariable(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst, uint32_t flexibility); + bool MatchPerVertexType(uint32_t src_type_id, uint32_t dst_type_id); + bool MatchPerVertexVariable(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst); + + // Helper functions for matching OpTypeForwardPointer + void MatchTypeForwardPointersByName(const IdGroup& src, const IdGroup& dst); + void MatchTypeForwardPointersByTypeOp(const IdGroup& src, const IdGroup& dst); + + // Helper functions for function matching. + using FunctionMap = std::map<uint32_t, const opt::Function*>; + + InstructionList GetFunctionBody(opt::IRContext* context, + opt::Function& function); + InstructionList GetFunctionHeader(const opt::Function& function); + void GetFunctionBodies(opt::IRContext* context, FunctionMap* functions, + FunctionInstMap* function_insts); + void GetFunctionHeaderInstructions(const opt::Module* module, + FunctionInstMap* function_insts); + void BestEffortMatchFunctions(const IdGroup& src_func_ids, + const IdGroup& dst_func_ids, + const FunctionInstMap& src_func_insts, + const FunctionInstMap& dst_func_insts); + + // Calculates the diff of two function bodies. Note that the matched + // instructions themselves may not be identical; output of exact matches + // should produce the exact instruction while inexact matches should produce a + // diff as well. + // + // Returns the similarity of the two bodies = 2*N_match / (N_src + N_dst) + void MatchFunctionParamIds(const opt::Function* src_func, + const opt::Function* dst_func); + float MatchFunctionBodies(const InstructionList& src_body, + const InstructionList& dst_body, + DiffMatch* src_match_result, + DiffMatch* dst_match_result); + void MatchIdsInFunctionBodies(const InstructionList& src_body, + const InstructionList& dst_body, + const DiffMatch& src_match_result, + const DiffMatch& dst_match_result, + uint32_t flexibility); + void MatchVariablesUsedByMatchedInstructions(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst, + uint32_t flexibility); + + // Helper functions to retrieve information pertaining to an id + const opt::Instruction* GetInst(const IdInstructions& id_to, uint32_t id); + uint32_t GetConstantUint(const IdInstructions& id_to, uint32_t constant_id); + SpvExecutionModel GetExecutionModel(const opt::Module* module, + uint32_t entry_point_id); + bool HasName(const IdInstructions& id_to, uint32_t id); + // Get the OpName associated with an id + std::string GetName(const IdInstructions& id_to, uint32_t id, bool* has_name); + // Get the OpName associated with an id, with argument types stripped for + // functions. Some tools don't encode function argument types in the OpName + // string, and this improves diff between SPIR-V from those tools and others. + std::string GetSanitizedName(const IdInstructions& id_to, uint32_t id); + uint32_t GetVarTypeId(const IdInstructions& id_to, uint32_t var_id, + SpvStorageClass* storage_class); + bool GetDecorationValue(const IdInstructions& id_to, uint32_t id, + SpvDecoration decoration, uint32_t* decoration_value); + const opt::Instruction* GetForwardPointerInst(const IdInstructions& id_to, + uint32_t id); + bool IsIntType(const IdInstructions& id_to, uint32_t type_id); + bool IsFloatType(const IdInstructions& id_to, uint32_t type_id); + bool IsConstantUint(const IdInstructions& id_to, uint32_t id); + bool IsVariable(const IdInstructions& id_to, uint32_t pointer_id); + bool IsOp(const IdInstructions& id_to, uint32_t id, SpvOp opcode); + bool IsPerVertexType(const IdInstructions& id_to, uint32_t type_id); + bool IsPerVertexVariable(const IdInstructions& id_to, uint32_t type_id); + SpvStorageClass GetPerVertexStorageClass(const opt::Module* module, + uint32_t type_id); + spv_ext_inst_type_t GetExtInstType(const IdInstructions& id_to, + uint32_t set_id); + spv_number_kind_t GetNumberKind(const IdInstructions& id_to, + const opt::Instruction& inst, + uint32_t operand_index, + uint32_t* number_bit_width); + spv_number_kind_t GetTypeNumberKind(const IdInstructions& id_to, uint32_t id, + uint32_t* number_bit_width); + + // Helper functions to output a diff line + const opt::Instruction* MappedDstInst(const opt::Instruction* src_inst); + const opt::Instruction* MappedSrcInst(const opt::Instruction* dst_inst); + const opt::Instruction* MappedInstImpl(const opt::Instruction* inst, + const IdMap& to_other, + const IdInstructions& other_id_to); + void OutputLine(std::function<bool()> are_lines_identical, + std::function<void()> output_src_line, + std::function<void()> output_dst_line); + template <typename InstList> + void OutputSection( + const InstList& src_insts, const InstList& dst_insts, + std::function<void(const opt::Instruction&, const IdInstructions&, + const opt::Instruction&)> + write_inst); + void ToParsedInstruction(const opt::Instruction& inst, + const IdInstructions& id_to, + const opt::Instruction& original_inst, + spv_parsed_instruction_t* parsed_inst, + std::vector<spv_parsed_operand_t>& parsed_operands, + std::vector<uint32_t>& inst_binary); + opt::Instruction ToMappedSrcIds(const opt::Instruction& dst_inst); + + void OutputRed() { + if (options_.color_output) out_ << spvtools::clr::red{true}; + } + void OutputGreen() { + if (options_.color_output) out_ << spvtools::clr::green{true}; + } + void OutputResetColor() { + if (options_.color_output) out_ << spvtools::clr::reset{true}; + } + + opt::IRContext* src_context_; + opt::IRContext* dst_context_; + const opt::Module* src_; + const opt::Module* dst_; + Options options_; + std::ostream& out_; + + // Helpers to look up instructions based on id. + IdInstructions src_id_to_; + IdInstructions dst_id_to_; + + // The ids that have been matched between src and dst so far. + SrcDstIdMap id_map_; + + // List of instructions in function bodies after canonicalization. Cached + // here to avoid duplicate work. More importantly, some maps use + // opt::Instruction pointers so they need to be unique. + FunctionInstMap src_func_insts_; + FunctionInstMap dst_func_insts_; + FunctionMap src_funcs_; + FunctionMap dst_funcs_; +}; + +void IdMap::MapUnmatchedIds(IdMap& other_way) { + const uint32_t src_id_bound = static_cast<uint32_t>(id_map_.size()); + const uint32_t dst_id_bound = static_cast<uint32_t>(other_way.id_map_.size()); + + uint32_t next_src_id = src_id_bound; + uint32_t next_dst_id = dst_id_bound; + + for (uint32_t src_id = 1; src_id < src_id_bound; ++src_id) { + if (!IsMapped(src_id)) { + MapIds(src_id, next_dst_id); + + other_way.id_map_.push_back(0); + other_way.MapIds(next_dst_id++, src_id); + } + } + + for (uint32_t dst_id = 1; dst_id < dst_id_bound; ++dst_id) { + if (!other_way.IsMapped(dst_id)) { + id_map_.push_back(0); + MapIds(next_src_id, dst_id); + + other_way.MapIds(dst_id, next_src_id++); + } + } +} + +void SrcDstIdMap::MapUnmatchedIds() { + src_to_dst_.MapUnmatchedIds(dst_to_src_); +} + +void IdInstructions::MapIdToInstruction(uint32_t id, + const opt::Instruction* inst) { + assert(id != 0); + assert(id < inst_map_.size()); + assert(inst_map_[id] == nullptr); + + inst_map_[id] = inst; +} + +void IdInstructions::MapIdsToInstruction( + opt::IteratorRange<opt::Module::const_inst_iterator> section) { + for (const opt::Instruction& inst : section) { + uint32_t result_id = inst.result_id(); + if (result_id == 0) { + continue; + } + + MapIdToInstruction(result_id, &inst); + } +} + +void IdInstructions::MapIdsToInfos( + opt::IteratorRange<opt::Module::const_inst_iterator> section) { + for (const opt::Instruction& inst : section) { + IdToInfoMap* info_map = nullptr; + uint32_t id_operand = 0; + + switch (inst.opcode()) { + case SpvOpName: + info_map = &name_map_; + break; + case SpvOpMemberName: + info_map = &name_map_; + break; + case SpvOpDecorate: + info_map = &decoration_map_; + break; + case SpvOpMemberDecorate: + info_map = &decoration_map_; + break; + case SpvOpTypeForwardPointer: { + uint32_t id = inst.GetSingleWordOperand(0); + assert(id != 0); + + assert(id < forward_pointer_map_.size()); + forward_pointer_map_[id] = &inst; + continue; + } + default: + // Currently unsupported instruction, don't attempt to use it for + // matching. + break; + } + + if (info_map == nullptr) { + continue; + } + + uint32_t id = inst.GetOperand(id_operand).AsId(); + assert(id != 0); + + assert(id < info_map->size()); + assert(std::find((*info_map)[id].begin(), (*info_map)[id].end(), &inst) == + (*info_map)[id].end()); + + (*info_map)[id].push_back(&inst); + } +} + +void Differ::PoolPotentialIds( + opt::IteratorRange<opt::Module::const_inst_iterator> section, + std::vector<uint32_t>& ids, bool is_src, + std::function<bool(const opt::Instruction&)> filter, + std::function<uint32_t(const opt::Instruction&)> get_id) { + for (const opt::Instruction& inst : section) { + if (!filter(inst)) { + continue; + } + + uint32_t result_id = get_id(inst); + assert(result_id != 0); + + assert(std::find(ids.begin(), ids.end(), result_id) == ids.end()); + + // Don't include ids that are already matched, for example through + // OpTypeForwardPointer. + const bool is_matched = is_src ? id_map_.IsSrcMapped(result_id) + : id_map_.IsDstMapped(result_id); + if (is_matched) { + continue; + } + + ids.push_back(result_id); + } +} + +void Differ::MatchIds( + PotentialIdMap& potential, + std::function<bool(const opt::Instruction*, const opt::Instruction*)> + match) { + for (size_t src_index = 0; src_index < potential.src_ids.size(); + ++src_index) { + for (size_t dst_index = 0; dst_index < potential.dst_ids.size(); + ++dst_index) { + const uint32_t src_id = potential.src_ids[src_index]; + const uint32_t dst_id = potential.dst_ids[dst_index]; + + if (dst_id == 0) { + // Already matched. + continue; + } + + const opt::Instruction* src_inst = src_id_to_.inst_map_[src_id]; + const opt::Instruction* dst_inst = dst_id_to_.inst_map_[dst_id]; + + if (match(src_inst, dst_inst)) { + id_map_.MapIds(src_id, dst_id); + + // Remove the ids from the potential list. + potential.src_ids[src_index] = 0; + potential.dst_ids[dst_index] = 0; + + // Find a match for the next src id. + break; + } + } + } + + // Remove matched ids to make the next iteration faster. + CompactIds(potential.src_ids); + CompactIds(potential.dst_ids); +} + +void Differ::MatchPreambleInstructions( + opt::IteratorRange<opt::Module::const_inst_iterator> src_insts, + opt::IteratorRange<opt::Module::const_inst_iterator> dst_insts) { + // First, pool all instructions from each section and sort them. + InstructionList sorted_src_insts = SortPreambleInstructions(src_, src_insts); + InstructionList sorted_dst_insts = SortPreambleInstructions(dst_, dst_insts); + + // Then walk and match them. + size_t src_cur = 0; + size_t dst_cur = 0; + + while (src_cur < sorted_src_insts.size() && + dst_cur < sorted_dst_insts.size()) { + const opt::Instruction* src_inst = sorted_src_insts[src_cur]; + const opt::Instruction* dst_inst = sorted_dst_insts[dst_cur]; + + int compare = ComparePreambleInstructions(src_inst, dst_inst, src_, dst_); + if (compare == 0) { + id_map_.MapInsts(src_inst, dst_inst); + } + if (compare <= 0) { + ++src_cur; + } + if (compare >= 0) { + ++dst_cur; + } + } +} + +InstructionList Differ::SortPreambleInstructions( + const opt::Module* module, + opt::IteratorRange<opt::Module::const_inst_iterator> insts) { + InstructionList sorted; + for (const opt::Instruction& inst : insts) { + sorted.push_back(&inst); + } + std::sort( + sorted.begin(), sorted.end(), + [this, module](const opt::Instruction* a, const opt::Instruction* b) { + return ComparePreambleInstructions(a, b, module, module) < 0; + }); + return sorted; +} + +int Differ::ComparePreambleInstructions(const opt::Instruction* a, + const opt::Instruction* b, + const opt::Module* src_inst_module, + const opt::Module* dst_inst_module) { + assert(a->opcode() == b->opcode()); + assert(!a->HasResultId()); + assert(!a->HasResultType()); + + const uint32_t a_operand_count = a->NumOperands(); + const uint32_t b_operand_count = b->NumOperands(); + + if (a_operand_count < b_operand_count) { + return -1; + } + if (a_operand_count > b_operand_count) { + return 1; + } + + // Instead of comparing OpExecutionMode entry point ids as ids, compare them + // through their corresponding execution model. This simplifies traversing + // the sorted list of instructions between src and dst modules. + if (a->opcode() == SpvOpExecutionMode) { + const SpvExecutionModel src_model = + GetExecutionModel(src_inst_module, a->GetSingleWordOperand(0)); + const SpvExecutionModel dst_model = + GetExecutionModel(dst_inst_module, b->GetSingleWordOperand(0)); + + if (src_model < dst_model) { + return -1; + } + if (src_model > dst_model) { + return 1; + } + } + + // Match every operand of the instruction. + for (uint32_t operand_index = 0; operand_index < a_operand_count; + ++operand_index) { + const opt::Operand& a_operand = a->GetOperand(operand_index); + const opt::Operand& b_operand = b->GetOperand(operand_index); + + if (a_operand.type < b_operand.type) { + return -1; + } + if (a_operand.type > b_operand.type) { + return 1; + } + + assert(a_operand.words.size() == 1); + assert(b_operand.words.size() == 1); + + switch (a_operand.type) { + case SPV_OPERAND_TYPE_ID: + // Don't compare ids, there can't be multiple instances of the + // OpExecutionMode with different ids of the same execution model. + break; + case SPV_OPERAND_TYPE_TYPE_ID: + case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID: + case SPV_OPERAND_TYPE_SCOPE_ID: + assert(false && "Unreachable"); + break; + case SPV_OPERAND_TYPE_LITERAL_STRING: { + int str_compare = + strcmp(a_operand.AsString().c_str(), b_operand.AsString().c_str()); + if (str_compare != 0) { + return str_compare; + } + break; + } + default: + // Expect literal values to match. + if (a_operand.words[0] < b_operand.words[0]) { + return -1; + } + if (a_operand.words[0] > b_operand.words[0]) { + return 1; + } + break; + } + } + + return 0; +} + +void Differ::MatchDebugAndAnnotationInstructions( + opt::IteratorRange<opt::Module::const_inst_iterator> src_insts, + opt::IteratorRange<opt::Module::const_inst_iterator> dst_insts) { + for (const opt::Instruction& src_inst : src_insts) { + for (const opt::Instruction& dst_inst : dst_insts) { + if (MappedSrcInst(&dst_inst) != nullptr) { + continue; + } + + // Map instructions as soon as they match. Debug and annotation + // instructions are matched such that there can't be multiple matches. + if (DoDebugAndAnnotationInstructionsMatch(&src_inst, &dst_inst)) { + id_map_.MapInsts(&src_inst, &dst_inst); + break; + } + } + } +} + +uint32_t Differ::GroupIdsHelperGetTypeId(const IdInstructions& id_to, + uint32_t id) { + return GetInst(id_to, id)->type_id(); +} + +SpvStorageClass Differ::GroupIdsHelperGetTypePointerStorageClass( + const IdInstructions& id_to, uint32_t id) { + const opt::Instruction* inst = GetInst(id_to, id); + assert(inst && inst->opcode() == SpvOpTypePointer); + return SpvStorageClass(inst->GetSingleWordInOperand(0)); +} + +SpvOp Differ::GroupIdsHelperGetTypePointerTypeOp(const IdInstructions& id_to, + uint32_t id) { + const opt::Instruction* inst = GetInst(id_to, id); + assert(inst && inst->opcode() == SpvOpTypePointer); + + const uint32_t type_id = inst->GetSingleWordInOperand(1); + const opt::Instruction* type_inst = GetInst(id_to, type_id); + assert(type_inst); + + return type_inst->opcode(); +} + +template <typename T> +void Differ::GroupIds(const IdGroup& ids, bool is_src, + std::map<T, IdGroup>* groups, + T (Differ::*get_group)(const IdInstructions&, uint32_t)) { + assert(groups->empty()); + + const IdInstructions& id_to = is_src ? src_id_to_ : dst_id_to_; + + for (const uint32_t id : ids) { + // Don't include ids that are already matched, for example through + // OpEntryPoint. + const bool is_matched = + is_src ? id_map_.IsSrcMapped(id) : id_map_.IsDstMapped(id); + if (is_matched) { + continue; + } + + T group = (this->*get_group)(id_to, id); + (*groups)[group].push_back(id); + } +} + +template <typename T> +void Differ::GroupIdsAndMatch( + const IdGroup& src_ids, const IdGroup& dst_ids, T invalid_group_key, + T (Differ::*get_group)(const IdInstructions&, uint32_t), + std::function<void(const IdGroup& src_group, const IdGroup& dst_group)> + match_group) { + // Group the ids based on a key (get_group) + std::map<T, IdGroup> src_groups; + std::map<T, IdGroup> dst_groups; + + GroupIds<T>(src_ids, true, &src_groups, get_group); + GroupIds<T>(dst_ids, false, &dst_groups, get_group); + + // Iterate over the groups, and match those with identical keys + for (const auto& iter : src_groups) { + const T& key = iter.first; + const IdGroup& src_group = iter.second; + + if (key == invalid_group_key) { + continue; + } + + const IdGroup& dst_group = dst_groups[key]; + + // Let the caller match the groups as appropriate. + match_group(src_group, dst_group); + } +} + +bool Differ::DoIdsMatch(uint32_t src_id, uint32_t dst_id) { + assert(dst_id != 0); + return id_map_.MappedDstId(src_id) == dst_id; +} + +bool Differ::DoesOperandMatch(const opt::Operand& src_operand, + const opt::Operand& dst_operand) { + assert(src_operand.type == dst_operand.type); + + switch (src_operand.type) { + case SPV_OPERAND_TYPE_ID: + case SPV_OPERAND_TYPE_TYPE_ID: + case SPV_OPERAND_TYPE_RESULT_ID: + case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID: + case SPV_OPERAND_TYPE_SCOPE_ID: + // Match ids only if they are already matched in the id map. + return DoIdsMatch(src_operand.AsId(), dst_operand.AsId()); + case SPV_OPERAND_TYPE_LITERAL_STRING: + return src_operand.AsString() == dst_operand.AsString(); + default: + // Otherwise expect them to match exactly. + assert(src_operand.type != SPV_OPERAND_TYPE_LITERAL_STRING); + if (src_operand.words.size() != dst_operand.words.size()) { + return false; + } + for (size_t i = 0; i < src_operand.words.size(); ++i) { + if (src_operand.words[i] != dst_operand.words[i]) { + return false; + } + } + return true; + } +} + +bool Differ::DoOperandsMatch(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst, + uint32_t in_operand_index_start, + uint32_t in_operand_count) { + // Caller should have returned early for instructions with different opcode. + assert(src_inst->opcode() == dst_inst->opcode()); + + bool match = true; + for (uint32_t i = 0; i < in_operand_count; ++i) { + const uint32_t in_operand_index = in_operand_index_start + i; + + const opt::Operand& src_operand = src_inst->GetInOperand(in_operand_index); + const opt::Operand& dst_operand = dst_inst->GetInOperand(in_operand_index); + + match = match && DoesOperandMatch(src_operand, dst_operand); + } + + return match; +} + +bool Differ::DoInstructionsMatch(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst) { + // Check whether the two instructions are identical, that is the instructions + // themselves are matched, every id is matched, and every other value is + // identical. + if (MappedDstInst(src_inst) != dst_inst) { + return false; + } + + assert(src_inst->opcode() == dst_inst->opcode()); + if (src_inst->NumOperands() != dst_inst->NumOperands()) { + return false; + } + + for (uint32_t operand_index = 0; operand_index < src_inst->NumOperands(); + ++operand_index) { + const opt::Operand& src_operand = src_inst->GetOperand(operand_index); + const opt::Operand& dst_operand = dst_inst->GetOperand(operand_index); + + if (!DoesOperandMatch(src_operand, dst_operand)) { + return false; + } + } + + return true; +} + +bool Differ::DoIdsMatchFuzzy(uint32_t src_id, uint32_t dst_id) { + assert(dst_id != 0); + const uint32_t mapped_dst_id = id_map_.MappedDstId(src_id); + + // Consider unmatched ids as a match. In function bodies, no result id is + // matched yet and thus they are excluded from instruction matching when used + // as parameters in subsequent instructions. + if (mapped_dst_id == 0 || mapped_dst_id == dst_id) { + return true; + } + + // Int and Uint constants are interchangeable, match them in that case. + if (AreIdenticalUintConstants(src_id, dst_id)) { + return true; + } + + return false; +} + +bool Differ::DoesOperandMatchFuzzy(const opt::Operand& src_operand, + const opt::Operand& dst_operand) { + if (src_operand.type != dst_operand.type) { + return false; + } + + assert(src_operand.type != SPV_OPERAND_TYPE_RESULT_ID); + assert(dst_operand.type != SPV_OPERAND_TYPE_RESULT_ID); + + switch (src_operand.type) { + case SPV_OPERAND_TYPE_ID: + case SPV_OPERAND_TYPE_TYPE_ID: + case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID: + case SPV_OPERAND_TYPE_SCOPE_ID: + // Match id operands only if they are already matched in the id map. + return DoIdsMatchFuzzy(src_operand.AsId(), dst_operand.AsId()); + default: + // Otherwise allow everything to match. + return true; + } +} + +bool Differ::DoInstructionsMatchFuzzy(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst) { + // Similar to DoOperandsMatch, but only checks that ids that have already been + // matched are identical. Ids that are unknown are allowed to match, as well + // as any non-id operand. + if (src_inst->opcode() != dst_inst->opcode()) { + return false; + } + // For external instructions, make sure the set and opcode of the external + // instruction matches too. + if (src_inst->opcode() == SpvOpExtInst) { + if (!DoOperandsMatch(src_inst, dst_inst, 0, 2)) { + return false; + } + } + + assert(src_inst->HasResultType() == dst_inst->HasResultType()); + if (src_inst->HasResultType() && + !DoIdsMatchFuzzy(src_inst->type_id(), dst_inst->type_id())) { + return false; + } + + // TODO: allow some instructions to match with different instruction lengths, + // for example OpImage* with additional operands. + if (src_inst->NumInOperandWords() != dst_inst->NumInOperandWords()) { + return false; + } + + bool match = true; + for (uint32_t in_operand_index = 0; + in_operand_index < src_inst->NumInOperandWords(); ++in_operand_index) { + const opt::Operand& src_operand = src_inst->GetInOperand(in_operand_index); + const opt::Operand& dst_operand = dst_inst->GetInOperand(in_operand_index); + + match = match && DoesOperandMatchFuzzy(src_operand, dst_operand); + } + + return match; +} + +bool Differ::AreIdenticalUintConstants(uint32_t src_id, uint32_t dst_id) { + return IsConstantUint(src_id_to_, src_id) && + IsConstantUint(dst_id_to_, dst_id) && + GetConstantUint(src_id_to_, src_id) == + GetConstantUint(dst_id_to_, dst_id); +} + +bool Differ::DoDebugAndAnnotationInstructionsMatch( + const opt::Instruction* src_inst, const opt::Instruction* dst_inst) { + if (src_inst->opcode() != dst_inst->opcode()) { + return false; + } + + switch (src_inst->opcode()) { + case SpvOpString: + case SpvOpSourceExtension: + case SpvOpModuleProcessed: + return DoesOperandMatch(src_inst->GetOperand(0), dst_inst->GetOperand(0)); + case SpvOpSource: + return DoOperandsMatch(src_inst, dst_inst, 0, 2); + case SpvOpSourceContinued: + return true; + case SpvOpName: + return DoOperandsMatch(src_inst, dst_inst, 0, 1); + case SpvOpMemberName: + return DoOperandsMatch(src_inst, dst_inst, 0, 2); + case SpvOpDecorate: + return DoOperandsMatch(src_inst, dst_inst, 0, 2); + case SpvOpMemberDecorate: + return DoOperandsMatch(src_inst, dst_inst, 0, 3); + case SpvOpExtInst: + case SpvOpDecorationGroup: + case SpvOpGroupDecorate: + case SpvOpGroupMemberDecorate: + return false; + default: + return false; + } +} + +bool Differ::AreVariablesMatchable(uint32_t src_id, uint32_t dst_id, + uint32_t flexibility) { + // Variables must match by their built-in decorations. + uint32_t src_built_in_decoration = 0, dst_built_in_decoration = 0; + const bool src_is_built_in = GetDecorationValue( + src_id_to_, src_id, SpvDecorationBuiltIn, &src_built_in_decoration); + const bool dst_is_built_in = GetDecorationValue( + dst_id_to_, dst_id, SpvDecorationBuiltIn, &dst_built_in_decoration); + + if (src_is_built_in != dst_is_built_in) { + return false; + } + if (src_is_built_in && src_built_in_decoration != dst_built_in_decoration) { + return false; + } + + // Check their types and storage classes. + SpvStorageClass src_storage_class, dst_storage_class; + const uint32_t src_type_id = + GetVarTypeId(src_id_to_, src_id, &src_storage_class); + const uint32_t dst_type_id = + GetVarTypeId(dst_id_to_, dst_id, &dst_storage_class); + + if (!DoIdsMatch(src_type_id, dst_type_id)) { + return false; + } + switch (flexibility) { + case 0: + if (src_storage_class != dst_storage_class) { + return false; + } + break; + case 1: + if (src_storage_class != dst_storage_class) { + // Allow one of the two to be Private while the other is Input or + // Output, this allows matching in/out variables that have been turned + // global as part of linking two stages (as done in ANGLE). + const bool src_is_io = src_storage_class == SpvStorageClassInput || + src_storage_class == SpvStorageClassOutput; + const bool dst_is_io = dst_storage_class == SpvStorageClassInput || + dst_storage_class == SpvStorageClassOutput; + const bool src_is_private = src_storage_class == SpvStorageClassPrivate; + const bool dst_is_private = dst_storage_class == SpvStorageClassPrivate; + + if (!((src_is_io && dst_is_private) || (src_is_private && dst_is_io))) { + return false; + } + } + break; + default: + assert(false && "Unreachable"); + return false; + } + + // TODO: Is there any other way to check compatiblity of the variables? It's + // easy to tell when the variables definitely don't match, but there's little + // information that can be used for a definite match. + return true; +} + +bool Differ::MatchOpTypeStruct(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst, + uint32_t flexibility) { + const uint32_t src_type_id = src_inst->result_id(); + const uint32_t dst_type_id = dst_inst->result_id(); + + bool src_has_name = false, dst_has_name = false; + std::string src_name = GetName(src_id_to_, src_type_id, &src_has_name); + std::string dst_name = GetName(dst_id_to_, dst_type_id, &dst_has_name); + + // If debug info is present, always match the structs by name. + if (src_has_name && dst_has_name) { + if (src_name != dst_name) { + return false; + } + + // For gl_PerVertex, find the type pointer of this type (array) and make + // sure the storage classes of src and dst match; geometry and tessellation + // shaders have two instances of gl_PerVertex. + if (src_name == "gl_PerVertex") { + return MatchPerVertexType(src_type_id, dst_type_id); + } + + return true; + } + + // If debug info is not present, match the structs by their type. + + // For gl_PerVertex, find the type pointer of this type (array) and match by + // storage class. The gl_PerVertex struct is itself found by the BuiltIn + // decorations applied to its members. + const bool src_is_per_vertex = IsPerVertexType(src_id_to_, src_type_id); + const bool dst_is_per_vertex = IsPerVertexType(dst_id_to_, dst_type_id); + if (src_is_per_vertex != dst_is_per_vertex) { + return false; + } + + if (src_is_per_vertex) { + return MatchPerVertexType(src_type_id, dst_type_id); + } + + switch (flexibility) { + case 0: + if (src_inst->NumInOperandWords() != dst_inst->NumInOperandWords()) { + return false; + } + return DoOperandsMatch(src_inst, dst_inst, 0, + src_inst->NumInOperandWords()); + case 1: + // TODO: match by taking a diff of the fields, and see if there's a >75% + // match. Need to then make sure OpMemberName, OpMemberDecorate, + // OpAccessChain etc are aware of the struct field matching. + return false; + default: + assert(false && "Unreachable"); + return false; + } +} + +bool Differ::MatchOpConstant(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst, + uint32_t flexibility) { + // The constants' type must match. In flexibility == 1, match constants of + // int and uint, as they are generally interchangeable. + switch (flexibility) { + case 0: + if (!DoesOperandMatch(src_inst->GetOperand(0), dst_inst->GetOperand(0))) { + return false; + } + break; + case 1: + if (!IsIntType(src_id_to_, src_inst->type_id()) || + !IsIntType(dst_id_to_, dst_inst->type_id())) { + return false; + } + break; + default: + assert(false && "Unreachable"); + return false; + } + + const opt::Operand& src_value_operand = src_inst->GetOperand(2); + const opt::Operand& dst_value_operand = dst_inst->GetOperand(2); + + const uint64_t src_value = src_value_operand.AsLiteralUint64(); + const uint64_t dst_value = dst_value_operand.AsLiteralUint64(); + + // If values are identical, it's a match. + if (src_value == dst_value) { + return true; + } + + // Otherwise, only allow flexibility for float types. + if (IsFloatType(src_id_to_, src_inst->type_id()) && flexibility == 1) { + // Tolerance is: + // + // - For float: allow 4 bits of mantissa as error + // - For double: allow 6 bits of mantissa as error + // + // TODO: the above values are arbitrary and a placeholder; investigate the + // amount of error resulting from using `printf("%f", f)` and `printf("%lf", + // d)` and having glslang parse them. + const uint64_t tolerance = src_value_operand.words.size() == 1 ? 16 : 64; + return src_value - dst_value < tolerance || + dst_value - src_value < tolerance; + } + + return false; +} + +bool Differ::MatchOpSpecConstant(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst) { + const uint32_t src_id = src_inst->result_id(); + const uint32_t dst_id = dst_inst->result_id(); + + bool src_has_name = false, dst_has_name = false; + std::string src_name = GetName(src_id_to_, src_id, &src_has_name); + std::string dst_name = GetName(dst_id_to_, dst_id, &dst_has_name); + + // If debug info is present, always match the spec consts by name. + if (src_has_name && dst_has_name) { + return src_name == dst_name; + } + + // Otherwise, match them by SpecId. + uint32_t src_spec_id, dst_spec_id; + + if (GetDecorationValue(src_id_to_, src_id, SpvDecorationSpecId, + &src_spec_id) && + GetDecorationValue(dst_id_to_, dst_id, SpvDecorationSpecId, + &dst_spec_id)) { + return src_spec_id == dst_spec_id; + } + + // There is no spec id, this is not valid. + assert(false && "Unreachable"); + return false; +} + +bool Differ::MatchOpVariable(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst, + uint32_t flexibility) { + const uint32_t src_id = src_inst->result_id(); + const uint32_t dst_id = dst_inst->result_id(); + + const bool src_is_pervertex = IsPerVertexVariable(src_id_to_, src_id); + const bool dst_is_pervertex = IsPerVertexVariable(dst_id_to_, dst_id); + + // For gl_PerVertex, make sure the input and output instances are matched + // correctly. + if (src_is_pervertex != dst_is_pervertex) { + return false; + } + if (src_is_pervertex) { + return MatchPerVertexVariable(src_inst, dst_inst); + } + + bool src_has_name = false, dst_has_name = false; + std::string src_name = GetName(src_id_to_, src_id, &src_has_name); + std::string dst_name = GetName(dst_id_to_, dst_id, &dst_has_name); + + // If debug info is present, always match the variables by name. + if (src_has_name && dst_has_name) { + return src_name == dst_name; + } + + // If debug info is not present, see if the variables can be matched by their + // built-in decorations. + uint32_t src_built_in_decoration; + const bool src_is_built_in = GetDecorationValue( + src_id_to_, src_id, SpvDecorationBuiltIn, &src_built_in_decoration); + + if (src_is_built_in && AreVariablesMatchable(src_id, dst_id, flexibility)) { + return true; + } + + SpvStorageClass src_storage_class, dst_storage_class; + GetVarTypeId(src_id_to_, src_id, &src_storage_class); + GetVarTypeId(dst_id_to_, dst_id, &dst_storage_class); + + if (src_storage_class != dst_storage_class) { + return false; + } + + // If variables are decorated with set/binding, match by the value of those + // decorations. + if (!options_.ignore_set_binding) { + uint32_t src_set = 0, dst_set = 0; + uint32_t src_binding = 0, dst_binding = 0; + + const bool src_has_set = GetDecorationValue( + src_id_to_, src_id, SpvDecorationDescriptorSet, &src_set); + const bool dst_has_set = GetDecorationValue( + dst_id_to_, dst_id, SpvDecorationDescriptorSet, &dst_set); + const bool src_has_binding = + GetDecorationValue(src_id_to_, src_id, SpvDecorationBinding, &src_set); + const bool dst_has_binding = + GetDecorationValue(dst_id_to_, dst_id, SpvDecorationBinding, &dst_set); + + if (src_has_set && dst_has_set && src_has_binding && dst_has_binding) { + return src_set == dst_set && src_binding == dst_binding; + } + } + + // If variables are decorated with location, match by the value of that + // decoration. + if (!options_.ignore_location) { + uint32_t src_location, dst_location; + + const bool src_has_location = GetDecorationValue( + src_id_to_, src_id, SpvDecorationLocation, &src_location); + const bool dst_has_location = GetDecorationValue( + dst_id_to_, dst_id, SpvDecorationLocation, &dst_location); + + if (src_has_location && dst_has_location) { + return src_location == dst_location; + } + } + + // Currently, there's no other way to match variables. + return false; +} + +bool Differ::MatchPerVertexType(uint32_t src_type_id, uint32_t dst_type_id) { + // For gl_PerVertex, find the type pointer of this type (array) and make sure + // the storage classes of src and dst match; geometry and tessellation shaders + // have two instances of gl_PerVertex. + SpvStorageClass src_storage_class = + GetPerVertexStorageClass(src_, src_type_id); + SpvStorageClass dst_storage_class = + GetPerVertexStorageClass(dst_, dst_type_id); + + assert(src_storage_class == SpvStorageClassInput || + src_storage_class == SpvStorageClassOutput); + assert(dst_storage_class == SpvStorageClassInput || + dst_storage_class == SpvStorageClassOutput); + + return src_storage_class == dst_storage_class; +} + +bool Differ::MatchPerVertexVariable(const opt::Instruction* src_inst, + const opt::Instruction* dst_inst) { + SpvStorageClass src_storage_class = + SpvStorageClass(src_inst->GetSingleWordInOperand(0)); + SpvStorageClass dst_storage_class = + SpvStorageClass(dst_inst->GetSingleWordInOperand(0)); + + return src_storage_class == dst_storage_class; +} + +void Differ::MatchTypeForwardPointersByName(const IdGroup& src, + const IdGroup& dst) { + // Given two sets of compatible groups of OpTypeForwardPointer instructions, + // attempts to match them by name. + + // Group them by debug info and loop over them. + GroupIdsAndMatch<std::string>( + src, dst, "", &Differ::GetSanitizedName, + [this](const IdGroup& src_group, const IdGroup& dst_group) { + + // Match only if there's a unique forward declaration with this debug + // name. + if (src_group.size() == 1 && dst_group.size() == 1) { + id_map_.MapIds(src_group[0], dst_group[0]); + } + }); +} + +void Differ::MatchTypeForwardPointersByTypeOp(const IdGroup& src, + const IdGroup& dst) { + // Given two sets of compatible groups of OpTypeForwardPointer instructions, + // attempts to match them by type op. Must be called after + // MatchTypeForwardPointersByName to match as many as possible by debug info. + + // Remove ids that are matched with debug info in + // MatchTypeForwardPointersByName. + IdGroup src_unmatched_ids; + IdGroup dst_unmatched_ids; + + std::copy_if(src.begin(), src.end(), std::back_inserter(src_unmatched_ids), + [this](uint32_t id) { return !id_map_.IsSrcMapped(id); }); + std::copy_if(dst.begin(), dst.end(), std::back_inserter(dst_unmatched_ids), + [this](uint32_t id) { return !id_map_.IsDstMapped(id); }); + + // Match only if there's a unique forward declaration with this + // storage class and type opcode. If both have debug info, they + // must not have been matchable. + if (src_unmatched_ids.size() == 1 && dst_unmatched_ids.size() == 1) { + uint32_t src_id = src_unmatched_ids[0]; + uint32_t dst_id = dst_unmatched_ids[0]; + if (!HasName(src_id_to_, src_id) || !HasName(dst_id_to_, dst_id)) { + id_map_.MapIds(src_id, dst_id); + } + } +} + +InstructionList Differ::GetFunctionBody(opt::IRContext* context, + opt::Function& function) { + // Canonicalize the blocks of the function to produce better diff, for example + // to not produce any diff if the src and dst have the same switch/case blocks + // but with the cases simply reordered. + std::list<opt::BasicBlock*> order; + context->cfg()->ComputeStructuredOrder(&function, &*function.begin(), &order); + + // Go over the instructions of the function and add the instructions to a flat + // list to simplify future iterations. + InstructionList body; + for (opt::BasicBlock* block : order) { + block->ForEachInst( + [&body](const opt::Instruction* inst) { body.push_back(inst); }, true); + } + body.push_back(function.EndInst()); + + return body; +} + +InstructionList Differ::GetFunctionHeader(const opt::Function& function) { + // Go over the instructions of the function and add the header instructions to + // a flat list to simplify diff generation. + InstructionList body; + function.WhileEachInst( + [&body](const opt::Instruction* inst) { + if (inst->opcode() == SpvOpLabel) { + return false; + } + body.push_back(inst); + return true; + }, + true, true); + + return body; +} + +void Differ::GetFunctionBodies(opt::IRContext* context, FunctionMap* functions, + FunctionInstMap* function_insts) { + for (opt::Function& function : *context->module()) { + uint32_t id = function.result_id(); + assert(functions->find(id) == functions->end()); + assert(function_insts->find(id) == function_insts->end()); + + (*functions)[id] = &function; + + InstructionList body = GetFunctionBody(context, function); + (*function_insts)[id] = std::move(body); + } +} + +void Differ::GetFunctionHeaderInstructions(const opt::Module* module, + FunctionInstMap* function_insts) { + for (opt::Function& function : *module) { + InstructionList body = GetFunctionHeader(function); + (*function_insts)[function.result_id()] = std::move(body); + } +} + +void Differ::BestEffortMatchFunctions(const IdGroup& src_func_ids, + const IdGroup& dst_func_ids, + const FunctionInstMap& src_func_insts, + const FunctionInstMap& dst_func_insts) { + struct MatchResult { + uint32_t src_id; + uint32_t dst_id; + DiffMatch src_match; + DiffMatch dst_match; + float match_rate; + bool operator<(const MatchResult& other) const { + return match_rate > other.match_rate; + } + }; + std::vector<MatchResult> all_match_results; + + for (const uint32_t src_func_id : src_func_ids) { + if (id_map_.IsSrcMapped(src_func_id)) { + continue; + } + const std::string src_name = GetSanitizedName(src_id_to_, src_func_id); + + for (const uint32_t dst_func_id : dst_func_ids) { + if (id_map_.IsDstMapped(dst_func_id)) { + continue; + } + + // Don't match functions that are named, but the names are different. + const std::string dst_name = GetSanitizedName(dst_id_to_, dst_func_id); + if (src_name != "" && dst_name != "" && src_name != dst_name) { + continue; + } + + DiffMatch src_match_result, dst_match_result; + float match_rate = MatchFunctionBodies( + src_func_insts.at(src_func_id), dst_func_insts.at(dst_func_id), + &src_match_result, &dst_match_result); + + // Only consider the functions a match if there's at least 60% match. + // This is an arbitrary limit that should be tuned. + constexpr float pass_match_rate = 0.6f; + if (match_rate >= pass_match_rate) { + all_match_results.emplace_back( + MatchResult{src_func_id, dst_func_id, std::move(src_match_result), + std::move(dst_match_result), match_rate}); + } + } + } + + std::sort(all_match_results.begin(), all_match_results.end()); + + for (const MatchResult& match_result : all_match_results) { + if (id_map_.IsSrcMapped(match_result.src_id) || + id_map_.IsDstMapped(match_result.dst_id)) { + continue; + } + + id_map_.MapIds(match_result.src_id, match_result.dst_id); + + MatchIdsInFunctionBodies(src_func_insts.at(match_result.src_id), + dst_func_insts.at(match_result.dst_id), + match_result.src_match, match_result.dst_match, 0); + } +} + +void Differ::MatchFunctionParamIds(const opt::Function* src_func, + const opt::Function* dst_func) { + IdGroup src_params; + IdGroup dst_params; + src_func->ForEachParam( + [&src_params](const opt::Instruction* param) { + src_params.push_back(param->result_id()); + }, + false); + dst_func->ForEachParam( + [&dst_params](const opt::Instruction* param) { + dst_params.push_back(param->result_id()); + }, + false); + + GroupIdsAndMatch<std::string>( + src_params, dst_params, "", &Differ::GetSanitizedName, + [this](const IdGroup& src_group, const IdGroup& dst_group) { + + // There shouldn't be two parameters with the same name, so the ids + // should match. There is nothing restricting the SPIR-V however to have + // two parameters with the same name, so be resilient against that. + if (src_group.size() == 1 && dst_group.size() == 1) { + id_map_.MapIds(src_group[0], dst_group[0]); + } + }); + + // Then match the parameters by their type. If there are multiple of them, + // match them by their order. + GroupIdsAndMatch<uint32_t>( + src_params, dst_params, 0, &Differ::GroupIdsHelperGetTypeId, + [this](const IdGroup& src_group_by_type_id, + const IdGroup& dst_group_by_type_id) { + + const size_t shared_param_count = + std::min(src_group_by_type_id.size(), dst_group_by_type_id.size()); + + for (size_t param_index = 0; param_index < shared_param_count; + ++param_index) { + id_map_.MapIds(src_group_by_type_id[0], dst_group_by_type_id[0]); + } + }); +} + +float Differ::MatchFunctionBodies(const InstructionList& src_body, + const InstructionList& dst_body, + DiffMatch* src_match_result, + DiffMatch* dst_match_result) { + LongestCommonSubsequence<std::vector<const opt::Instruction*>> lcs(src_body, + dst_body); + + uint32_t best_match_length = lcs.Get<const opt::Instruction*>( + [this](const opt::Instruction* src_inst, + const opt::Instruction* dst_inst) { + return DoInstructionsMatchFuzzy(src_inst, dst_inst); + }, + src_match_result, dst_match_result); + + // TODO: take the gaps in between matches and match those again with a relaxed + // instruction-and-type-only comparison. This can produce a better diff for + // example if an array index is changed, causing the OpAccessChain id to not + // match and subsequently every operation that's derived from that id. + // Usually this mismatch cascades until the next OpStore which doesn't produce + // an id. + + return static_cast<float>(best_match_length) * 2.0f / + static_cast<float>(src_body.size() + dst_body.size()); +} + +void Differ::MatchIdsInFunctionBodies(const InstructionList& src_body, + const InstructionList& dst_body, + const DiffMatch& src_match_result, + const DiffMatch& dst_match_result, + uint32_t flexibility) { + size_t src_cur = 0; + size_t dst_cur = 0; + + while (src_cur < src_body.size() && dst_cur < dst_body.size()) { + if (src_match_result[src_cur] && dst_match_result[dst_cur]) { + // Match instructions the src and dst instructions. + // + // TODO: count the matchings between variables discovered this way and + // choose the "best match" after all functions have been diffed and all + // instructions analyzed. + const opt::Instruction* src_inst = src_body[src_cur++]; + const opt::Instruction* dst_inst = dst_body[dst_cur++]; + + // Record the matching between the instructions. This is done only once + // (hence flexibility == 0). Calls with non-zero flexibility values will + // only deal with matching other ids based on the operands. + if (flexibility == 0) { + id_map_.MapInsts(src_inst, dst_inst); + } + + // Match any unmatched variables referenced by the instructions. + MatchVariablesUsedByMatchedInstructions(src_inst, dst_inst, flexibility); + continue; + } + if (!src_match_result[src_cur]) { + ++src_cur; + } + if (!dst_match_result[dst_cur]) { + ++dst_cur; + } + } +} + +void Differ::MatchVariablesUsedByMatchedInstructions( + const opt::Instruction* src_inst, const opt::Instruction* dst_inst, + uint32_t flexibility) { + // For OpAccessChain, OpLoad and OpStore instructions that reference unmatched + // variables, match them as a best effort. + assert(src_inst->opcode() == dst_inst->opcode()); + switch (src_inst->opcode()) { + default: + // TODO: match functions based on OpFunctionCall? + break; + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: + case SpvOpPtrAccessChain: + case SpvOpInBoundsPtrAccessChain: + case SpvOpLoad: + case SpvOpStore: + const uint32_t src_pointer_id = src_inst->GetSingleWordInOperand(0); + const uint32_t dst_pointer_id = dst_inst->GetSingleWordInOperand(0); + if (IsVariable(src_id_to_, src_pointer_id) && + IsVariable(dst_id_to_, dst_pointer_id) && + !id_map_.IsSrcMapped(src_pointer_id) && + !id_map_.IsDstMapped(dst_pointer_id) && + AreVariablesMatchable(src_pointer_id, dst_pointer_id, flexibility)) { + id_map_.MapIds(src_pointer_id, dst_pointer_id); + } + break; + } +} + +const opt::Instruction* Differ::GetInst(const IdInstructions& id_to, + uint32_t id) { + assert(id != 0); + assert(id < id_to.inst_map_.size()); + + const opt::Instruction* inst = id_to.inst_map_[id]; + assert(inst != nullptr); + + return inst; +} + +uint32_t Differ::GetConstantUint(const IdInstructions& id_to, + uint32_t constant_id) { + const opt::Instruction* constant_inst = GetInst(id_to, constant_id); + assert(constant_inst->opcode() == SpvOpConstant); + assert(GetInst(id_to, constant_inst->type_id())->opcode() == SpvOpTypeInt); + + return constant_inst->GetSingleWordInOperand(0); +} + +SpvExecutionModel Differ::GetExecutionModel(const opt::Module* module, + uint32_t entry_point_id) { + for (const opt::Instruction& inst : module->entry_points()) { + assert(inst.opcode() == SpvOpEntryPoint); + if (inst.GetSingleWordOperand(1) == entry_point_id) { + return SpvExecutionModel(inst.GetSingleWordOperand(0)); + } + } + + assert(false && "Unreachable"); + return SpvExecutionModel(0xFFF); +} + +bool Differ::HasName(const IdInstructions& id_to, uint32_t id) { + assert(id != 0); + assert(id < id_to.name_map_.size()); + + for (const opt::Instruction* inst : id_to.name_map_[id]) { + if (inst->opcode() == SpvOpName) { + return true; + } + } + + return false; +} + +std::string Differ::GetName(const IdInstructions& id_to, uint32_t id, + bool* has_name) { + assert(id != 0); + assert(id < id_to.name_map_.size()); + + for (const opt::Instruction* inst : id_to.name_map_[id]) { + if (inst->opcode() == SpvOpName) { + *has_name = true; + return inst->GetOperand(1).AsString(); + } + } + + *has_name = false; + return ""; +} + +std::string Differ::GetSanitizedName(const IdInstructions& id_to, uint32_t id) { + bool has_name = false; + std::string name = GetName(id_to, id, &has_name); + + if (!has_name) { + return ""; + } + + // Remove args from the name, in case this is a function name + return name.substr(0, name.find('(')); +} + +uint32_t Differ::GetVarTypeId(const IdInstructions& id_to, uint32_t var_id, + SpvStorageClass* storage_class) { + const opt::Instruction* var_inst = GetInst(id_to, var_id); + assert(var_inst->opcode() == SpvOpVariable); + + *storage_class = SpvStorageClass(var_inst->GetSingleWordInOperand(0)); + + // Get the type pointer from the variable. + const uint32_t type_pointer_id = var_inst->type_id(); + const opt::Instruction* type_pointer_inst = GetInst(id_to, type_pointer_id); + + // Get the type from the type pointer. + return type_pointer_inst->GetSingleWordInOperand(1); +} + +bool Differ::GetDecorationValue(const IdInstructions& id_to, uint32_t id, + SpvDecoration decoration, + uint32_t* decoration_value) { + assert(id != 0); + assert(id < id_to.decoration_map_.size()); + + for (const opt::Instruction* inst : id_to.decoration_map_[id]) { + if (inst->opcode() == SpvOpDecorate && + inst->GetSingleWordOperand(0) == id && + inst->GetSingleWordOperand(1) == decoration) { + *decoration_value = inst->GetSingleWordOperand(2); + return true; + } + } + + return false; +} + +const opt::Instruction* Differ::GetForwardPointerInst( + const IdInstructions& id_to, uint32_t id) { + assert(id != 0); + assert(id < id_to.forward_pointer_map_.size()); + return id_to.forward_pointer_map_[id]; +} + +bool Differ::IsIntType(const IdInstructions& id_to, uint32_t type_id) { + return IsOp(id_to, type_id, SpvOpTypeInt); +} + +bool Differ::IsFloatType(const IdInstructions& id_to, uint32_t type_id) { + return IsOp(id_to, type_id, SpvOpTypeFloat); +} + +bool Differ::IsConstantUint(const IdInstructions& id_to, uint32_t id) { + const opt::Instruction* constant_inst = GetInst(id_to, id); + if (constant_inst->opcode() != SpvOpConstant) { + return false; + } + + const opt::Instruction* type_inst = GetInst(id_to, constant_inst->type_id()); + return type_inst->opcode() == SpvOpTypeInt; +} + +bool Differ::IsVariable(const IdInstructions& id_to, uint32_t pointer_id) { + return IsOp(id_to, pointer_id, SpvOpVariable); +} + +bool Differ::IsOp(const IdInstructions& id_to, uint32_t id, SpvOp op) { + return GetInst(id_to, id)->opcode() == op; +} + +bool Differ::IsPerVertexType(const IdInstructions& id_to, uint32_t type_id) { + assert(type_id != 0); + assert(type_id < id_to.decoration_map_.size()); + + for (const opt::Instruction* inst : id_to.decoration_map_[type_id]) { + if (inst->opcode() == SpvOpMemberDecorate && + inst->GetSingleWordOperand(0) == type_id && + inst->GetSingleWordOperand(2) == SpvDecorationBuiltIn) { + SpvBuiltIn built_in = SpvBuiltIn(inst->GetSingleWordOperand(3)); + + // Only gl_PerVertex can have, and it can only have, the following + // built-in decorations. + return built_in == SpvBuiltInPosition || + built_in == SpvBuiltInPointSize || + built_in == SpvBuiltInClipDistance || + built_in == SpvBuiltInCullDistance; + } + } + + return false; +} + +bool Differ::IsPerVertexVariable(const IdInstructions& id_to, uint32_t var_id) { + // Get the type from the type pointer. + SpvStorageClass storage_class; + uint32_t type_id = GetVarTypeId(id_to, var_id, &storage_class); + const opt::Instruction* type_inst = GetInst(id_to, type_id); + + // If array, get the element type. + if (type_inst->opcode() == SpvOpTypeArray) { + type_id = type_inst->GetSingleWordInOperand(0); + } + + // Now check if the type is gl_PerVertex. + return IsPerVertexType(id_to, type_id); +} + +SpvStorageClass Differ::GetPerVertexStorageClass(const opt::Module* module, + uint32_t type_id) { + for (const opt::Instruction& inst : module->types_values()) { + switch (inst.opcode()) { + case SpvOpTypeArray: + // The gl_PerVertex instance could be an array, look for a variable of + // the array type instead. + if (inst.GetSingleWordInOperand(0) == type_id) { + type_id = inst.result_id(); + } + break; + case SpvOpTypePointer: + // Find the storage class of the pointer to this type. + if (inst.GetSingleWordInOperand(1) == type_id) { + return SpvStorageClass(inst.GetSingleWordInOperand(0)); + } + break; + default: + break; + } + } + + // gl_PerVertex is declared, but is unused. Return either of Input or Output + // classes just so it matches one in the other module. This should be highly + // unlikely, perhaps except for ancient GS-used-to-emulate-CS scenarios. + return SpvStorageClassOutput; +} + +spv_ext_inst_type_t Differ::GetExtInstType(const IdInstructions& id_to, + uint32_t set_id) { + const opt::Instruction* set_inst = GetInst(id_to, set_id); + return spvExtInstImportTypeGet(set_inst->GetInOperand(0).AsString().c_str()); +} + +spv_number_kind_t Differ::GetNumberKind(const IdInstructions& id_to, + const opt::Instruction& inst, + uint32_t operand_index, + uint32_t* number_bit_width) { + const opt::Operand& operand = inst.GetOperand(operand_index); + *number_bit_width = 0; + + // A very limited version of Parser::parseOperand. + switch (operand.type) { + case SPV_OPERAND_TYPE_LITERAL_INTEGER: + case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER: + // Always unsigned integers. + *number_bit_width = 32; + return SPV_NUMBER_UNSIGNED_INT; + case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER: + case SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER: + switch (inst.opcode()) { + case SpvOpSwitch: + case SpvOpConstant: + case SpvOpSpecConstant: + // Same kind of number as the selector (OpSwitch) or the type + // (Op*Constant). + return GetTypeNumberKind(id_to, inst.GetSingleWordOperand(0), + number_bit_width); + default: + assert(false && "Unreachable"); + break; + } + break; + default: + break; + } + + return SPV_NUMBER_NONE; +} + +spv_number_kind_t Differ::GetTypeNumberKind(const IdInstructions& id_to, + uint32_t id, + uint32_t* number_bit_width) { + const opt::Instruction* type_inst = GetInst(id_to, id); + if (!spvOpcodeIsScalarType(type_inst->opcode())) { + type_inst = GetInst(id_to, type_inst->type_id()); + } + + switch (type_inst->opcode()) { + case SpvOpTypeInt: + *number_bit_width = type_inst->GetSingleWordOperand(1); + return type_inst->GetSingleWordOperand(2) == 0 ? SPV_NUMBER_UNSIGNED_INT + : SPV_NUMBER_SIGNED_INT; + break; + case SpvOpTypeFloat: + *number_bit_width = type_inst->GetSingleWordOperand(1); + return SPV_NUMBER_FLOATING; + default: + assert(false && "Unreachable"); + return SPV_NUMBER_NONE; + } +} + +void Differ::MatchCapabilities() { + MatchPreambleInstructions(src_->capabilities(), dst_->capabilities()); +} + +void Differ::MatchExtensions() { + MatchPreambleInstructions(src_->extensions(), dst_->extensions()); +} + +void Differ::MatchExtInstImportIds() { + // Bunch all of this section's ids as potential matches. + PotentialIdMap potential_id_map; + auto get_result_id = [](const opt::Instruction& inst) { + return inst.result_id(); + }; + auto accept_all = [](const opt::Instruction&) { return true; }; + + PoolPotentialIds(src_->ext_inst_imports(), potential_id_map.src_ids, true, + accept_all, get_result_id); + PoolPotentialIds(dst_->ext_inst_imports(), potential_id_map.dst_ids, false, + accept_all, get_result_id); + + // Then match the ids. + MatchIds(potential_id_map, [](const opt::Instruction* src_inst, + const opt::Instruction* dst_inst) { + // Match OpExtInstImport by exact name, which is operand 1 + const opt::Operand& src_name = src_inst->GetOperand(1); + const opt::Operand& dst_name = dst_inst->GetOperand(1); + + return src_name.AsString() == dst_name.AsString(); + }); +} +void Differ::MatchMemoryModel() { + // Always match the memory model instructions, there is always a single one of + // it. + id_map_.MapInsts(src_->GetMemoryModel(), dst_->GetMemoryModel()); +} + +void Differ::MatchEntryPointIds() { + // Match OpEntryPoint ids (at index 1) by ExecutionModel (at index 0) and + // possibly name (at index 2). OpEntryPoint doesn't produce a result id, so + // this function doesn't use the helpers the other functions use. + + // Map from execution model to OpEntryPoint instructions of that model. + using ExecutionModelMap = + std::unordered_map<uint32_t, std::vector<const opt::Instruction*>>; + ExecutionModelMap src_entry_points_map; + ExecutionModelMap dst_entry_points_map; + std::set<uint32_t> all_execution_models; + + for (const opt::Instruction& src_inst : src_->entry_points()) { + uint32_t execution_model = src_inst.GetSingleWordOperand(0); + src_entry_points_map[execution_model].push_back(&src_inst); + all_execution_models.insert(execution_model); + } + for (const opt::Instruction& dst_inst : dst_->entry_points()) { + uint32_t execution_model = dst_inst.GetSingleWordOperand(0); + dst_entry_points_map[execution_model].push_back(&dst_inst); + all_execution_models.insert(execution_model); + } + + // Go through each model and match the ids. + for (const uint32_t execution_model : all_execution_models) { + auto& src_insts = src_entry_points_map[execution_model]; + auto& dst_insts = dst_entry_points_map[execution_model]; + + // If there is only one entry point in src and dst with that model, match + // them unconditionally. + if (src_insts.size() == 1 && dst_insts.size() == 1) { + uint32_t src_id = src_insts[0]->GetSingleWordOperand(1); + uint32_t dst_id = dst_insts[0]->GetSingleWordOperand(1); + id_map_.MapIds(src_id, dst_id); + id_map_.MapInsts(src_insts[0], dst_insts[0]); + continue; + } + + // Otherwise match them by name. + bool matched = false; + for (const opt::Instruction* src_inst : src_insts) { + for (const opt::Instruction* dst_inst : dst_insts) { + const opt::Operand& src_name = src_inst->GetOperand(2); + const opt::Operand& dst_name = dst_inst->GetOperand(2); + + if (src_name.AsString() == dst_name.AsString()) { + uint32_t src_id = src_inst->GetSingleWordOperand(1); + uint32_t dst_id = dst_inst->GetSingleWordOperand(1); + id_map_.MapIds(src_id, dst_id); + id_map_.MapInsts(src_inst, dst_inst); + matched = true; + break; + } + } + if (matched) { + break; + } + } + } +} + +void Differ::MatchExecutionModes() { + MatchPreambleInstructions(src_->execution_modes(), dst_->execution_modes()); +} + +void Differ::MatchTypeForwardPointers() { + // Bunch all of type forward pointers as potential matches. + PotentialIdMap potential_id_map; + auto get_pointer_type_id = [](const opt::Instruction& inst) { + return inst.GetSingleWordOperand(0); + }; + auto accept_type_forward_pointer_ops = [](const opt::Instruction& inst) { + return inst.opcode() == SpvOpTypeForwardPointer; + }; + + PoolPotentialIds(src_->types_values(), potential_id_map.src_ids, true, + accept_type_forward_pointer_ops, get_pointer_type_id); + PoolPotentialIds(dst_->types_values(), potential_id_map.dst_ids, false, + accept_type_forward_pointer_ops, get_pointer_type_id); + + // Matching types with cyclical references (i.e. in the style of linked lists) + // can get very complex. Currently, the diff tool matches types bottom up, so + // on every instruction it expects to know if its operands are already matched + // or not. With cyclical references, it cannot know that. Type matching may + // need significant modifications to be able to support this use case. + // + // Currently, forwarded types are only matched by storage class and debug + // info, with minimal matching of the type being forwarded: + // + // - Group by class + // - Group by OpType being pointed to + // - Group by debug info + // - If same name and unique, match + // - If leftover is unique, match + + // Group forwarded pointers by storage class first and loop over them. + GroupIdsAndMatch<SpvStorageClass>( + potential_id_map.src_ids, potential_id_map.dst_ids, SpvStorageClassMax, + &Differ::GroupIdsHelperGetTypePointerStorageClass, + [this](const IdGroup& src_group_by_storage_class, + const IdGroup& dst_group_by_storage_class) { + + // Group them further by the type they are pointing to and loop over + // them. + GroupIdsAndMatch<SpvOp>( + src_group_by_storage_class, dst_group_by_storage_class, SpvOpMax, + &Differ::GroupIdsHelperGetTypePointerTypeOp, + [this](const IdGroup& src_group_by_type_op, + const IdGroup& dst_group_by_type_op) { + + // Group them even further by debug info, if possible and match by + // debug name. + MatchTypeForwardPointersByName(src_group_by_type_op, + dst_group_by_type_op); + + // Match the leftovers only if they lack debug info and there is + // only one instance of them. + MatchTypeForwardPointersByTypeOp(src_group_by_type_op, + dst_group_by_type_op); + }); + }); + + // Match the instructions that forward declare the same type themselves + for (uint32_t src_id : potential_id_map.src_ids) { + uint32_t dst_id = id_map_.MappedDstId(src_id); + if (dst_id == 0) continue; + + const opt::Instruction* src_forward_inst = + GetForwardPointerInst(src_id_to_, src_id); + const opt::Instruction* dst_forward_inst = + GetForwardPointerInst(dst_id_to_, dst_id); + + assert(src_forward_inst); + assert(dst_forward_inst); + + id_map_.MapInsts(src_forward_inst, dst_forward_inst); + } +} + +void Differ::MatchTypeIds() { + // Bunch all of type ids as potential matches. + PotentialIdMap potential_id_map; + auto get_result_id = [](const opt::Instruction& inst) { + return inst.result_id(); + }; + auto accept_type_ops = [](const opt::Instruction& inst) { + return spvOpcodeGeneratesType(inst.opcode()); + }; + + PoolPotentialIds(src_->types_values(), potential_id_map.src_ids, true, + accept_type_ops, get_result_id); + PoolPotentialIds(dst_->types_values(), potential_id_map.dst_ids, false, + accept_type_ops, get_result_id); + + // Then match the ids. Start with exact matches, then match the leftover with + // gradually loosening degrees of strictness. For example, in the absence of + // debug info, two block types will be matched if they differ only in a few of + // the fields. + for (uint32_t flexibility = 0; flexibility < 2; ++flexibility) { + MatchIds(potential_id_map, [this, flexibility]( + const opt::Instruction* src_inst, + const opt::Instruction* dst_inst) { + const SpvOp src_op = src_inst->opcode(); + const SpvOp dst_op = dst_inst->opcode(); + + // Don't match if the opcode is not the same. + if (src_op != dst_op) { + return false; + } + + switch (src_op) { + case SpvOpTypeVoid: + case SpvOpTypeBool: + case SpvOpTypeSampler: + // void, bool and sampler are unique, match them. + return true; + case SpvOpTypeInt: + case SpvOpTypeFloat: + case SpvOpTypeVector: + case SpvOpTypeMatrix: + case SpvOpTypeSampledImage: + case SpvOpTypeRuntimeArray: + case SpvOpTypePointer: + // Match these instructions when all operands match. + assert(src_inst->NumInOperandWords() == + dst_inst->NumInOperandWords()); + return DoOperandsMatch(src_inst, dst_inst, 0, + src_inst->NumInOperandWords()); + + case SpvOpTypeFunction: + case SpvOpTypeImage: + // Match function types only if they have the same number of operands, + // and they all match. + // Match image types similarly, expecting the optional final parameter + // to match (if provided in both) + if (src_inst->NumInOperandWords() != dst_inst->NumInOperandWords()) { + return false; + } + return DoOperandsMatch(src_inst, dst_inst, 0, + src_inst->NumInOperandWords()); + + case SpvOpTypeArray: + // Match arrays only if the element type and length match. The length + // is an id of a constant, so the actual constant it's defining is + // compared instead. + if (!DoOperandsMatch(src_inst, dst_inst, 0, 1)) { + return false; + } + + if (AreIdenticalUintConstants(src_inst->GetSingleWordInOperand(1), + dst_inst->GetSingleWordInOperand(1))) { + return true; + } + + // If size is not OpConstant, expect the ids to match exactly (for + // example if a spec contant is used). + return DoOperandsMatch(src_inst, dst_inst, 1, 1); + + case SpvOpTypeStruct: + return MatchOpTypeStruct(src_inst, dst_inst, flexibility); + + default: + return false; + } + }); + } +} + +void Differ::MatchConstants() { + // Bunch all of constant ids as potential matches. + PotentialIdMap potential_id_map; + auto get_result_id = [](const opt::Instruction& inst) { + return inst.result_id(); + }; + auto accept_type_ops = [](const opt::Instruction& inst) { + return spvOpcodeIsConstant(inst.opcode()); + }; + + PoolPotentialIds(src_->types_values(), potential_id_map.src_ids, true, + accept_type_ops, get_result_id); + PoolPotentialIds(dst_->types_values(), potential_id_map.dst_ids, false, + accept_type_ops, get_result_id); + + // Then match the ids. Constants are matched exactly, except for float types + // that are first matched exactly, then leftovers are matched with a small + // error. + for (uint32_t flexibility = 0; flexibility < 2; ++flexibility) { + MatchIds(potential_id_map, [this, flexibility]( + const opt::Instruction* src_inst, + const opt::Instruction* dst_inst) { + const SpvOp src_op = src_inst->opcode(); + const SpvOp dst_op = dst_inst->opcode(); + + // Don't match if the opcode is not the same. + if (src_op != dst_op) { + return false; + } + + switch (src_op) { + case SpvOpConstantTrue: + case SpvOpConstantFalse: + // true and false are unique, match them. + return true; + case SpvOpConstant: + return MatchOpConstant(src_inst, dst_inst, flexibility); + case SpvOpConstantComposite: + case SpvOpSpecConstantComposite: + // Composite constants must match in type and value. + // + // TODO: match OpConstantNull with OpConstantComposite with all zeros + // at flexibility == 1 + // TODO: match constants from structs that have been flexibly-matched. + if (src_inst->NumInOperandWords() != dst_inst->NumInOperandWords()) { + return false; + } + return DoesOperandMatch(src_inst->GetOperand(0), + dst_inst->GetOperand(0)) && + DoOperandsMatch(src_inst, dst_inst, 0, + src_inst->NumInOperandWords()); + case SpvOpConstantSampler: + // Match sampler constants exactly. + // TODO: Allow flexibility in parameters to better diff shaders where + // the sampler param has changed. + assert(src_inst->NumInOperandWords() == + dst_inst->NumInOperandWords()); + return DoOperandsMatch(src_inst, dst_inst, 0, + src_inst->NumInOperandWords()); + case SpvOpConstantNull: + // Match null constants as long as the type matches. + return DoesOperandMatch(src_inst->GetOperand(0), + dst_inst->GetOperand(0)); + + case SpvOpSpecConstantTrue: + case SpvOpSpecConstantFalse: + case SpvOpSpecConstant: + case SpvOpSpecConstantOp: + // Match spec constants by name if available, then by the SpecId + // decoration. + return MatchOpSpecConstant(src_inst, dst_inst); + + default: + return false; + } + }); + } +} + +void Differ::MatchVariableIds() { + // Bunch all of variable ids as potential matches. + PotentialIdMap potential_id_map; + auto get_result_id = [](const opt::Instruction& inst) { + return inst.result_id(); + }; + auto accept_type_ops = [](const opt::Instruction& inst) { + return inst.opcode() == SpvOpVariable; + }; + + PoolPotentialIds(src_->types_values(), potential_id_map.src_ids, true, + accept_type_ops, get_result_id); + PoolPotentialIds(dst_->types_values(), potential_id_map.dst_ids, false, + accept_type_ops, get_result_id); + + // Then match the ids. Start with exact matches, then match the leftover with + // gradually loosening degrees of strictness. For example, in the absence of + // debug info, two otherwise identical variables will be matched if one of + // them has a Private storage class and the other doesn't. + for (uint32_t flexibility = 0; flexibility < 2; ++flexibility) { + MatchIds(potential_id_map, + [this, flexibility](const opt::Instruction* src_inst, + const opt::Instruction* dst_inst) { + assert(src_inst->opcode() == SpvOpVariable); + assert(dst_inst->opcode() == SpvOpVariable); + + return MatchOpVariable(src_inst, dst_inst, flexibility); + }); + } +} + +void Differ::MatchFunctions() { + IdGroup src_func_ids; + IdGroup dst_func_ids; + + for (const auto& func : src_funcs_) { + src_func_ids.push_back(func.first); + } + for (const auto& func : dst_funcs_) { + dst_func_ids.push_back(func.first); + } + + // Base the matching of functions on debug info when available. + GroupIdsAndMatch<std::string>( + src_func_ids, dst_func_ids, "", &Differ::GetSanitizedName, + [this](const IdGroup& src_group, const IdGroup& dst_group) { + + // If there is a single function with this name in src and dst, it's a + // definite match. + if (src_group.size() == 1 && dst_group.size() == 1) { + id_map_.MapIds(src_group[0], dst_group[0]); + return; + } + + // If there are multiple functions with the same name, group them by + // type, and match only if the types match (and are unique). + GroupIdsAndMatch<uint32_t>(src_group, dst_group, 0, + &Differ::GroupIdsHelperGetTypeId, + [this](const IdGroup& src_group_by_type_id, + const IdGroup& dst_group_by_type_id) { + + if (src_group_by_type_id.size() == 1 && + dst_group_by_type_id.size() == 1) { + id_map_.MapIds(src_group_by_type_id[0], + dst_group_by_type_id[0]); + } + }); + }); + + // Any functions that are left are pooled together and matched as if unnamed, + // with the only exception that two functions with mismatching names are not + // matched. + // + // Before that however, the diff of the functions that are matched are taken + // and processed, so that more of the global variables can be matched before + // attempting to match the rest of the functions. They can contribute to the + // precision of the diff of those functions. + for (const uint32_t src_func_id : src_func_ids) { + const uint32_t dst_func_id = id_map_.MappedDstId(src_func_id); + if (dst_func_id == 0) { + continue; + } + + // Since these functions are definite matches, match their parameters for a + // better diff. + MatchFunctionParamIds(src_funcs_[src_func_id], dst_funcs_[dst_func_id]); + + // Take the diff of the two functions. + DiffMatch src_match_result, dst_match_result; + MatchFunctionBodies(src_func_insts_[src_func_id], + dst_func_insts_[dst_func_id], &src_match_result, + &dst_match_result); + + // Match ids between the two function bodies; which can also result in + // global variables getting matched. + MatchIdsInFunctionBodies(src_func_insts_[src_func_id], + dst_func_insts_[dst_func_id], src_match_result, + dst_match_result, 0); + } + + // Best effort match functions with matching type. + GroupIdsAndMatch<uint32_t>( + src_func_ids, dst_func_ids, 0, &Differ::GroupIdsHelperGetTypeId, + [this](const IdGroup& src_group_by_type_id, + const IdGroup& dst_group_by_type_id) { + + BestEffortMatchFunctions(src_group_by_type_id, dst_group_by_type_id, + src_func_insts_, dst_func_insts_); + }); + + // Any function that's left, best effort match them. + BestEffortMatchFunctions(src_func_ids, dst_func_ids, src_func_insts_, + dst_func_insts_); +} + +void Differ::MatchDebugs1() { + // This section in cludes: OpString, OpSourceExtension, OpSource, + // OpSourceContinued + MatchDebugAndAnnotationInstructions(src_->debugs1(), dst_->debugs1()); +} + +void Differ::MatchDebugs2() { + // This section includes: OpName, OpMemberName + MatchDebugAndAnnotationInstructions(src_->debugs2(), dst_->debugs2()); +} + +void Differ::MatchDebugs3() { + // This section includes: OpModuleProcessed + MatchDebugAndAnnotationInstructions(src_->debugs3(), dst_->debugs3()); +} + +void Differ::MatchExtInstDebugInfo() { + // This section includes OpExtInst for DebugInfo extension + MatchDebugAndAnnotationInstructions(src_->ext_inst_debuginfo(), + dst_->ext_inst_debuginfo()); +} + +void Differ::MatchAnnotations() { + // This section includes OpDecorate and family. + MatchDebugAndAnnotationInstructions(src_->annotations(), dst_->annotations()); +} + +const opt::Instruction* Differ::MappedDstInst( + const opt::Instruction* src_inst) { + return MappedInstImpl(src_inst, id_map_.SrcToDstMap(), dst_id_to_); +} + +const opt::Instruction* Differ::MappedSrcInst( + const opt::Instruction* dst_inst) { + return MappedInstImpl(dst_inst, id_map_.DstToSrcMap(), src_id_to_); +} + +const opt::Instruction* Differ::MappedInstImpl( + const opt::Instruction* inst, const IdMap& to_other, + const IdInstructions& other_id_to) { + if (inst->HasResultId()) { + if (to_other.IsMapped(inst->result_id())) { + const uint32_t other_result_id = to_other.MappedId(inst->result_id()); + + assert(other_result_id < other_id_to.inst_map_.size()); + return other_id_to.inst_map_[other_result_id]; + } + + return nullptr; + } + + return to_other.MappedInst(inst); +} + +void Differ::OutputLine(std::function<bool()> are_lines_identical, + std::function<void()> output_src_line, + std::function<void()> output_dst_line) { + if (are_lines_identical()) { + out_ << " "; + output_src_line(); + } else { + OutputRed(); + out_ << "-"; + output_src_line(); + + OutputGreen(); + out_ << "+"; + output_dst_line(); + + OutputResetColor(); + } +} + +const opt::Instruction* IterInst(opt::Module::const_inst_iterator& iter) { + return &*iter; +} + +const opt::Instruction* IterInst(InstructionList::const_iterator& iter) { + return *iter; +} + +template <typename InstList> +void Differ::OutputSection( + const InstList& src_insts, const InstList& dst_insts, + std::function<void(const opt::Instruction&, const IdInstructions&, + const opt::Instruction&)> + write_inst) { + auto src_iter = src_insts.begin(); + auto dst_iter = dst_insts.begin(); + + // - While src_inst doesn't have a match, output it with - + // - While dst_inst doesn't have a match, output it with + + // - Now src_inst and dst_inst both have matches; might not match each other! + // * If section is unordered, just process src_inst and its match (dst_inst + // or not), + // dst_inst will eventually be processed when its match is seen. + // * If section is ordered, also just process src_inst and its match. Its + // match must + // necessarily be dst_inst. + while (src_iter != src_insts.end() || dst_iter != dst_insts.end()) { + OutputRed(); + while (src_iter != src_insts.end() && + MappedDstInst(IterInst(src_iter)) == nullptr) { + out_ << "-"; + write_inst(*IterInst(src_iter), src_id_to_, *IterInst(src_iter)); + ++src_iter; + } + OutputGreen(); + while (dst_iter != dst_insts.end() && + MappedSrcInst(IterInst(dst_iter)) == nullptr) { + out_ << "+"; + write_inst(ToMappedSrcIds(*IterInst(dst_iter)), dst_id_to_, + *IterInst(dst_iter)); + ++dst_iter; + } + OutputResetColor(); + + if (src_iter != src_insts.end() && dst_iter != dst_insts.end()) { + const opt::Instruction* src_inst = IterInst(src_iter); + const opt::Instruction* matched_dst_inst = MappedDstInst(src_inst); + + assert(matched_dst_inst != nullptr); + assert(MappedSrcInst(IterInst(dst_iter)) != nullptr); + + OutputLine( + [this, src_inst, matched_dst_inst]() { + return DoInstructionsMatch(src_inst, matched_dst_inst); + }, + [this, src_inst, &write_inst]() { + write_inst(*src_inst, src_id_to_, *src_inst); + }, + [this, matched_dst_inst, &write_inst]() { + write_inst(ToMappedSrcIds(*matched_dst_inst), dst_id_to_, + *matched_dst_inst); + }); + + ++src_iter; + ++dst_iter; + } + } +} + +void Differ::ToParsedInstruction( + const opt::Instruction& inst, const IdInstructions& id_to, + const opt::Instruction& original_inst, + spv_parsed_instruction_t* parsed_inst, + std::vector<spv_parsed_operand_t>& parsed_operands, + std::vector<uint32_t>& inst_binary) { + inst.ToBinaryWithoutAttachedDebugInsts(&inst_binary); + parsed_operands.resize(inst.NumOperands()); + + parsed_inst->words = inst_binary.data(); + parsed_inst->num_words = static_cast<uint16_t>(inst_binary.size()); + parsed_inst->opcode = static_cast<uint16_t>(inst.opcode()); + parsed_inst->ext_inst_type = + inst.opcode() == SpvOpExtInst + ? GetExtInstType(id_to, original_inst.GetSingleWordInOperand(0)) + : SPV_EXT_INST_TYPE_NONE; + parsed_inst->type_id = + inst.HasResultType() ? inst.GetSingleWordOperand(0) : 0; + parsed_inst->result_id = inst.HasResultId() ? inst.result_id() : 0; + parsed_inst->operands = parsed_operands.data(); + parsed_inst->num_operands = static_cast<uint16_t>(parsed_operands.size()); + + // Word 0 is always op and num_words, so operands start at offset 1. + uint32_t offset = 1; + for (uint16_t operand_index = 0; operand_index < parsed_inst->num_operands; + ++operand_index) { + const opt::Operand& operand = inst.GetOperand(operand_index); + spv_parsed_operand_t& parsed_operand = parsed_operands[operand_index]; + + parsed_operand.offset = static_cast<uint16_t>(offset); + parsed_operand.num_words = static_cast<uint16_t>(operand.words.size()); + parsed_operand.type = operand.type; + parsed_operand.number_kind = GetNumberKind( + id_to, original_inst, operand_index, &parsed_operand.number_bit_width); + + offset += parsed_operand.num_words; + } +} + +opt::Instruction Differ::ToMappedSrcIds(const opt::Instruction& dst_inst) { + // Create an identical instruction to dst_inst, except ids are changed to the + // mapped one. + opt::Instruction mapped_inst = dst_inst; + + for (uint32_t operand_index = 0; operand_index < mapped_inst.NumOperands(); + ++operand_index) { + opt::Operand& operand = mapped_inst.GetOperand(operand_index); + + if (spvIsIdType(operand.type)) { + assert(id_map_.IsDstMapped(operand.AsId())); + operand.words[0] = id_map_.MappedSrcId(operand.AsId()); + } + } + + return mapped_inst; +} + +spv_result_t Differ::Output() { + id_map_.MapUnmatchedIds(); + src_id_to_.inst_map_.resize(id_map_.SrcToDstMap().IdBound(), nullptr); + dst_id_to_.inst_map_.resize(id_map_.DstToSrcMap().IdBound(), nullptr); + + const spv_target_env target_env = SPV_ENV_UNIVERSAL_1_6; + spv_opcode_table opcode_table; + spv_operand_table operand_table; + spv_ext_inst_table ext_inst_table; + spv_result_t result; + + result = spvOpcodeTableGet(&opcode_table, target_env); + if (result != SPV_SUCCESS) return result; + + result = spvOperandTableGet(&operand_table, target_env); + if (result != SPV_SUCCESS) return result; + + result = spvExtInstTableGet(&ext_inst_table, target_env); + if (result != SPV_SUCCESS) return result; + + spv_context_t context{ + target_env, + opcode_table, + operand_table, + ext_inst_table, + }; + + const AssemblyGrammar grammar(&context); + if (!grammar.isValid()) return SPV_ERROR_INVALID_TABLE; + + uint32_t disassembly_options = SPV_BINARY_TO_TEXT_OPTION_PRINT; + if (options_.indent) { + disassembly_options |= SPV_BINARY_TO_TEXT_OPTION_INDENT; + } + + NameMapper name_mapper = GetTrivialNameMapper(); + disassemble::InstructionDisassembler dis(grammar, out_, disassembly_options, + name_mapper); + + if (!options_.no_header) { + // Output the header + // TODO: when using diff with text, the assembler overrides the version and + // generator, so these aren't reflected correctly in the output. Could + // potentially extract this info from the header comment. + OutputLine([]() { return true; }, [&dis]() { dis.EmitHeaderSpirv(); }, + []() { assert(false && "Unreachable"); }); + OutputLine([this]() { return src_->version() == dst_->version(); }, + [this, &dis]() { dis.EmitHeaderVersion(src_->version()); }, + [this, &dis]() { dis.EmitHeaderVersion(dst_->version()); }); + OutputLine([this]() { return src_->generator() == dst_->generator(); }, + [this, &dis]() { dis.EmitHeaderGenerator(src_->generator()); }, + [this, &dis]() { dis.EmitHeaderGenerator(dst_->generator()); }); + OutputLine( + [this]() { return src_->IdBound() == id_map_.SrcToDstMap().IdBound(); }, + [this, &dis]() { dis.EmitHeaderIdBound(src_->IdBound()); }, + [this, &dis]() { + dis.EmitHeaderIdBound(id_map_.SrcToDstMap().IdBound()); + }); + OutputLine([this]() { return src_->schema() == dst_->schema(); }, + [this, &dis]() { dis.EmitHeaderSchema(src_->schema()); }, + [this, &dis]() { dis.EmitHeaderSchema(dst_->schema()); }); + } + + // For each section, iterate both modules and output the disassembly. + auto write_inst = [this, &dis](const opt::Instruction& inst, + const IdInstructions& id_to, + const opt::Instruction& original_inst) { + spv_parsed_instruction_t parsed_inst; + std::vector<spv_parsed_operand_t> parsed_operands; + std::vector<uint32_t> inst_binary; + + ToParsedInstruction(inst, id_to, original_inst, &parsed_inst, + parsed_operands, inst_binary); + + dis.EmitInstruction(parsed_inst, 0); + }; + + OutputSection(src_->capabilities(), dst_->capabilities(), write_inst); + OutputSection(src_->extensions(), dst_->extensions(), write_inst); + OutputSection(src_->ext_inst_imports(), dst_->ext_inst_imports(), write_inst); + + // There is only one memory model. + OutputLine( + [this]() { + return DoInstructionsMatch(src_->GetMemoryModel(), + dst_->GetMemoryModel()); + }, + [this, &write_inst]() { + write_inst(*src_->GetMemoryModel(), src_id_to_, + *src_->GetMemoryModel()); + }, + [this, &write_inst]() { + write_inst(*dst_->GetMemoryModel(), dst_id_to_, + *dst_->GetMemoryModel()); + }); + + OutputSection(src_->entry_points(), dst_->entry_points(), write_inst); + OutputSection(src_->execution_modes(), dst_->execution_modes(), write_inst); + OutputSection(src_->debugs1(), dst_->debugs1(), write_inst); + OutputSection(src_->debugs2(), dst_->debugs2(), write_inst); + OutputSection(src_->debugs3(), dst_->debugs3(), write_inst); + OutputSection(src_->ext_inst_debuginfo(), dst_->ext_inst_debuginfo(), + write_inst); + OutputSection(src_->annotations(), dst_->annotations(), write_inst); + OutputSection(src_->types_values(), dst_->types_values(), write_inst); + + // Get the body of all the functions. + FunctionInstMap src_func_header_insts; + FunctionInstMap dst_func_header_insts; + + GetFunctionHeaderInstructions(src_, &src_func_header_insts); + GetFunctionHeaderInstructions(dst_, &dst_func_header_insts); + + for (const auto& src_func : src_func_insts_) { + const uint32_t src_func_id = src_func.first; + const InstructionList& src_insts = src_func.second; + const InstructionList& src_header_insts = + src_func_header_insts[src_func_id]; + + const uint32_t dst_func_id = id_map_.MappedDstId(src_func_id); + if (dst_func_insts_.find(dst_func_id) == dst_func_insts_.end()) { + OutputSection(src_header_insts, InstructionList(), write_inst); + OutputSection(src_insts, InstructionList(), write_inst); + continue; + } + + const InstructionList& dst_insts = dst_func_insts_[dst_func_id]; + const InstructionList& dst_header_insts = + dst_func_header_insts[dst_func_id]; + OutputSection(src_header_insts, dst_header_insts, write_inst); + OutputSection(src_insts, dst_insts, write_inst); + } + + for (const auto& dst_func : dst_func_insts_) { + const uint32_t dst_func_id = dst_func.first; + const InstructionList& dst_insts = dst_func.second; + const InstructionList& dst_header_insts = + dst_func_header_insts[dst_func_id]; + + const uint32_t src_func_id = id_map_.MappedSrcId(dst_func_id); + if (src_func_insts_.find(src_func_id) == src_func_insts_.end()) { + OutputSection(InstructionList(), dst_header_insts, write_inst); + OutputSection(InstructionList(), dst_insts, write_inst); + } + } + + out_ << std::flush; + + return SPV_SUCCESS; +} + +} // anonymous namespace + +spv_result_t Diff(opt::IRContext* src, opt::IRContext* dst, std::ostream& out, + Options options) { + // High level algorithm: + // + // - Some sections of SPIR-V don't deal with ids; instructions in those + // sections are matched identically. For example OpCapability instructions. + // - Some sections produce ids, and they can be trivially matched by their + // parameters. For example OpExtInstImport instructions. + // - Some sections annotate ids. These are matched at the end, after the ids + // themselves are matched. For example OpName or OpDecorate instructions. + // - Some sections produce ids that depend on other ids and they can be + // recursively matched. For example OpType* instructions. + // - Some sections produce ids that are not trivially matched. For these ids, + // the debug info is used when possible, or a best guess (such as through + // decorations) is used. For example OpVariable instructions. + // - Matching functions is done with multiple attempts: + // * Functions with identical debug names are matched if there are no + // overloads. + // * Otherwise, functions with identical debug names and types are matched. + // * The rest of the functions are best-effort matched, first in groups of + // identical type, then any with any. + // * The best-effort matching takes the diff of every pair of functions in + // a group and selects the top matches that also meet a similarity + // index. + // * Once a pair of functions are matched, the fuzzy diff of the + // instructions is used to match the instructions in the function body. + // The fuzzy diff makes sure that sufficiently similar instructions are + // matched and that yet-to-be-matched result ids don't result in a larger + // diff. + // + // Once the instructions are matched between the src and dst SPIR-V, the src + // is traversed and its disassembly is output. In the process, any unmatched + // instruction is prefixed with -, and any unmatched instruction in dst in the + // same section is output prefixed with +. To avoid confusion, the + // instructions in dst are output with matching ids in src so the output + // assembly is consistent. + + Differ differ(src, dst, out, options); + + // First, match instructions between the different non-annotation sections of + // the SPIR-V. + differ.MatchCapabilities(); + differ.MatchExtensions(); + differ.MatchExtInstImportIds(); + differ.MatchMemoryModel(); + differ.MatchEntryPointIds(); + differ.MatchExecutionModes(); + differ.MatchTypeForwardPointers(); + differ.MatchTypeIds(); + differ.MatchConstants(); + differ.MatchVariableIds(); + differ.MatchFunctions(); + + // Match instructions that annotate previously-matched ids. + differ.MatchDebugs1(); + differ.MatchDebugs2(); + differ.MatchDebugs3(); + differ.MatchExtInstDebugInfo(); + differ.MatchAnnotations(); + + // Show the disassembly with the diff. + // + // TODO: Based on an option, output either based on src or dst, i.e. the diff + // can show the ids and instruction/function order either from src or dst. + spv_result_t result = differ.Output(); + + differ.DumpIdMap(); + + return result; +} + +} // namespace diff +} // namespace spvtools diff --git a/source/diff/diff.h b/source/diff/diff.h new file mode 100644 index 00000000..932de9ee --- /dev/null +++ b/source/diff/diff.h @@ -0,0 +1,48 @@ +// Copyright (c) 2022 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_DIFF_DIFF_H_ +#define SOURCE_DIFF_DIFF_H_ + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace diff { + +struct Options { + bool ignore_set_binding = false; + bool ignore_location = false; + bool indent = false; + bool no_header = false; + bool color_output = false; + bool dump_id_map = false; +}; + +// Given two SPIR-V modules, this function outputs the textual diff of their +// assembly in `out`. The diff is *semantic*, so that the ordering of certain +// instructions wouldn't matter. +// +// The output is a disassembly of src, with diff(1)-style + and - lines that +// show how the src is changed into dst. To make this disassembly +// self-consistent, the ids that are output are all in the space of the src +// module; e.g. any + lines (showing instructions from the dst module) have +// their ids mapped to the matched instruction in the src module (or a new id +// allocated in the src module if unmatched). +spv_result_t Diff(opt::IRContext* src, opt::IRContext* dst, std::ostream& out, + Options options); + +} // namespace diff +} // namespace spvtools + +#endif // SOURCE_DIFF_DIFF_H_ diff --git a/source/diff/lcs.h b/source/diff/lcs.h new file mode 100644 index 00000000..6c00e864 --- /dev/null +++ b/source/diff/lcs.h @@ -0,0 +1,224 @@ +// Copyright (c) 2022 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_DIFF_LCS_H_ +#define SOURCE_DIFF_LCS_H_ + +#include <algorithm> +#include <cassert> +#include <cstddef> +#include <cstdint> +#include <functional> +#include <stack> +#include <vector> + +namespace spvtools { +namespace diff { + +// The result of a diff. +using DiffMatch = std::vector<bool>; + +// Helper class to find the longest common subsequence between two function +// bodies. +template <typename Sequence> +class LongestCommonSubsequence { + public: + LongestCommonSubsequence(const Sequence& src, const Sequence& dst) + : src_(src), + dst_(dst), + table_(src.size(), std::vector<DiffMatchEntry>(dst.size())) {} + + // Given two sequences, it creates a matching between them. The elements are + // simply marked as matched in src and dst, with any unmatched element in src + // implying a removal and any unmatched element in dst implying an addition. + // + // Returns the length of the longest common subsequence. + template <typename T> + uint32_t Get(std::function<bool(T src_elem, T dst_elem)> match, + DiffMatch* src_match_result, DiffMatch* dst_match_result); + + private: + struct DiffMatchIndex { + uint32_t src_offset; + uint32_t dst_offset; + }; + + template <typename T> + void CalculateLCS(std::function<bool(T src_elem, T dst_elem)> match); + void RetrieveMatch(DiffMatch* src_match_result, DiffMatch* dst_match_result); + bool IsInBound(DiffMatchIndex index) { + return index.src_offset < src_.size() && index.dst_offset < dst_.size(); + } + bool IsCalculated(DiffMatchIndex index) { + assert(IsInBound(index)); + return table_[index.src_offset][index.dst_offset].valid; + } + bool IsCalculatedOrOutOfBound(DiffMatchIndex index) { + return !IsInBound(index) || IsCalculated(index); + } + uint32_t GetMemoizedLength(DiffMatchIndex index) { + if (!IsInBound(index)) { + return 0; + } + assert(IsCalculated(index)); + return table_[index.src_offset][index.dst_offset].best_match_length; + } + bool IsMatched(DiffMatchIndex index) { + assert(IsCalculated(index)); + return table_[index.src_offset][index.dst_offset].matched; + } + void MarkMatched(DiffMatchIndex index, uint32_t best_match_length, + bool matched) { + assert(IsInBound(index)); + DiffMatchEntry& entry = table_[index.src_offset][index.dst_offset]; + assert(!entry.valid); + + entry.best_match_length = best_match_length & 0x3FFFFFFF; + assert(entry.best_match_length == best_match_length); + entry.matched = matched; + entry.valid = true; + } + + const Sequence& src_; + const Sequence& dst_; + + struct DiffMatchEntry { + DiffMatchEntry() : best_match_length(0), matched(false), valid(false) {} + + uint32_t best_match_length : 30; + // Whether src[i] and dst[j] matched. This is an optimization to avoid + // calling the `match` function again when walking the LCS table. + uint32_t matched : 1; + // Use for the recursive algorithm to know if the contents of this entry are + // valid. + uint32_t valid : 1; + }; + + std::vector<std::vector<DiffMatchEntry>> table_; +}; + +template <typename Sequence> +template <typename T> +uint32_t LongestCommonSubsequence<Sequence>::Get( + std::function<bool(T src_elem, T dst_elem)> match, + DiffMatch* src_match_result, DiffMatch* dst_match_result) { + CalculateLCS(match); + RetrieveMatch(src_match_result, dst_match_result); + return GetMemoizedLength({0, 0}); +} + +template <typename Sequence> +template <typename T> +void LongestCommonSubsequence<Sequence>::CalculateLCS( + std::function<bool(T src_elem, T dst_elem)> match) { + // The LCS algorithm is simple. Given sequences s and d, with a:b depicting a + // range in python syntax: + // + // lcs(s[i:], d[j:]) = + // lcs(s[i+1:], d[j+1:]) + 1 if s[i] == d[j] + // max(lcs(s[i+1:], d[j:]), lcs(s[i:], d[j+1:])) o.w. + // + // Once the LCS table is filled according to the above, it can be walked and + // the best match retrieved. + // + // This is a recursive function with memoization, which avoids filling table + // entries where unnecessary. This makes the best case O(N) instead of + // O(N^2). The implemention uses a std::stack to avoid stack overflow on long + // sequences. + + if (src_.empty() || dst_.empty()) { + return; + } + + std::stack<DiffMatchIndex> to_calculate; + to_calculate.push({0, 0}); + + while (!to_calculate.empty()) { + DiffMatchIndex current = to_calculate.top(); + to_calculate.pop(); + assert(IsInBound(current)); + + // If already calculated through another path, ignore it. + if (IsCalculated(current)) { + continue; + } + + if (match(src_[current.src_offset], dst_[current.dst_offset])) { + // If the current elements match, advance both indices and calculate the + // LCS if not already. Visit `current` again afterwards, so its + // corresponding entry will be updated. + DiffMatchIndex next = {current.src_offset + 1, current.dst_offset + 1}; + if (IsCalculatedOrOutOfBound(next)) { + MarkMatched(current, GetMemoizedLength(next) + 1, true); + } else { + to_calculate.push(current); + to_calculate.push(next); + } + continue; + } + + // We've reached a pair of elements that don't match. Calculate the LCS for + // both cases of either being left unmatched and take the max. Visit + // `current` again afterwards, so its corresponding entry will be updated. + DiffMatchIndex next_src = {current.src_offset + 1, current.dst_offset}; + DiffMatchIndex next_dst = {current.src_offset, current.dst_offset + 1}; + + if (IsCalculatedOrOutOfBound(next_src) && + IsCalculatedOrOutOfBound(next_dst)) { + uint32_t best_match_length = + std::max(GetMemoizedLength(next_src), GetMemoizedLength(next_dst)); + MarkMatched(current, best_match_length, false); + continue; + } + + to_calculate.push(current); + if (!IsCalculatedOrOutOfBound(next_src)) { + to_calculate.push(next_src); + } + if (!IsCalculatedOrOutOfBound(next_dst)) { + to_calculate.push(next_dst); + } + } +} + +template <typename Sequence> +void LongestCommonSubsequence<Sequence>::RetrieveMatch( + DiffMatch* src_match_result, DiffMatch* dst_match_result) { + src_match_result->clear(); + dst_match_result->clear(); + + src_match_result->resize(src_.size(), false); + dst_match_result->resize(dst_.size(), false); + + DiffMatchIndex current = {0, 0}; + while (IsInBound(current)) { + if (IsMatched(current)) { + (*src_match_result)[current.src_offset++] = true; + (*dst_match_result)[current.dst_offset++] = true; + continue; + } + + if (GetMemoizedLength({current.src_offset + 1, current.dst_offset}) >= + GetMemoizedLength({current.src_offset, current.dst_offset + 1})) { + ++current.src_offset; + } else { + ++current.dst_offset; + } + } +} + +} // namespace diff +} // namespace spvtools + +#endif // SOURCE_DIFF_LCS_H_ diff --git a/source/disassemble.cpp b/source/disassemble.cpp index c553988f..1d61b9f9 100644 --- a/source/disassemble.cpp +++ b/source/disassemble.cpp @@ -17,6 +17,8 @@ // This file contains a disassembler: It converts a SPIR-V binary // to text. +#include "source/disassemble.h" + #include <algorithm> #include <cassert> #include <cstring> @@ -28,9 +30,7 @@ #include "source/assembly_grammar.h" #include "source/binary.h" #include "source/diagnostic.h" -#include "source/disassemble.h" #include "source/ext_inst.h" -#include "source/name_mapper.h" #include "source/opcode.h" #include "source/parsed_operand.h" #include "source/print.h" @@ -40,29 +40,21 @@ #include "source/util/make_unique.h" #include "spirv-tools/libspirv.h" +namespace spvtools { namespace { // A Disassembler instance converts a SPIR-V binary to its assembly // representation. class Disassembler { public: - Disassembler(const spvtools::AssemblyGrammar& grammar, uint32_t options, - spvtools::NameMapper name_mapper) - : grammar_(grammar), - print_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_PRINT, options)), - color_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_COLOR, options)), - indent_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_INDENT, options) - ? kStandardIndent - : 0), - comment_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_COMMENT, options)), + Disassembler(const AssemblyGrammar& grammar, uint32_t options, + NameMapper name_mapper) + : print_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_PRINT, options)), text_(), out_(print_ ? out_stream() : out_stream(text_)), - stream_(out_.get()), + instruction_disassembler_(grammar, out_.get(), options, name_mapper), header_(!spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER, options)), - show_byte_offset_(spvIsInBitfield( - SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET, options)), - byte_offset_(0), - name_mapper_(std::move(name_mapper)) {} + byte_offset_(0) {} // Emits the assembly header for the module, and sets up internal state // so subsequent callbacks can handle the cases where the entire module @@ -78,56 +70,13 @@ class Disassembler { spv_result_t SaveTextResult(spv_text* text_result) const; private: - enum { kStandardIndent = 15 }; - - using out_stream = spvtools::out_stream; - - // Emits an operand for the given instruction, where the instruction - // is at offset words from the start of the binary. - void EmitOperand(const spv_parsed_instruction_t& inst, - const uint16_t operand_index); - - // Emits a mask expression for the given mask word of the specified type. - void EmitMaskOperand(const spv_operand_type_t type, const uint32_t word); - - // Resets the output color, if color is turned on. - void ResetColor() { - if (color_) out_.get() << spvtools::clr::reset{print_}; - } - // Sets the output to grey, if color is turned on. - void SetGrey() { - if (color_) out_.get() << spvtools::clr::grey{print_}; - } - // Sets the output to blue, if color is turned on. - void SetBlue() { - if (color_) out_.get() << spvtools::clr::blue{print_}; - } - // Sets the output to yellow, if color is turned on. - void SetYellow() { - if (color_) out_.get() << spvtools::clr::yellow{print_}; - } - // Sets the output to red, if color is turned on. - void SetRed() { - if (color_) out_.get() << spvtools::clr::red{print_}; - } - // Sets the output to green, if color is turned on. - void SetGreen() { - if (color_) out_.get() << spvtools::clr::green{print_}; - } - - const spvtools::AssemblyGrammar& grammar_; const bool print_; // Should we also print to the standard output stream? - const bool color_; // Should we print in colour? - const int indent_; // How much to indent. 0 means don't indent - const int comment_; // Should we comment the source spv_endianness_t endian_; // The detected endianness of the binary. std::stringstream text_; // Captures the text, if not printing. out_stream out_; // The Output stream. Either to text_ or standard output. - std::ostream& stream_; // The output std::stream. - const bool header_; // Should we output header as the leading comment? - const bool show_byte_offset_; // Should we print byte offset, in hex? - size_t byte_offset_; // The number of bytes processed so far. - spvtools::NameMapper name_mapper_; + disassemble::InstructionDisassembler instruction_disassembler_; + const bool header_; // Should we output header as the leading comment? + size_t byte_offset_; // The number of bytes processed so far. bool inserted_decoration_space_ = false; bool inserted_debug_space_ = false; bool inserted_type_space_ = false; @@ -139,21 +88,11 @@ spv_result_t Disassembler::HandleHeader(spv_endianness_t endian, endian_ = endian; if (header_) { - const char* generator_tool = - spvGeneratorStr(SPV_GENERATOR_TOOL_PART(generator)); - stream_ << "; SPIR-V\n" - << "; Version: " << SPV_SPIRV_VERSION_MAJOR_PART(version) << "." - << SPV_SPIRV_VERSION_MINOR_PART(version) << "\n" - << "; Generator: " << generator_tool; - // For unknown tools, print the numeric tool value. - if (0 == strcmp("Unknown", generator_tool)) { - stream_ << "(" << SPV_GENERATOR_TOOL_PART(generator) << ")"; - } - // Print the miscellaneous part of the generator word on the same - // line as the tool name. - stream_ << "; " << SPV_GENERATOR_MISC_PART(generator) << "\n" - << "; Bound: " << id_bound << "\n" - << "; Schema: " << schema << "\n"; + instruction_disassembler_.EmitHeaderSpirv(); + instruction_disassembler_.EmitHeaderVersion(version); + instruction_disassembler_.EmitHeaderGenerator(generator); + instruction_disassembler_.EmitHeaderIdBound(id_bound); + instruction_disassembler_.EmitHeaderSchema(schema); } byte_offset_ = SPV_INDEX_INSTRUCTION * sizeof(uint32_t); @@ -163,31 +102,149 @@ spv_result_t Disassembler::HandleHeader(spv_endianness_t endian, spv_result_t Disassembler::HandleInstruction( const spv_parsed_instruction_t& inst) { - auto opcode = static_cast<SpvOp>(inst.opcode); - if (comment_ && opcode == SpvOpFunction) { - stream_ << std::endl; - stream_ << std::string(indent_, ' '); - stream_ << "; Function " << name_mapper_(inst.result_id) << std::endl; - } - if (comment_ && !inserted_decoration_space_ && - spvOpcodeIsDecoration(opcode)) { - inserted_decoration_space_ = true; - stream_ << std::endl; - stream_ << std::string(indent_, ' '); - stream_ << "; Annotations" << std::endl; + instruction_disassembler_.EmitSectionComment(inst, inserted_decoration_space_, + inserted_debug_space_, + inserted_type_space_); + + instruction_disassembler_.EmitInstruction(inst, byte_offset_); + + byte_offset_ += inst.num_words * sizeof(uint32_t); + + return SPV_SUCCESS; +} + +spv_result_t Disassembler::SaveTextResult(spv_text* text_result) const { + if (!print_) { + size_t length = text_.str().size(); + char* str = new char[length + 1]; + if (!str) return SPV_ERROR_OUT_OF_MEMORY; + strncpy(str, text_.str().c_str(), length + 1); + spv_text text = new spv_text_t(); + if (!text) { + delete[] str; + return SPV_ERROR_OUT_OF_MEMORY; + } + text->str = str; + text->length = length; + *text_result = text; } - if (comment_ && !inserted_debug_space_ && spvOpcodeIsDebug(opcode)) { - inserted_debug_space_ = true; - stream_ << std::endl; - stream_ << std::string(indent_, ' '); - stream_ << "; Debug Information" << std::endl; + return SPV_SUCCESS; +} + +spv_result_t DisassembleHeader(void* user_data, spv_endianness_t endian, + uint32_t /* magic */, uint32_t version, + uint32_t generator, uint32_t id_bound, + uint32_t schema) { + assert(user_data); + auto disassembler = static_cast<Disassembler*>(user_data); + return disassembler->HandleHeader(endian, version, generator, id_bound, + schema); +} + +spv_result_t DisassembleInstruction( + void* user_data, const spv_parsed_instruction_t* parsed_instruction) { + assert(user_data); + auto disassembler = static_cast<Disassembler*>(user_data); + return disassembler->HandleInstruction(*parsed_instruction); +} + +// Simple wrapper class to provide extra data necessary for targeted +// instruction disassembly. +class WrappedDisassembler { + public: + WrappedDisassembler(Disassembler* dis, const uint32_t* binary, size_t wc) + : disassembler_(dis), inst_binary_(binary), word_count_(wc) {} + + Disassembler* disassembler() { return disassembler_; } + const uint32_t* inst_binary() const { return inst_binary_; } + size_t word_count() const { return word_count_; } + + private: + Disassembler* disassembler_; + const uint32_t* inst_binary_; + const size_t word_count_; +}; + +spv_result_t DisassembleTargetHeader(void* user_data, spv_endianness_t endian, + uint32_t /* magic */, uint32_t version, + uint32_t generator, uint32_t id_bound, + uint32_t schema) { + assert(user_data); + auto wrapped = static_cast<WrappedDisassembler*>(user_data); + return wrapped->disassembler()->HandleHeader(endian, version, generator, + id_bound, schema); +} + +spv_result_t DisassembleTargetInstruction( + void* user_data, const spv_parsed_instruction_t* parsed_instruction) { + assert(user_data); + auto wrapped = static_cast<WrappedDisassembler*>(user_data); + // Check if this is the instruction we want to disassemble. + if (wrapped->word_count() == parsed_instruction->num_words && + std::equal(wrapped->inst_binary(), + wrapped->inst_binary() + wrapped->word_count(), + parsed_instruction->words)) { + // Found the target instruction. Disassemble it and signal that we should + // stop searching so we don't output the same instruction again. + if (auto error = + wrapped->disassembler()->HandleInstruction(*parsed_instruction)) + return error; + return SPV_REQUESTED_TERMINATION; } - if (comment_ && !inserted_type_space_ && spvOpcodeGeneratesType(opcode)) { - inserted_type_space_ = true; - stream_ << std::endl; - stream_ << std::string(indent_, ' '); - stream_ << "; Types, variables and constants" << std::endl; + return SPV_SUCCESS; +} + +constexpr int kStandardIndent = 15; +} // namespace + +namespace disassemble { +InstructionDisassembler::InstructionDisassembler(const AssemblyGrammar& grammar, + std::ostream& stream, + uint32_t options, + NameMapper name_mapper) + : grammar_(grammar), + stream_(stream), + print_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_PRINT, options)), + color_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_COLOR, options)), + indent_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_INDENT, options) + ? kStandardIndent + : 0), + comment_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_COMMENT, options)), + show_byte_offset_( + spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET, options)), + name_mapper_(std::move(name_mapper)) {} + +void InstructionDisassembler::EmitHeaderSpirv() { stream_ << "; SPIR-V\n"; } + +void InstructionDisassembler::EmitHeaderVersion(uint32_t version) { + stream_ << "; Version: " << SPV_SPIRV_VERSION_MAJOR_PART(version) << "." + << SPV_SPIRV_VERSION_MINOR_PART(version) << "\n"; +} + +void InstructionDisassembler::EmitHeaderGenerator(uint32_t generator) { + const char* generator_tool = + spvGeneratorStr(SPV_GENERATOR_TOOL_PART(generator)); + stream_ << "; Generator: " << generator_tool; + // For unknown tools, print the numeric tool value. + if (0 == strcmp("Unknown", generator_tool)) { + stream_ << "(" << SPV_GENERATOR_TOOL_PART(generator) << ")"; } + // Print the miscellaneous part of the generator word on the same + // line as the tool name. + stream_ << "; " << SPV_GENERATOR_MISC_PART(generator) << "\n"; +} + +void InstructionDisassembler::EmitHeaderIdBound(uint32_t id_bound) { + stream_ << "; Bound: " << id_bound << "\n"; +} + +void InstructionDisassembler::EmitHeaderSchema(uint32_t schema) { + stream_ << "; Schema: " << schema << "\n"; +} + +void InstructionDisassembler::EmitInstruction( + const spv_parsed_instruction_t& inst, size_t inst_byte_offset) { + auto opcode = static_cast<SpvOp>(inst.opcode); if (inst.result_id) { SetBlue(); @@ -222,20 +279,45 @@ spv_result_t Disassembler::HandleInstruction( auto saved_flags = stream_.flags(); auto saved_fill = stream_.fill(); stream_ << " ; 0x" << std::setw(8) << std::hex << std::setfill('0') - << byte_offset_; + << inst_byte_offset; stream_.flags(saved_flags); stream_.fill(saved_fill); ResetColor(); } - - byte_offset_ += inst.num_words * sizeof(uint32_t); - stream_ << "\n"; - return SPV_SUCCESS; } -void Disassembler::EmitOperand(const spv_parsed_instruction_t& inst, - const uint16_t operand_index) { +void InstructionDisassembler::EmitSectionComment( + const spv_parsed_instruction_t& inst, bool& inserted_decoration_space, + bool& inserted_debug_space, bool& inserted_type_space) { + auto opcode = static_cast<SpvOp>(inst.opcode); + if (comment_ && opcode == SpvOpFunction) { + stream_ << std::endl; + stream_ << std::string(indent_, ' '); + stream_ << "; Function " << name_mapper_(inst.result_id) << std::endl; + } + if (comment_ && !inserted_decoration_space && spvOpcodeIsDecoration(opcode)) { + inserted_decoration_space = true; + stream_ << std::endl; + stream_ << std::string(indent_, ' '); + stream_ << "; Annotations" << std::endl; + } + if (comment_ && !inserted_debug_space && spvOpcodeIsDebug(opcode)) { + inserted_debug_space = true; + stream_ << std::endl; + stream_ << std::string(indent_, ' '); + stream_ << "; Debug Information" << std::endl; + } + if (comment_ && !inserted_type_space && spvOpcodeGeneratesType(opcode)) { + inserted_type_space = true; + stream_ << std::endl; + stream_ << std::string(indent_, ' '); + stream_ << "; Types, variables and constants" << std::endl; + } +} + +void InstructionDisassembler::EmitOperand(const spv_parsed_instruction_t& inst, + const uint16_t operand_index) { assert(operand_index < inst.num_operands); const spv_parsed_operand_t& operand = inst.operands[operand_index]; const uint32_t word = inst.words[operand.offset]; @@ -277,19 +359,17 @@ void Disassembler::EmitOperand(const spv_parsed_instruction_t& inst, case SPV_OPERAND_TYPE_LITERAL_INTEGER: case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER: { SetRed(); - spvtools::EmitNumericLiteral(&stream_, inst, operand); + EmitNumericLiteral(&stream_, inst, operand); ResetColor(); } break; case SPV_OPERAND_TYPE_LITERAL_STRING: { stream_ << "\""; SetGreen(); - // Strings are always little-endian, and null-terminated. - // Write out the characters, escaping as needed, and without copying - // the entire string. - auto c_str = reinterpret_cast<const char*>(inst.words + operand.offset); - for (auto p = c_str; *p; ++p) { - if (*p == '"' || *p == '\\') stream_ << '\\'; - stream_ << *p; + + std::string str = spvDecodeLiteralStringOperand(inst, operand_index); + for (char const& c : str) { + if (c == '"' || c == '\\') stream_ << '\\'; + stream_ << c; } ResetColor(); stream_ << '"'; @@ -362,8 +442,8 @@ void Disassembler::EmitOperand(const spv_parsed_instruction_t& inst, ResetColor(); } -void Disassembler::EmitMaskOperand(const spv_operand_type_t type, - const uint32_t word) { +void InstructionDisassembler::EmitMaskOperand(const spv_operand_type_t type, + const uint32_t word) { // Scan the mask from least significant bit to most significant bit. For each // set bit, emit the name of that bit. Separate multiple names with '|'. uint32_t remaining_word = word; @@ -389,88 +469,66 @@ void Disassembler::EmitMaskOperand(const spv_operand_type_t type, } } -spv_result_t Disassembler::SaveTextResult(spv_text* text_result) const { - if (!print_) { - size_t length = text_.str().size(); - char* str = new char[length + 1]; - if (!str) return SPV_ERROR_OUT_OF_MEMORY; - strncpy(str, text_.str().c_str(), length + 1); - spv_text text = new spv_text_t(); - if (!text) { - delete[] str; - return SPV_ERROR_OUT_OF_MEMORY; - } - text->str = str; - text->length = length; - *text_result = text; - } - return SPV_SUCCESS; +void InstructionDisassembler::ResetColor() { + if (color_) stream_ << spvtools::clr::reset{print_}; } - -spv_result_t DisassembleHeader(void* user_data, spv_endianness_t endian, - uint32_t /* magic */, uint32_t version, - uint32_t generator, uint32_t id_bound, - uint32_t schema) { - assert(user_data); - auto disassembler = static_cast<Disassembler*>(user_data); - return disassembler->HandleHeader(endian, version, generator, id_bound, - schema); +void InstructionDisassembler::SetGrey() { + if (color_) stream_ << spvtools::clr::grey{print_}; } - -spv_result_t DisassembleInstruction( - void* user_data, const spv_parsed_instruction_t* parsed_instruction) { - assert(user_data); - auto disassembler = static_cast<Disassembler*>(user_data); - return disassembler->HandleInstruction(*parsed_instruction); +void InstructionDisassembler::SetBlue() { + if (color_) stream_ << spvtools::clr::blue{print_}; } +void InstructionDisassembler::SetYellow() { + if (color_) stream_ << spvtools::clr::yellow{print_}; +} +void InstructionDisassembler::SetRed() { + if (color_) stream_ << spvtools::clr::red{print_}; +} +void InstructionDisassembler::SetGreen() { + if (color_) stream_ << spvtools::clr::green{print_}; +} +} // namespace disassemble + +std::string spvInstructionBinaryToText(const spv_target_env env, + const uint32_t* instCode, + const size_t instWordCount, + const uint32_t* code, + const size_t wordCount, + const uint32_t options) { + spv_context context = spvContextCreate(env); + const AssemblyGrammar grammar(context); + if (!grammar.isValid()) { + spvContextDestroy(context); + return ""; + } -// Simple wrapper class to provide extra data necessary for targeted -// instruction disassembly. -class WrappedDisassembler { - public: - WrappedDisassembler(Disassembler* dis, const uint32_t* binary, size_t wc) - : disassembler_(dis), inst_binary_(binary), word_count_(wc) {} - - Disassembler* disassembler() { return disassembler_; } - const uint32_t* inst_binary() const { return inst_binary_; } - size_t word_count() const { return word_count_; } - - private: - Disassembler* disassembler_; - const uint32_t* inst_binary_; - const size_t word_count_; -}; + // Generate friendly names for Ids if requested. + std::unique_ptr<FriendlyNameMapper> friendly_mapper; + NameMapper name_mapper = GetTrivialNameMapper(); + if (options & SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES) { + friendly_mapper = MakeUnique<FriendlyNameMapper>(context, code, wordCount); + name_mapper = friendly_mapper->GetNameMapper(); + } -spv_result_t DisassembleTargetHeader(void* user_data, spv_endianness_t endian, - uint32_t /* magic */, uint32_t version, - uint32_t generator, uint32_t id_bound, - uint32_t schema) { - assert(user_data); - auto wrapped = static_cast<WrappedDisassembler*>(user_data); - return wrapped->disassembler()->HandleHeader(endian, version, generator, - id_bound, schema); -} + // Now disassemble! + Disassembler disassembler(grammar, options, name_mapper); + WrappedDisassembler wrapped(&disassembler, instCode, instWordCount); + spvBinaryParse(context, &wrapped, code, wordCount, DisassembleTargetHeader, + DisassembleTargetInstruction, nullptr); -spv_result_t DisassembleTargetInstruction( - void* user_data, const spv_parsed_instruction_t* parsed_instruction) { - assert(user_data); - auto wrapped = static_cast<WrappedDisassembler*>(user_data); - // Check if this is the instruction we want to disassemble. - if (wrapped->word_count() == parsed_instruction->num_words && - std::equal(wrapped->inst_binary(), - wrapped->inst_binary() + wrapped->word_count(), - parsed_instruction->words)) { - // Found the target instruction. Disassemble it and signal that we should - // stop searching so we don't output the same instruction again. - if (auto error = - wrapped->disassembler()->HandleInstruction(*parsed_instruction)) - return error; - return SPV_REQUESTED_TERMINATION; + spv_text text = nullptr; + std::string output; + if (disassembler.SaveTextResult(&text) == SPV_SUCCESS) { + output.assign(text->str, text->str + text->length); + // Drop trailing newline characters. + while (!output.empty() && output.back() == '\n') output.pop_back(); } - return SPV_SUCCESS; -} + spvTextDestroy(text); + spvContextDestroy(context); -} // namespace + return output; +} +} // namespace spvtools spv_result_t spvBinaryToText(const spv_const_context context, const uint32_t* code, const size_t wordCount, @@ -495,53 +553,13 @@ spv_result_t spvBinaryToText(const spv_const_context context, } // Now disassemble! - Disassembler disassembler(grammar, options, name_mapper); - if (auto error = spvBinaryParse(&hijack_context, &disassembler, code, - wordCount, DisassembleHeader, - DisassembleInstruction, pDiagnostic)) { + spvtools::Disassembler disassembler(grammar, options, name_mapper); + if (auto error = + spvBinaryParse(&hijack_context, &disassembler, code, wordCount, + spvtools::DisassembleHeader, + spvtools::DisassembleInstruction, pDiagnostic)) { return error; } return disassembler.SaveTextResult(pText); } - -std::string spvtools::spvInstructionBinaryToText(const spv_target_env env, - const uint32_t* instCode, - const size_t instWordCount, - const uint32_t* code, - const size_t wordCount, - const uint32_t options) { - spv_context context = spvContextCreate(env); - const spvtools::AssemblyGrammar grammar(context); - if (!grammar.isValid()) { - spvContextDestroy(context); - return ""; - } - - // Generate friendly names for Ids if requested. - std::unique_ptr<spvtools::FriendlyNameMapper> friendly_mapper; - spvtools::NameMapper name_mapper = spvtools::GetTrivialNameMapper(); - if (options & SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES) { - friendly_mapper = spvtools::MakeUnique<spvtools::FriendlyNameMapper>( - context, code, wordCount); - name_mapper = friendly_mapper->GetNameMapper(); - } - - // Now disassemble! - Disassembler disassembler(grammar, options, name_mapper); - WrappedDisassembler wrapped(&disassembler, instCode, instWordCount); - spvBinaryParse(context, &wrapped, code, wordCount, DisassembleTargetHeader, - DisassembleTargetInstruction, nullptr); - - spv_text text = nullptr; - std::string output; - if (disassembler.SaveTextResult(&text) == SPV_SUCCESS) { - output.assign(text->str, text->str + text->length); - // Drop trailing newline characters. - while (!output.empty() && output.back() == '\n') output.pop_back(); - } - spvTextDestroy(text); - spvContextDestroy(context); - - return output; -} diff --git a/source/disassemble.h b/source/disassemble.h index ac357427..8eacb100 100644 --- a/source/disassemble.h +++ b/source/disassemble.h @@ -15,8 +15,10 @@ #ifndef SOURCE_DISASSEMBLE_H_ #define SOURCE_DISASSEMBLE_H_ +#include <iosfwd> #include <string> +#include "source/name_mapper.h" #include "spirv-tools/libspirv.h" namespace spvtools { @@ -33,6 +35,63 @@ std::string spvInstructionBinaryToText(const spv_target_env env, const size_t word_count, const uint32_t options); +class AssemblyGrammar; +namespace disassemble { + +// Shared code with other tools (than the disassembler) that might need to +// output disassembly. An InstructionDisassembler instance converts SPIR-V +// binary for an instruction to its assembly representation. +class InstructionDisassembler { + public: + InstructionDisassembler(const AssemblyGrammar& grammar, std::ostream& stream, + uint32_t options, NameMapper name_mapper); + + // Emits the assembly header for the module. + void EmitHeaderSpirv(); + void EmitHeaderVersion(uint32_t version); + void EmitHeaderGenerator(uint32_t generator); + void EmitHeaderIdBound(uint32_t id_bound); + void EmitHeaderSchema(uint32_t schema); + + // Emits the assembly text for the given instruction. + void EmitInstruction(const spv_parsed_instruction_t& inst, + size_t inst_byte_offset); + + // Emits a comment between different sections of the module. + void EmitSectionComment(const spv_parsed_instruction_t& inst, + bool& inserted_decoration_space, + bool& inserted_debug_space, + bool& inserted_type_space); + + // Resets the output color, if color is turned on. + void ResetColor(); + // Set the output color, if color is turned on. + void SetGrey(); + void SetBlue(); + void SetYellow(); + void SetRed(); + void SetGreen(); + + private: + // Emits an operand for the given instruction, where the instruction + // is at offset words from the start of the binary. + void EmitOperand(const spv_parsed_instruction_t& inst, + const uint16_t operand_index); + + // Emits a mask expression for the given mask word of the specified type. + void EmitMaskOperand(const spv_operand_type_t type, const uint32_t word); + + const spvtools::AssemblyGrammar& grammar_; + std::ostream& stream_; + const bool print_; // Should we also print to the standard output stream? + const bool color_; // Should we print in colour? + const int indent_; // How much to indent. 0 means don't indent + const int comment_; // Should we comment the source + const bool show_byte_offset_; // Should we print byte offset, in hex? + spvtools::NameMapper name_mapper_; +}; + +} // namespace disassemble } // namespace spvtools #endif // SOURCE_DISASSEMBLE_H_ diff --git a/source/ext_inst.cpp b/source/ext_inst.cpp index 812053ec..4e279545 100644 --- a/source/ext_inst.cpp +++ b/source/ext_inst.cpp @@ -96,6 +96,8 @@ spv_result_t spvExtInstTableGet(spv_ext_inst_table* pExtInstTable, case SPV_ENV_UNIVERSAL_1_4: case SPV_ENV_UNIVERSAL_1_5: case SPV_ENV_VULKAN_1_2: + case SPV_ENV_UNIVERSAL_1_6: + case SPV_ENV_VULKAN_1_3: *pExtInstTable = &kTable_1_0; return SPV_SUCCESS; default: diff --git a/source/ext_inst.h b/source/ext_inst.h index aff6e308..4027f4c3 100644 --- a/source/ext_inst.h +++ b/source/ext_inst.h @@ -27,7 +27,7 @@ bool spvExtInstIsNonSemantic(const spv_ext_inst_type_t type); // Returns true if the extended instruction set is debug info bool spvExtInstIsDebugInfo(const spv_ext_inst_type_t type); -// Finds the named extented instruction of the given type in the given extended +// Finds the named extended instruction of the given type in the given extended // instruction table. On success, returns SPV_SUCCESS and writes a handle of // the instruction entry into *entry. spv_result_t spvExtInstTableNameLookup(const spv_ext_inst_table table, @@ -35,7 +35,7 @@ spv_result_t spvExtInstTableNameLookup(const spv_ext_inst_table table, const char* name, spv_ext_inst_desc* entry); -// Finds the extented instruction of the given type in the given extended +// Finds the extended instruction of the given type in the given extended // instruction table by value. On success, returns SPV_SUCCESS and writes a // handle of the instruction entry into *entry. spv_result_t spvExtInstTableValueLookup(const spv_ext_inst_table table, diff --git a/source/extensions.cpp b/source/extensions.cpp index a94db273..049a3ad1 100644 --- a/source/extensions.cpp +++ b/source/extensions.cpp @@ -18,6 +18,7 @@ #include <sstream> #include <string> +#include "source/binary.h" #include "source/enum_string_mapping.h" namespace spvtools { @@ -30,8 +31,9 @@ std::string GetExtensionString(const spv_parsed_instruction_t* inst) { const auto& operand = inst->operands[0]; assert(operand.type == SPV_OPERAND_TYPE_LITERAL_STRING); assert(inst->num_words > operand.offset); + (void)operand; /* No unused variables in release builds. */ - return reinterpret_cast<const char*>(inst->words + operand.offset); + return spvDecodeLiteralStringOperand(*inst, 0); } std::string ExtensionSetToString(const ExtensionSet& extensions) { diff --git a/source/fuzz/fact_manager/fact_manager.h b/source/fuzz/fact_manager/fact_manager.h index 5cf5b18b..ce28ae4c 100644 --- a/source/fuzz/fact_manager/fact_manager.h +++ b/source/fuzz/fact_manager/fact_manager.h @@ -163,7 +163,7 @@ class FactManager { std::vector<const protobufs::DataDescriptor*> GetSynonymsForDataDescriptor( const protobufs::DataDescriptor& data_descriptor) const; - // Returns true if and ony if |data_descriptor1| and |data_descriptor2| are + // Returns true if and only if |data_descriptor1| and |data_descriptor2| are // known to be synonymous. bool IsSynonymous(const protobufs::DataDescriptor& data_descriptor1, const protobufs::DataDescriptor& data_descriptor2) const; @@ -174,7 +174,7 @@ class FactManager { //============================== // Querying facts about dead blocks - // Returns true if and ony if |block_id| is the id of a block known to be + // Returns true if and only if |block_id| is the id of a block known to be // dynamically unreachable. bool BlockIsDead(uint32_t block_id) const; @@ -184,7 +184,7 @@ class FactManager { //============================== // Querying facts about livesafe function - // Returns true if and ony if |function_id| is the id of a function known + // Returns true if and only if |function_id| is the id of a function known // to be livesafe. bool FunctionIsLivesafe(uint32_t function_id) const; @@ -194,7 +194,7 @@ class FactManager { //============================== // Querying facts about irrelevant values - // Returns true if and ony if the value of the pointee associated with + // Returns true if and only if the value of the pointee associated with // |pointer_id| is irrelevant. bool PointeeValueIsIrrelevant(uint32_t pointer_id) const; diff --git a/source/fuzz/fuzzer_context.cpp b/source/fuzz/fuzzer_context.cpp index c2175425..7e34cc33 100644 --- a/source/fuzz/fuzzer_context.cpp +++ b/source/fuzz/fuzzer_context.cpp @@ -21,7 +21,7 @@ namespace fuzz { namespace { -// An offset between the the module's id bound and the minimum fresh id. +// An offset between the module's id bound and the minimum fresh id. // // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2541): consider // the case where the maximum id bound is reached. diff --git a/source/fuzz/fuzzer_pass.cpp b/source/fuzz/fuzzer_pass.cpp index d91482cc..6a879851 100644 --- a/source/fuzz/fuzzer_pass.cpp +++ b/source/fuzz/fuzzer_pass.cpp @@ -261,7 +261,7 @@ uint32_t FuzzerPass::FindOrCreateFloatType(uint32_t width) { uint32_t FuzzerPass::FindOrCreateFunctionType( uint32_t return_type_id, const std::vector<uint32_t>& argument_id) { - // FindFunctionType has a sigle argument for OpTypeFunction operands + // FindFunctionType has a single 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; diff --git a/source/fuzz/fuzzer_pass_donate_modules.cpp b/source/fuzz/fuzzer_pass_donate_modules.cpp index 5bdf6973..29ede58b 100644 --- a/source/fuzz/fuzzer_pass_donate_modules.cpp +++ b/source/fuzz/fuzzer_pass_donate_modules.cpp @@ -479,7 +479,7 @@ void FuzzerPassDonateModules::HandleTypeOrValue( "should have been donated."); // It is OK to have duplicate constant composite definitions, so add - // this to the module using remapped versions of all consituent ids and + // this to the module using remapped versions of all constituent ids and // the result type. new_result_id = GetFuzzerContext()->GetFreshId(); std::vector<uint32_t> constituent_ids; diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto index cba9397b..e71b6a3f 100644 --- a/source/fuzz/protobufs/spvtoolsfuzz.proto +++ b/source/fuzz/protobufs/spvtoolsfuzz.proto @@ -1961,10 +1961,10 @@ message TransformationReplaceBooleanConstantWithConstantBinary { // A descriptor for the boolean constant id we would like to replace IdUseDescriptor id_use_descriptor = 1; - // Id for the constant to be used on the LHS of the comparision + // Id for the constant to be used on the LHS of the comparison uint32 lhs_id = 2; - // Id for the constant to be used on the RHS of the comparision + // Id for the constant to be used on the RHS of the comparison uint32 rhs_id = 3; // Opcode for binary operator @@ -2403,7 +2403,7 @@ message TransformationWrapVectorSynonym { // va = vector(..., a, ...) // vb = vector(..., b, ...) // - // where a and b are in the same position i in each of their correponding vector + // where a and b are in the same position i in each of their corresponding vector // and a is synonymous with va[i] and b is synonymous with vb[i]. // // The transformation then add an instruction vc = va op vb where c is synonymous diff --git a/source/fuzz/transformation_add_constant_composite.cpp b/source/fuzz/transformation_add_constant_composite.cpp index e6cd5a96..89007ab6 100644 --- a/source/fuzz/transformation_add_constant_composite.cpp +++ b/source/fuzz/transformation_add_constant_composite.cpp @@ -75,7 +75,7 @@ bool TransformationAddConstantComposite::IsApplicable( // We do not create constants of structs decorated with Block nor // BufferBlock. The SPIR-V spec does not explicitly disallow this, but it // seems like a strange thing to do, so we disallow it to avoid triggering - // low priorty edge case issues related to it. + // low priority edge case issues related to it. if (fuzzerutil::HasBlockOrBufferBlockDecoration( ir_context, composite_type_instruction->result_id())) { return false; diff --git a/source/fuzz/transformation_duplicate_region_with_selection.cpp b/source/fuzz/transformation_duplicate_region_with_selection.cpp index a80becd9..db88610f 100644 --- a/source/fuzz/transformation_duplicate_region_with_selection.cpp +++ b/source/fuzz/transformation_duplicate_region_with_selection.cpp @@ -325,7 +325,7 @@ void TransformationDuplicateRegionWithSelection::Apply( std::map<uint32_t, uint32_t> original_id_to_phi_id = fuzzerutil::RepeatedUInt32PairToMap(message_.original_id_to_phi_id()); - // Use oveflow ids to fill in any required ids that are missing from these + // Use overflow ids to fill in any required ids that are missing from these // maps. for (auto block : region_blocks) { if (original_label_to_duplicate_label.count(block->id()) == 0) { diff --git a/source/fuzz/transformation_replace_id_with_synonym.h b/source/fuzz/transformation_replace_id_with_synonym.h index 4570fcef..66f8e439 100644 --- a/source/fuzz/transformation_replace_id_with_synonym.h +++ b/source/fuzz/transformation_replace_id_with_synonym.h @@ -32,7 +32,7 @@ class TransformationReplaceIdWithSynonym : public Transformation { protobufs::IdUseDescriptor id_use_descriptor, uint32_t synonymous_id); // - The fact manager must know that the id identified by - // |message_.id_use_descriptor| is synonomous with |message_.synonymous_id|. + // |message_.id_use_descriptor| is synonymous with |message_.synonymous_id|. // - Replacing the id in |message_.id_use_descriptor| by // |message_.synonymous_id| must respect SPIR-V's rules about uses being // dominated by their definitions. diff --git a/source/link/CMakeLists.txt b/source/link/CMakeLists.txt index c8dd2f71..a452a107 100644 --- a/source/link/CMakeLists.txt +++ b/source/link/CMakeLists.txt @@ -23,7 +23,7 @@ target_include_directories(SPIRV-Tools-link $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> PRIVATE ${spirv-tools_BINARY_DIR} ) -# We need the IR functionnalities from the optimizer +# We need the IR functionalities from the optimizer target_link_libraries(SPIRV-Tools-link PUBLIC SPIRV-Tools-opt) diff --git a/source/link/linker.cpp b/source/link/linker.cpp index c5ca5625..76ce775d 100644 --- a/source/link/linker.cpp +++ b/source/link/linker.cpp @@ -19,6 +19,7 @@ #include <cstring> #include <iostream> #include <memory> +#include <numeric> #include <string> #include <unordered_map> #include <unordered_set> @@ -37,6 +38,7 @@ #include "source/spirv_constant.h" #include "source/spirv_target_env.h" #include "source/util/make_unique.h" +#include "source/util/string_utils.h" #include "spirv-tools/libspirv.hpp" namespace spvtools { @@ -86,10 +88,6 @@ spv_result_t ShiftIdsInModules(const MessageConsumer& consumer, // // |header| should not be null, |modules| should not be empty and pointers // should be non-null. |max_id_bound| should be strictly greater than 0. -// -// TODO(pierremoreau): What to do when binaries use different versions of -// SPIR-V? For now, use the max of all versions found in -// the input modules. spv_result_t GenerateHeader(const MessageConsumer& consumer, const std::vector<opt::Module*>& modules, uint32_t max_id_bound, opt::ModuleHeader* header); @@ -130,7 +128,7 @@ spv_result_t CheckImportExportCompatibility(const MessageConsumer& consumer, // Remove linkage specific instructions, such as prototypes of imported // functions, declarations of imported variables, import (and export if -// necessary) linkage attribtes. +// necessary) linkage attributes. // // |linked_context| and |decoration_manager| should not be null, and the // 'RemoveDuplicatePass' should be run first. @@ -148,6 +146,15 @@ spv_result_t RemoveLinkageSpecificInstructions( spv_result_t VerifyIds(const MessageConsumer& consumer, opt::IRContext* linked_context); +// Verify that the universal limits are not crossed, and warn the user +// otherwise. +// +// TODO(pierremoreau): +// - Verify against the limits of the environment (e.g. Vulkan limits if +// consuming vulkan1.x) +spv_result_t VerifyLimits(const MessageConsumer& consumer, + const opt::IRContext& linked_context); + spv_result_t ShiftIdsInModules(const MessageConsumer& consumer, std::vector<opt::Module*>* modules, uint32_t* max_id_bound) { @@ -163,29 +170,31 @@ spv_result_t ShiftIdsInModules(const MessageConsumer& consumer, return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA) << "|max_id_bound| of ShiftIdsInModules should not be null."; - uint32_t id_bound = modules->front()->IdBound() - 1u; + const size_t id_bound = + std::accumulate(modules->begin(), modules->end(), static_cast<size_t>(1), + [](const size_t& accumulation, opt::Module* module) { + return accumulation + module->IdBound() - 1u; + }); + if (id_bound > std::numeric_limits<uint32_t>::max()) + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA) + << "Too many IDs (" << id_bound + << "): combining all modules would overflow the 32-bit word of the " + "SPIR-V header."; + + *max_id_bound = static_cast<uint32_t>(id_bound); + + uint32_t id_offset = modules->front()->IdBound() - 1u; for (auto module_iter = modules->begin() + 1; module_iter != modules->end(); ++module_iter) { Module* module = *module_iter; - module->ForEachInst([&id_bound](Instruction* insn) { - insn->ForEachId([&id_bound](uint32_t* id) { *id += id_bound; }); + module->ForEachInst([&id_offset](Instruction* insn) { + insn->ForEachId([&id_offset](uint32_t* id) { *id += id_offset; }); }); - id_bound += module->IdBound() - 1u; - if (id_bound > 0x3FFFFF) - return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_ID) - << "The limit of IDs, 4194303, was exceeded:" - << " " << id_bound << " is the current ID bound."; + id_offset += module->IdBound() - 1u; // Invalidate the DefUseManager module->context()->InvalidateAnalyses(opt::IRContext::kAnalysisDefUse); } - ++id_bound; - if (id_bound > 0x3FFFFF) - return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_ID) - << "The limit of IDs, 4194303, was exceeded:" - << " " << id_bound << " is the current ID bound."; - - *max_id_bound = id_bound; return SPV_SUCCESS; } @@ -202,15 +211,25 @@ spv_result_t GenerateHeader(const MessageConsumer& consumer, return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA) << "|max_id_bound| of GenerateHeader should not be null."; - uint32_t version = 0u; - for (const auto& module : modules) - version = std::max(version, module->version()); + const uint32_t linked_version = modules.front()->version(); + for (std::size_t i = 1; i < modules.size(); ++i) { + const uint32_t module_version = modules[i]->version(); + if (module_version != linked_version) + return DiagnosticStream({0, 0, 1}, consumer, "", SPV_ERROR_INTERNAL) + << "Conflicting SPIR-V versions: " + << SPV_SPIRV_VERSION_MAJOR_PART(linked_version) << "." + << SPV_SPIRV_VERSION_MINOR_PART(linked_version) + << " (input modules 1 through " << i << ") vs " + << SPV_SPIRV_VERSION_MAJOR_PART(module_version) << "." + << SPV_SPIRV_VERSION_MINOR_PART(module_version) + << " (input module " << (i + 1) << ")."; + } header->magic_number = SpvMagicNumber; - header->version = version; + header->version = linked_version; header->generator = SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS_LINKER, 0); header->bound = max_id_bound; - header->reserved = 0u; + header->schema = 0u; return SPV_SUCCESS; } @@ -243,55 +262,65 @@ spv_result_t MergeModules(const MessageConsumer& consumer, linked_module->AddExtInstImport( std::unique_ptr<Instruction>(inst.Clone(linked_context))); - do { - const Instruction* memory_model_inst = input_modules[0]->GetMemoryModel(); - if (memory_model_inst == nullptr) break; - - uint32_t addressing_model = memory_model_inst->GetSingleWordOperand(0u); - uint32_t memory_model = memory_model_inst->GetSingleWordOperand(1u); - for (const auto& module : input_modules) { - memory_model_inst = module->GetMemoryModel(); - if (memory_model_inst == nullptr) continue; - - if (addressing_model != memory_model_inst->GetSingleWordOperand(0u)) { - spv_operand_desc initial_desc = nullptr, current_desc = nullptr; - grammar.lookupOperand(SPV_OPERAND_TYPE_ADDRESSING_MODEL, - addressing_model, &initial_desc); - grammar.lookupOperand(SPV_OPERAND_TYPE_ADDRESSING_MODEL, - memory_model_inst->GetSingleWordOperand(0u), - ¤t_desc); - return DiagnosticStream(position, consumer, "", SPV_ERROR_INTERNAL) - << "Conflicting addressing models: " << initial_desc->name - << " vs " << current_desc->name << "."; - } - if (memory_model != memory_model_inst->GetSingleWordOperand(1u)) { - spv_operand_desc initial_desc = nullptr, current_desc = nullptr; - grammar.lookupOperand(SPV_OPERAND_TYPE_MEMORY_MODEL, memory_model, - &initial_desc); - grammar.lookupOperand(SPV_OPERAND_TYPE_MEMORY_MODEL, - memory_model_inst->GetSingleWordOperand(1u), - ¤t_desc); - return DiagnosticStream(position, consumer, "", SPV_ERROR_INTERNAL) - << "Conflicting memory models: " << initial_desc->name << " vs " - << current_desc->name << "."; - } + const Instruction* linked_memory_model_inst = + input_modules.front()->GetMemoryModel(); + if (linked_memory_model_inst == nullptr) { + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY) + << "Input module 1 is lacking an OpMemoryModel instruction."; + } + const uint32_t linked_addressing_model = + linked_memory_model_inst->GetSingleWordOperand(0u); + const uint32_t linked_memory_model = + linked_memory_model_inst->GetSingleWordOperand(1u); + + for (std::size_t i = 1; i < input_modules.size(); ++i) { + const Module* module = input_modules[i]; + const Instruction* memory_model_inst = module->GetMemoryModel(); + if (memory_model_inst == nullptr) + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY) + << "Input module " << (i + 1) + << " is lacking an OpMemoryModel instruction."; + + const uint32_t module_addressing_model = + memory_model_inst->GetSingleWordOperand(0u); + if (module_addressing_model != linked_addressing_model) { + spv_operand_desc linked_desc = nullptr, module_desc = nullptr; + grammar.lookupOperand(SPV_OPERAND_TYPE_ADDRESSING_MODEL, + linked_addressing_model, &linked_desc); + grammar.lookupOperand(SPV_OPERAND_TYPE_ADDRESSING_MODEL, + module_addressing_model, &module_desc); + return DiagnosticStream(position, consumer, "", SPV_ERROR_INTERNAL) + << "Conflicting addressing models: " << linked_desc->name + << " (input modules 1 through " << i << ") vs " + << module_desc->name << " (input module " << (i + 1) << ")."; } - if (memory_model_inst != nullptr) - linked_module->SetMemoryModel(std::unique_ptr<Instruction>( - memory_model_inst->Clone(linked_context))); - } while (false); + const uint32_t module_memory_model = + memory_model_inst->GetSingleWordOperand(1u); + if (module_memory_model != linked_memory_model) { + spv_operand_desc linked_desc = nullptr, module_desc = nullptr; + grammar.lookupOperand(SPV_OPERAND_TYPE_MEMORY_MODEL, linked_memory_model, + &linked_desc); + grammar.lookupOperand(SPV_OPERAND_TYPE_MEMORY_MODEL, module_memory_model, + &module_desc); + return DiagnosticStream(position, consumer, "", SPV_ERROR_INTERNAL) + << "Conflicting memory models: " << linked_desc->name + << " (input modules 1 through " << i << ") vs " + << module_desc->name << " (input module " << (i + 1) << ")."; + } + } + linked_module->SetMemoryModel(std::unique_ptr<Instruction>( + linked_memory_model_inst->Clone(linked_context))); - std::vector<std::pair<uint32_t, const char*>> entry_points; + std::vector<std::pair<uint32_t, std::string>> entry_points; for (const auto& module : input_modules) for (const auto& inst : module->entry_points()) { const uint32_t model = inst.GetSingleWordInOperand(0); - const char* const name = - reinterpret_cast<const char*>(inst.GetInOperand(2).words.data()); + const std::string name = inst.GetInOperand(2).AsString(); const auto i = std::find_if( entry_points.begin(), entry_points.end(), - [model, name](const std::pair<uint32_t, const char*>& v) { - return v.first == model && strcmp(name, v.second) == 0; + [model, name](const std::pair<uint32_t, std::string>& v) { + return v.first == model && v.second == name; }); if (i != entry_points.end()) { spv_operand_desc desc = nullptr; @@ -332,13 +361,10 @@ spv_result_t MergeModules(const MessageConsumer& consumer, // If the generated module uses SPIR-V 1.1 or higher, add an // OpModuleProcessed instruction about the linking step. - if (linked_module->version() >= 0x10100) { + if (linked_module->version() >= SPV_SPIRV_VERSION_WORD(1, 1)) { const std::string processed_string("Linked by SPIR-V Tools Linker"); - const auto num_chars = processed_string.size(); - // Compute num words, accommodate the terminating null character. - const auto num_words = (num_chars + 1 + 3) / 4; - std::vector<uint32_t> processed_words(num_words, 0u); - std::memcpy(processed_words.data(), processed_string.data(), num_chars); + std::vector<uint32_t> processed_words = + spvtools::utils::MakeVector(processed_string); linked_module->AddDebug3Inst(std::unique_ptr<Instruction>( new Instruction(linked_context, SpvOpModuleProcessed, 0u, 0u, {{SPV_OPERAND_TYPE_LITERAL_STRING, processed_words}}))); @@ -352,18 +378,12 @@ spv_result_t MergeModules(const MessageConsumer& consumer, // TODO(pierremoreau): Since the modules have not been validate, should we // expect SpvStorageClassFunction variables outside // functions? - uint32_t num_global_values = 0u; for (const auto& module : input_modules) { for (const auto& inst : module->types_values()) { linked_module->AddType( std::unique_ptr<Instruction>(inst.Clone(linked_context))); - num_global_values += inst.opcode() == SpvOpVariable; } } - if (num_global_values > 0xFFFF) - return DiagnosticStream(position, consumer, "", SPV_ERROR_INTERNAL) - << "The limit of global values, 65535, was exceeded;" - << " " << num_global_values << " global values were found."; // Process functions and their basic blocks for (const auto& module : input_modules) { @@ -414,8 +434,7 @@ spv_result_t GetImportExportPairs(const MessageConsumer& consumer, const uint32_t type = decoration.GetSingleWordInOperand(3u); LinkageSymbolInfo symbol_info; - symbol_info.name = - reinterpret_cast<const char*>(decoration.GetInOperand(2u).words.data()); + symbol_info.name = decoration.GetInOperand(2u).AsString(); symbol_info.id = id; symbol_info.type_id = 0u; @@ -636,6 +655,34 @@ spv_result_t VerifyIds(const MessageConsumer& consumer, return SPV_SUCCESS; } +spv_result_t VerifyLimits(const MessageConsumer& consumer, + const opt::IRContext& linked_context) { + spv_position_t position = {}; + + const uint32_t max_id_bound = linked_context.module()->id_bound(); + if (max_id_bound >= SPV_LIMIT_RESULT_ID_BOUND) + DiagnosticStream({0u, 0u, 4u}, consumer, "", SPV_WARNING) + << "The minimum limit of IDs, " << (SPV_LIMIT_RESULT_ID_BOUND - 1) + << ", was exceeded:" + << " " << max_id_bound << " is the current ID bound.\n" + << "The resulting module might not be supported by all " + "implementations."; + + size_t num_global_values = 0u; + for (const auto& inst : linked_context.module()->types_values()) { + num_global_values += inst.opcode() == SpvOpVariable; + } + if (num_global_values >= SPV_LIMIT_GLOBAL_VARIABLES_MAX) + DiagnosticStream(position, consumer, "", SPV_WARNING) + << "The minimum limit of global values, " + << (SPV_LIMIT_GLOBAL_VARIABLES_MAX - 1) << ", was exceeded;" + << " " << num_global_values << " global values were found.\n" + << "The resulting module might not be supported by all " + "implementations."; + + return SPV_SUCCESS; +} + } // namespace spv_result_t Link(const Context& context, @@ -760,7 +807,11 @@ spv_result_t Link(const Context& context, const uint32_t* const* binaries, pass_res = manager.Run(&linked_context); if (pass_res == opt::Pass::Status::Failure) return SPV_ERROR_INVALID_DATA; - // Phase 11: Output the module + // Phase 11: Warn if SPIR-V limits were exceeded + res = VerifyLimits(consumer, linked_context); + if (res != SPV_SUCCESS) return res; + + // Phase 12: Output the module linked_context.module()->ToBinary(linked_binary, true); return SPV_SUCCESS; diff --git a/source/name_mapper.cpp b/source/name_mapper.cpp index eb08f8fe..3b31d33a 100644 --- a/source/name_mapper.cpp +++ b/source/name_mapper.cpp @@ -22,10 +22,10 @@ #include <unordered_map> #include <unordered_set> -#include "spirv-tools/libspirv.h" - +#include "source/binary.h" #include "source/latest_version_spirv_header.h" #include "source/parsed_operand.h" +#include "spirv-tools/libspirv.h" namespace spvtools { namespace { @@ -172,7 +172,7 @@ spv_result_t FriendlyNameMapper::ParseInstruction( const auto result_id = inst.result_id; switch (inst.opcode) { case SpvOpName: - SaveName(inst.words[1], reinterpret_cast<const char*>(inst.words + 2)); + SaveName(inst.words[1], spvDecodeLiteralStringOperand(inst, 1)); break; case SpvOpDecorate: // Decorations come after OpName. So OpName will take precedence over @@ -274,9 +274,8 @@ spv_result_t FriendlyNameMapper::ParseInstruction( SaveName(result_id, "Queue"); break; case SpvOpTypeOpaque: - SaveName(result_id, - std::string("Opaque_") + - Sanitize(reinterpret_cast<const char*>(inst.words + 2))); + SaveName(result_id, std::string("Opaque_") + + Sanitize(spvDecodeLiteralStringOperand(inst, 1))); break; case SpvOpTypePipeStorage: SaveName(result_id, "PipeStorage"); diff --git a/source/opcode.cpp b/source/opcode.cpp index c96cde8d..c9c425d8 100644 --- a/source/opcode.cpp +++ b/source/opcode.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2020 The Khronos Group Inc. +// Copyright (c) 2015-2022 The Khronos Group Inc. // Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights // reserved. // @@ -40,12 +40,12 @@ struct OpcodeDescPtrLen { static const spv_opcode_table_t kOpcodeTable = {ARRAY_SIZE(kOpcodeTableEntries), kOpcodeTableEntries}; -// Represents a vendor tool entry in the SPIR-V XML Regsitry. +// Represents a vendor tool entry in the SPIR-V XML Registry. struct VendorTool { uint32_t value; const char* vendor; const char* tool; // Might be empty string. - const char* vendor_tool; // Combiantion of vendor and tool. + const char* vendor_tool; // Combination of vendor and tool. }; const VendorTool vendor_tools[] = { @@ -631,6 +631,7 @@ bool spvOpcodeIsDebug(SpvOp opcode) { case SpvOpString: case SpvOpLine: case SpvOpNoLine: + case SpvOpModuleProcessed: return true; default: return false; diff --git a/source/operand.cpp b/source/operand.cpp index 6d83e81e..0c255a35 100644 --- a/source/operand.cpp +++ b/source/operand.cpp @@ -72,12 +72,16 @@ spv_result_t spvOperandTableNameLookup(spv_target_env env, // Note that the second rule assumes the extension enabling this operand // is indeed requested in the SPIR-V code; checking that should be // validator's work. - if (((version >= entry.minVersion && version <= entry.lastVersion) || - entry.numExtensions > 0u || entry.numCapabilities > 0u) && - nameLength == strlen(entry.name) && + if (nameLength == strlen(entry.name) && !strncmp(entry.name, name, nameLength)) { - *pEntry = &entry; - return SPV_SUCCESS; + if ((version >= entry.minVersion && version <= entry.lastVersion) || + entry.numExtensions > 0u || entry.numCapabilities > 0u) { + *pEntry = &entry; + return SPV_SUCCESS; + } else { + // if there is no extension/capability then the version is wrong + return SPV_ERROR_WRONG_VERSION; + } } } } diff --git a/source/opt/CMakeLists.txt b/source/opt/CMakeLists.txt index 7d522fb5..61e7a981 100644 --- a/source/opt/CMakeLists.txt +++ b/source/opt/CMakeLists.txt @@ -45,6 +45,7 @@ set(SPIRV_TOOLS_OPT_SOURCES eliminate_dead_constant_pass.h eliminate_dead_functions_pass.h eliminate_dead_functions_util.h + eliminate_dead_input_components_pass.h eliminate_dead_members_pass.h empty_pass.h feature_manager.h @@ -99,6 +100,7 @@ set(SPIRV_TOOLS_OPT_SOURCES reflect.h register_pressure.h relax_float_ops_pass.h + remove_dontinline_pass.h remove_duplicates_pass.h remove_unused_interface_variables_pass.h replace_desc_array_access_using_var_index.h @@ -108,10 +110,11 @@ set(SPIRV_TOOLS_OPT_SOURCES scalar_replacement_pass.h set_spec_constant_default_value_pass.h simplification_pass.h + spread_volatile_semantics.h ssa_rewrite_pass.h strength_reduction_pass.h strip_debug_info_pass.h - strip_reflect_info_pass.h + strip_nonsemantic_info_pass.h struct_cfg_analysis.h tree_iterator.h type_manager.h @@ -156,6 +159,7 @@ set(SPIRV_TOOLS_OPT_SOURCES eliminate_dead_constant_pass.cpp eliminate_dead_functions_pass.cpp eliminate_dead_functions_util.cpp + eliminate_dead_input_components_pass.cpp eliminate_dead_members_pass.cpp feature_manager.cpp fix_storage_class.cpp @@ -206,6 +210,7 @@ set(SPIRV_TOOLS_OPT_SOURCES redundancy_elimination.cpp register_pressure.cpp relax_float_ops_pass.cpp + remove_dontinline_pass.cpp remove_duplicates_pass.cpp remove_unused_interface_variables_pass.cpp replace_desc_array_access_using_var_index.cpp @@ -215,10 +220,11 @@ set(SPIRV_TOOLS_OPT_SOURCES scalar_replacement_pass.cpp set_spec_constant_default_value_pass.cpp simplification_pass.cpp + spread_volatile_semantics.cpp ssa_rewrite_pass.cpp strength_reduction_pass.cpp strip_debug_info_pass.cpp - strip_reflect_info_pass.cpp + strip_nonsemantic_info_pass.cpp struct_cfg_analysis.cpp type_manager.cpp types.cpp diff --git a/source/opt/aggressive_dead_code_elim_pass.cpp b/source/opt/aggressive_dead_code_elim_pass.cpp index 0b54d5e8..04737521 100644 --- a/source/opt/aggressive_dead_code_elim_pass.cpp +++ b/source/opt/aggressive_dead_code_elim_pass.cpp @@ -27,6 +27,7 @@ #include "source/opt/iterator.h" #include "source/opt/reflect.h" #include "source/spirv_constant.h" +#include "source/util/string_utils.h" namespace spvtools { namespace opt { @@ -146,8 +147,7 @@ void AggressiveDCEPass::AddStores(Function* func, uint32_t ptrId) { bool AggressiveDCEPass::AllExtensionsSupported() const { // If any extension not in allowlist, return false for (auto& ei : get_module()->extensions()) { - const char* extName = - reinterpret_cast<const char*>(&ei.GetInOperand(0).words[0]); + const std::string extName = ei.GetInOperand(0).AsString(); if (extensions_allowlist_.find(extName) == extensions_allowlist_.end()) return false; } @@ -156,11 +156,9 @@ bool AggressiveDCEPass::AllExtensionsSupported() const { for (auto& inst : context()->module()->ext_inst_imports()) { assert(inst.opcode() == SpvOpExtInstImport && "Expecting an import of an extension's instruction set."); - const char* extension_name = - reinterpret_cast<const char*>(&inst.GetInOperand(0).words[0]); - if (0 == std::strncmp(extension_name, "NonSemantic.", 12) && - 0 != std::strncmp(extension_name, "NonSemantic.Shader.DebugInfo.100", - 32)) { + const std::string extension_name = inst.GetInOperand(0).AsString(); + if (spvtools::utils::starts_with(extension_name, "NonSemantic.") && + extension_name != "NonSemantic.Shader.DebugInfo.100") { return false; } } @@ -569,12 +567,7 @@ void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() { } // Keep all entry points. for (auto& entry : get_module()->entry_points()) { - if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4) && - !preserve_interface_) { - // In SPIR-V 1.4 and later, entry points must list all global variables - // used. DCE can still remove non-input/output variables and update the - // interface list. Mark the entry point as live and inputs and outputs as - // live, but defer decisions all other interfaces. + if (!preserve_interface_) { live_insts_.Set(entry.unique_id()); // The actual function is live always. AddToWorklist( @@ -582,8 +575,9 @@ void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() { for (uint32_t i = 3; i < entry.NumInOperands(); ++i) { auto* var = get_def_use_mgr()->GetDef(entry.GetSingleWordInOperand(i)); auto storage_class = var->GetSingleWordInOperand(0u); - if (storage_class == SpvStorageClassInput || - storage_class == SpvStorageClassOutput) { + // Vulkan support outputs without an associated input, but not inputs + // without an associated output. + if (storage_class == SpvStorageClassOutput) { AddToWorklist(var); } } @@ -885,8 +879,7 @@ bool AggressiveDCEPass::ProcessGlobalValues() { } } - if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4) && - !preserve_interface_) { + if (!preserve_interface_) { // Remove the dead interface variables from the entry point interface list. for (auto& entry : get_module()->entry_points()) { std::vector<Operand> new_operands; @@ -974,6 +967,7 @@ void AggressiveDCEPass::InitExtensions() { "SPV_KHR_integer_dot_product", "SPV_EXT_shader_image_int64", "SPV_KHR_non_semantic_info", + "SPV_KHR_uniform_group_instructions", }); } diff --git a/source/opt/amd_ext_to_khr.cpp b/source/opt/amd_ext_to_khr.cpp index ccedc0bc..dd9bafda 100644 --- a/source/opt/amd_ext_to_khr.cpp +++ b/source/opt/amd_ext_to_khr.cpp @@ -584,9 +584,9 @@ bool ReplaceCubeFaceCoord(IRContext* ctx, Instruction* inst, } // Get the constants that will be used. - uint32_t f0_const_id = const_mgr->GetFloatConst(0.0); - uint32_t f2_const_id = const_mgr->GetFloatConst(2.0); - uint32_t f0_5_const_id = const_mgr->GetFloatConst(0.5); + uint32_t f0_const_id = const_mgr->GetFloatConstId(0.0); + uint32_t f2_const_id = const_mgr->GetFloatConstId(2.0); + uint32_t f0_5_const_id = const_mgr->GetFloatConstId(0.5); const analysis::Constant* vec_const = const_mgr->GetConstant(v2_float_type, {f0_5_const_id, f0_5_const_id}); uint32_t vec_const_id = @@ -731,12 +731,12 @@ bool ReplaceCubeFaceIndex(IRContext* ctx, Instruction* inst, } // Get the constants that will be used. - uint32_t f0_const_id = const_mgr->GetFloatConst(0.0); - uint32_t f1_const_id = const_mgr->GetFloatConst(1.0); - uint32_t f2_const_id = const_mgr->GetFloatConst(2.0); - uint32_t f3_const_id = const_mgr->GetFloatConst(3.0); - uint32_t f4_const_id = const_mgr->GetFloatConst(4.0); - uint32_t f5_const_id = const_mgr->GetFloatConst(5.0); + uint32_t f0_const_id = const_mgr->GetFloatConstId(0.0); + uint32_t f1_const_id = const_mgr->GetFloatConstId(1.0); + uint32_t f2_const_id = const_mgr->GetFloatConstId(2.0); + uint32_t f3_const_id = const_mgr->GetFloatConstId(3.0); + uint32_t f4_const_id = const_mgr->GetFloatConstId(4.0); + uint32_t f5_const_id = const_mgr->GetFloatConstId(5.0); // Extract the input values. Instruction* x = ir_builder.AddCompositeExtract(float_type_id, input_id, {0}); @@ -935,8 +935,7 @@ Pass::Status AmdExtensionToKhrPass::Process() { std::vector<Instruction*> to_be_killed; for (Instruction& inst : context()->module()->extensions()) { if (inst.opcode() == SpvOpExtension) { - if (ext_to_remove.count(reinterpret_cast<const char*>( - &(inst.GetInOperand(0).words[0]))) != 0) { + if (ext_to_remove.count(inst.GetInOperand(0).AsString()) != 0) { to_be_killed.push_back(&inst); } } @@ -944,8 +943,7 @@ Pass::Status AmdExtensionToKhrPass::Process() { for (Instruction& inst : context()->ext_inst_imports()) { if (inst.opcode() == SpvOpExtInstImport) { - if (ext_to_remove.count(reinterpret_cast<const char*>( - &(inst.GetInOperand(0).words[0]))) != 0) { + if (ext_to_remove.count(inst.GetInOperand(0).AsString()) != 0) { to_be_killed.push_back(&inst); } } diff --git a/source/opt/amd_ext_to_khr.h b/source/opt/amd_ext_to_khr.h index fd3dab4e..6a39d953 100644 --- a/source/opt/amd_ext_to_khr.h +++ b/source/opt/amd_ext_to_khr.h @@ -23,7 +23,7 @@ namespace spvtools { namespace opt { // Replaces the extensions VK_AMD_shader_ballot, VK_AMD_gcn_shader, and -// VK_AMD_shader_trinary_minmax with equivalant code using core instructions and +// VK_AMD_shader_trinary_minmax with equivalent code using core instructions and // capabilities. class AmdExtensionToKhrPass : public Pass { public: diff --git a/source/opt/basic_block.h b/source/opt/basic_block.h index 6741a50f..dd3b2e28 100644 --- a/source/opt/basic_block.h +++ b/source/opt/basic_block.h @@ -83,7 +83,7 @@ class BasicBlock { const Instruction* GetMergeInst() const; Instruction* GetMergeInst(); - // Returns the OpLoopMerge instruciton in this basic block, if it exists. + // Returns the OpLoopMerge instruction in this basic block, if it exists. // Otherwise return null. May be used whenever tail() can be used. const Instruction* GetLoopMergeInst() const; Instruction* GetLoopMergeInst(); diff --git a/source/opt/ccp_pass.cpp b/source/opt/ccp_pass.cpp index 8b896d50..5f855027 100644 --- a/source/opt/ccp_pass.cpp +++ b/source/opt/ccp_pass.cpp @@ -102,6 +102,34 @@ SSAPropagator::PropStatus CCPPass::VisitPhi(Instruction* phi) { return SSAPropagator::kInteresting; } +uint32_t CCPPass::ComputeLatticeMeet(Instruction* instr, uint32_t val2) { + // Given two values val1 and val2, the meet operation in the constant + // lattice uses the following rules: + // + // meet(val1, UNDEFINED) = val1 + // meet(val1, VARYING) = VARYING + // meet(val1, val2) = val1 if val1 == val2 + // meet(val1, val2) = VARYING if val1 != val2 + // + // When two different values meet, the result is always varying because CCP + // does not allow lateral transitions in the lattice. This prevents + // infinite cycles during propagation. + auto val1_it = values_.find(instr->result_id()); + if (val1_it == values_.end()) { + return val2; + } + + uint32_t val1 = val1_it->second; + if (IsVaryingValue(val1)) { + return val1; + } else if (IsVaryingValue(val2)) { + return val2; + } else if (val1 != val2) { + return kVaryingSSAId; + } + return val2; +} + SSAPropagator::PropStatus CCPPass::VisitAssignment(Instruction* instr) { assert(instr->result_id() != 0 && "Expecting an instruction that produces a result"); @@ -115,8 +143,10 @@ SSAPropagator::PropStatus CCPPass::VisitAssignment(Instruction* instr) { if (IsVaryingValue(it->second)) { return MarkInstructionVarying(instr); } else { - values_[instr->result_id()] = it->second; - return SSAPropagator::kInteresting; + uint32_t new_val = ComputeLatticeMeet(instr, it->second); + values_[instr->result_id()] = new_val; + return IsVaryingValue(new_val) ? SSAPropagator::kVarying + : SSAPropagator::kInteresting; } } return SSAPropagator::kNotInteresting; @@ -142,9 +172,13 @@ SSAPropagator::PropStatus CCPPass::VisitAssignment(Instruction* instr) { if (folded_inst != nullptr) { // We do not want to change the body of the function by adding new // instructions. When folding we can only generate new constants. - assert(folded_inst->IsConstant() && "CCP is only interested in constant."); - values_[instr->result_id()] = folded_inst->result_id(); - return SSAPropagator::kInteresting; + assert((folded_inst->IsConstant() || + IsSpecConstantInst(folded_inst->opcode())) && + "CCP is only interested in constant values."); + uint32_t new_val = ComputeLatticeMeet(instr, folded_inst->result_id()); + values_[instr->result_id()] = new_val; + return IsVaryingValue(new_val) ? SSAPropagator::kVarying + : SSAPropagator::kInteresting; } // Conservatively mark this instruction as varying if any input id is varying. diff --git a/source/opt/ccp_pass.h b/source/opt/ccp_pass.h index fb20c780..77ea9f80 100644 --- a/source/opt/ccp_pass.h +++ b/source/opt/ccp_pass.h @@ -92,6 +92,22 @@ class CCPPass : public MemPass { // generated during propagation. analysis::ConstantManager* const_mgr_; + // Returns a new value for |instr| by computing the meet operation between + // its existing value and |val2|. + // + // Given two values val1 and val2, the meet operation in the constant + // lattice uses the following rules: + // + // meet(val1, UNDEFINED) = val1 + // meet(val1, VARYING) = VARYING + // meet(val1, val2) = val1 if val1 == val2 + // meet(val1, val2) = VARYING if val1 != val2 + // + // When two different values meet, the result is always varying because CCP + // does not allow lateral transitions in the lattice. This prevents + // infinite cycles during propagation. + uint32_t ComputeLatticeMeet(Instruction* instr, uint32_t val2); + // Constant value table. Each entry <id, const_decl_id> in this map // represents the compile-time constant value for |id| as declared by // |const_decl_id|. Each |const_decl_id| in this table is an OpConstant diff --git a/source/opt/cfg.h b/source/opt/cfg.h index f2806822..33412f18 100644 --- a/source/opt/cfg.h +++ b/source/opt/cfg.h @@ -30,7 +30,7 @@ class CFG { public: explicit CFG(Module* module); - // Return the list of predecesors for basic block with label |blkid|. + // Return the list of predecessors for basic block with label |blkid|. // TODO(dnovillo): Move this to BasicBlock. const std::vector<uint32_t>& preds(uint32_t blk_id) const { assert(label2preds_.count(blk_id)); diff --git a/source/opt/compact_ids_pass.cpp b/source/opt/compact_ids_pass.cpp index 8815b8c6..70848d79 100644 --- a/source/opt/compact_ids_pass.cpp +++ b/source/opt/compact_ids_pass.cpp @@ -86,7 +86,8 @@ Pass::Status CompactIdsPass::Process() { }, true); - if (modified) { + if (context()->module()->id_bound() != result_id_mapping.size() + 1) { + modified = true; context()->module()->SetIdBound( static_cast<uint32_t>(result_id_mapping.size() + 1)); // There are ids in the feature manager that could now be invalid diff --git a/source/opt/const_folding_rules.cpp b/source/opt/const_folding_rules.cpp index 515a3ed5..249e11e5 100644 --- a/source/opt/const_folding_rules.cpp +++ b/source/opt/const_folding_rules.cpp @@ -22,6 +22,45 @@ namespace { const uint32_t kExtractCompositeIdInIdx = 0; +// Returns a constants with the value NaN of the given type. Only works for +// 32-bit and 64-bit float point types. Returns |nullptr| if an error occurs. +const analysis::Constant* GetNan(const analysis::Type* type, + analysis::ConstantManager* const_mgr) { + const analysis::Float* float_type = type->AsFloat(); + if (float_type == nullptr) { + return nullptr; + } + + switch (float_type->width()) { + case 32: + return const_mgr->GetFloatConst(std::numeric_limits<float>::quiet_NaN()); + case 64: + return const_mgr->GetDoubleConst( + std::numeric_limits<double>::quiet_NaN()); + default: + return nullptr; + } +} + +// Returns a constants with the value INF of the given type. Only works for +// 32-bit and 64-bit float point types. Returns |nullptr| if an error occurs. +const analysis::Constant* GetInf(const analysis::Type* type, + analysis::ConstantManager* const_mgr) { + const analysis::Float* float_type = type->AsFloat(); + if (float_type == nullptr) { + return nullptr; + } + + switch (float_type->width()) { + case 32: + return const_mgr->GetFloatConst(std::numeric_limits<float>::infinity()); + case 64: + return const_mgr->GetDoubleConst(std::numeric_limits<double>::infinity()); + default: + return nullptr; + } +} + // Returns true if |type| is Float or a vector of Float. bool HasFloatingPoint(const analysis::Type* type) { if (type->AsFloat()) { @@ -33,6 +72,23 @@ bool HasFloatingPoint(const analysis::Type* type) { return false; } +// Returns a constants with the value |-val| of the given type. Only works for +// 32-bit and 64-bit float point types. Returns |nullptr| if an error occurs. +const analysis::Constant* negateFPConst(const analysis::Type* result_type, + const analysis::Constant* val, + analysis::ConstantManager* const_mgr) { + const analysis::Float* float_type = result_type->AsFloat(); + assert(float_type != nullptr); + if (float_type->width() == 32) { + float fa = val->GetFloat(); + return const_mgr->GetFloatConst(-fa); + } else if (float_type->width() == 64) { + double da = val->GetDouble(); + return const_mgr->GetDoubleConst(-da); + } + return nullptr; +} + // Folds an OpcompositeExtract where input is a composite constant. ConstantFoldingRule FoldExtractWithConstants() { return [](IRContext* context, Instruction* inst, @@ -492,7 +548,60 @@ ConstantFoldingRule FoldQuantizeToF16() { ConstantFoldingRule FoldFSub() { return FoldFPBinaryOp(FOLD_FPARITH_OP(-)); } ConstantFoldingRule FoldFAdd() { return FoldFPBinaryOp(FOLD_FPARITH_OP(+)); } ConstantFoldingRule FoldFMul() { return FoldFPBinaryOp(FOLD_FPARITH_OP(*)); } -ConstantFoldingRule FoldFDiv() { return FoldFPBinaryOp(FOLD_FPARITH_OP(/)); } + +// Returns the constant that results from evaluating |numerator| / 0.0. Returns +// |nullptr| if the result could not be evaluated. +const analysis::Constant* FoldFPScalarDivideByZero( + const analysis::Type* result_type, const analysis::Constant* numerator, + analysis::ConstantManager* const_mgr) { + if (numerator == nullptr) { + return nullptr; + } + + if (numerator->IsZero()) { + return GetNan(result_type, const_mgr); + } + + const analysis::Constant* result = GetInf(result_type, const_mgr); + if (result == nullptr) { + return nullptr; + } + + if (numerator->AsFloatConstant()->GetValueAsDouble() < 0.0) { + result = negateFPConst(result_type, result, const_mgr); + } + return result; +} + +// Returns the result of folding |numerator| / |denominator|. Returns |nullptr| +// if it cannot be folded. +const analysis::Constant* FoldScalarFPDivide( + const analysis::Type* result_type, const analysis::Constant* numerator, + const analysis::Constant* denominator, + analysis::ConstantManager* const_mgr) { + if (denominator == nullptr) { + return nullptr; + } + + if (denominator->IsZero()) { + return FoldFPScalarDivideByZero(result_type, numerator, const_mgr); + } + + const analysis::FloatConstant* denominator_float = + denominator->AsFloatConstant(); + if (denominator_float && denominator->GetValueAsDouble() == -0.0) { + const analysis::Constant* result = + FoldFPScalarDivideByZero(result_type, numerator, const_mgr); + if (result != nullptr) + result = negateFPConst(result_type, result, const_mgr); + return result; + } else { + return FOLD_FPARITH_OP(/)(result_type, numerator, denominator, const_mgr); + } +} + +// Returns the constant folding rule to fold |OpFDiv| with two constants. +ConstantFoldingRule FoldFDiv() { return FoldFPBinaryOp(FoldScalarFPDivide); } bool CompareFloatingPoint(bool op_result, bool op_unordered, bool need_ordered) { @@ -655,20 +764,7 @@ UnaryScalarFoldingRule FoldFNegateOp() { analysis::ConstantManager* const_mgr) -> const analysis::Constant* { assert(result_type != nullptr && a != nullptr); assert(result_type == a->type()); - const analysis::Float* float_type = result_type->AsFloat(); - assert(float_type != nullptr); - if (float_type->width() == 32) { - float fa = a->GetFloat(); - utils::FloatProxy<float> result(-fa); - std::vector<uint32_t> words = result.GetWords(); - return const_mgr->GetConstant(result_type, words); - } else if (float_type->width() == 64) { - double da = a->GetDouble(); - utils::FloatProxy<double> result(-da); - std::vector<uint32_t> words = result.GetWords(); - return const_mgr->GetConstant(result_type, words); - } - return nullptr; + return negateFPConst(result_type, a, const_mgr); }; } @@ -1250,7 +1346,7 @@ void ConstantFoldingRules::AddFoldingRules() { FoldFPUnaryOp(FoldFTranscendentalUnary(std::log))); #ifdef __ANDROID__ - // Android NDK r15c tageting ABI 15 doesn't have full support for C++11 + // Android NDK r15c targeting ABI 15 doesn't have full support for C++11 // (no std::exp2/log2). ::exp2 is available from C99 but ::log2 isn't // available up until ABI 18 so we use a shim auto log2_shim = [](double v) -> double { return log(v) / log(2.0); }; diff --git a/source/opt/constants.cpp b/source/opt/constants.cpp index 020e248b..bcff08c1 100644 --- a/source/opt/constants.cpp +++ b/source/opt/constants.cpp @@ -158,6 +158,7 @@ Type* ConstantManager::GetType(const Instruction* inst) const { std::vector<const Constant*> ConstantManager::GetOperandConstants( const Instruction* inst) const { std::vector<const Constant*> constants; + constants.reserve(inst->NumInOperands()); for (uint32_t i = 0; i < inst->NumInOperands(); i++) { const Operand* operand = &inst->GetInOperand(i); if (operand->type != SPV_OPERAND_TYPE_ID) { @@ -420,13 +421,30 @@ const Constant* ConstantManager::GetNumericVectorConstantWithWords( return GetConstant(type, element_ids); } -uint32_t ConstantManager::GetFloatConst(float val) { +uint32_t ConstantManager::GetFloatConstId(float val) { + const Constant* c = GetFloatConst(val); + return GetDefiningInstruction(c)->result_id(); +} + +const Constant* ConstantManager::GetFloatConst(float val) { Type* float_type = context()->get_type_mgr()->GetFloatType(); utils::FloatProxy<float> v(val); const Constant* c = GetConstant(float_type, v.GetWords()); + return c; +} + +uint32_t ConstantManager::GetDoubleConstId(double val) { + const Constant* c = GetDoubleConst(val); return GetDefiningInstruction(c)->result_id(); } +const Constant* ConstantManager::GetDoubleConst(double val) { + Type* float_type = context()->get_type_mgr()->GetDoubleType(); + utils::FloatProxy<double> v(val); + const Constant* c = GetConstant(float_type, v.GetWords()); + return c; +} + uint32_t ConstantManager::GetSIntConst(int32_t val) { Type* sint_type = context()->get_type_mgr()->GetSIntType(); const Constant* c = GetConstant(sint_type, {static_cast<uint32_t>(val)}); diff --git a/source/opt/constants.h b/source/opt/constants.h index 52bd809a..c039ae08 100644 --- a/source/opt/constants.h +++ b/source/opt/constants.h @@ -541,7 +541,7 @@ class ConstantManager { // instruction at the end of the current module's types section. // // |type_id| is an optional argument for disambiguating equivalent types. If - // |type_id| is specified, the contant returned will have that type id. + // |type_id| is specified, the constant returned will have that type id. Instruction* GetDefiningInstruction(const Constant* c, uint32_t type_id = 0, Module::inst_iterator* pos = nullptr); @@ -637,7 +637,16 @@ class ConstantManager { } // Returns the id of a 32-bit floating point constant with value |val|. - uint32_t GetFloatConst(float val); + uint32_t GetFloatConstId(float val); + + // Returns a 32-bit float constant with the given value. + const Constant* GetFloatConst(float val); + + // Returns the id of a 64-bit floating point constant with value |val|. + uint32_t GetDoubleConstId(double val); + + // Returns a 64-bit float constant with the given value. + const Constant* GetDoubleConst(double val); // Returns the id of a 32-bit signed integer constant with value |val|. uint32_t GetSIntConst(int32_t val); diff --git a/source/opt/convert_to_half_pass.cpp b/source/opt/convert_to_half_pass.cpp index b127eabe..4086e31a 100644 --- a/source/opt/convert_to_half_pass.cpp +++ b/source/opt/convert_to_half_pass.cpp @@ -181,7 +181,7 @@ bool ConvertToHalfPass::ProcessPhi(Instruction* inst, uint32_t from_width, uint32_t to_width) { // Add converts of any float operands to to_width if they are of from_width. // If converting to 16, change type of phi to float16 equivalent and remember - // result id. Converts need to be added to preceeding blocks. + // result id. Converts need to be added to preceding blocks. uint32_t ocnt = 0; uint32_t* prev_idp; bool modified = false; diff --git a/source/opt/copy_prop_arrays.cpp b/source/opt/copy_prop_arrays.cpp index 62ed5e77..321d4969 100644 --- a/source/opt/copy_prop_arrays.cpp +++ b/source/opt/copy_prop_arrays.cpp @@ -745,11 +745,11 @@ void CopyPropagateArrays::UpdateUses(Instruction* original_ptr_inst, context()->AnalyzeUses(use); } break; + case SpvOpDecorate: + // We treat an OpImageTexelPointer as a load. The result type should + // always have the Image storage class, and should not need to be + // updated. case SpvOpImageTexelPointer: - // We treat an OpImageTexelPointer as a load. The result type should - // always have the Image storage class, and should not need to be - // updated. - // Replace the actual use. context()->ForgetUses(use); use->SetOperand(index, {new_ptr_inst->result_id()}); diff --git a/source/opt/copy_prop_arrays.h b/source/opt/copy_prop_arrays.h index f4314a74..46a508cf 100644 --- a/source/opt/copy_prop_arrays.h +++ b/source/opt/copy_prop_arrays.h @@ -35,7 +35,7 @@ namespace opt { // // The hard part is keeping all of the types correct. We do not want to // have to do too large a search to update everything, which may not be -// possible, do we give up if we see any instruction that might be hard to +// possible, so we give up if we see any instruction that might be hard to // update. class CopyPropagateArrays : public MemPass { diff --git a/source/opt/dead_branch_elim_pass.cpp b/source/opt/dead_branch_elim_pass.cpp index 356dbcb3..cc616ca6 100644 --- a/source/opt/dead_branch_elim_pass.cpp +++ b/source/opt/dead_branch_elim_pass.cpp @@ -207,7 +207,7 @@ bool DeadBranchElimPass::SimplifyBranch(BasicBlock* block, Instruction::OperandList new_operands; new_operands.push_back(terminator->GetInOperand(0)); new_operands.push_back({SPV_OPERAND_TYPE_ID, {live_lab_id}}); - terminator->SetInOperands(move(new_operands)); + terminator->SetInOperands(std::move(new_operands)); context()->UpdateDefUse(terminator); } else { // Check if the merge instruction is still needed because of a diff --git a/source/opt/dead_branch_elim_pass.h b/source/opt/dead_branch_elim_pass.h index 7841bc47..198bad2d 100644 --- a/source/opt/dead_branch_elim_pass.h +++ b/source/opt/dead_branch_elim_pass.h @@ -98,7 +98,7 @@ class DeadBranchElimPass : public MemPass { // Fix phis in reachable blocks so that only live (or unremovable) incoming // edges are present. If the block now only has a single live incoming edge, // remove the phi and replace its uses with its data input. If the single - // remaining incoming edge is from the phi itself, the the phi is in an + // remaining incoming edge is from the phi itself, the phi is in an // unreachable single block loop. Either the block is dead and will be // removed, or it's reachable from an unreachable continue target. In the // latter case that continue target block will be collapsed into a block that @@ -158,7 +158,7 @@ class DeadBranchElimPass : public MemPass { uint32_t cont_id, uint32_t header_id, uint32_t merge_id, std::unordered_set<BasicBlock*>* blocks_with_back_edges); - // Returns true if there is a brach to the merge node of the selection + // Returns true if there is a branch to the merge node of the selection // construct |switch_header_id| that is inside a nested selection construct or // in the header of the nested selection construct. bool SwitchHasNestedBreak(uint32_t switch_header_id); diff --git a/source/opt/debug_info_manager.cpp b/source/opt/debug_info_manager.cpp index 060e0d93..c1df6258 100644 --- a/source/opt/debug_info_manager.cpp +++ b/source/opt/debug_info_manager.cpp @@ -149,7 +149,7 @@ void DebugInfoManager::RegisterDbgDeclare(uint32_t var_id, // Create new constant directly into global value area, bypassing the // Constant manager. This is used when the DefUse or Constant managers // are invalid and cannot be regenerated due to the module being in an -// inconsistant state e.g. in the middle of significant modification +// inconsistent state e.g. in the middle of significant modification // such as inlining. Invalidate Constant and DefUse managers if used. uint32_t AddNewConstInGlobals(IRContext* context, uint32_t const_value) { uint32_t id = context->TakeNextId(); diff --git a/source/opt/def_use_manager.cpp b/source/opt/def_use_manager.cpp index 394b9fa1..e1e441e0 100644 --- a/source/opt/def_use_manager.cpp +++ b/source/opt/def_use_manager.cpp @@ -13,16 +13,23 @@ // limitations under the License. #include "source/opt/def_use_manager.h" - -#include <iostream> - -#include "source/opt/log.h" -#include "source/opt/reflect.h" +#include "source/util/make_unique.h" namespace spvtools { namespace opt { namespace analysis { +// Don't compact before we have a reasonable number of ids allocated (~32kb). +static const size_t kCompactThresholdMinTotalIds = (8 * 1024); +// Compact when fewer than this fraction of the storage is used (should be 2^n +// for performance). +static const size_t kCompactThresholdFractionFreeIds = 8; + +DefUseManager::DefUseManager() { + use_pool_ = MakeUnique<UseListPool>(); + used_id_pool_ = MakeUnique<UsedIdListPool>(); +} + void DefUseManager::AnalyzeInstDef(Instruction* inst) { const uint32_t def_id = inst->result_id(); if (def_id != 0) { @@ -39,15 +46,15 @@ void DefUseManager::AnalyzeInstDef(Instruction* inst) { } void DefUseManager::AnalyzeInstUse(Instruction* inst) { + // It might have existed before. + EraseUseRecordsOfOperandIds(inst); + // Create entry for the given instruction. Note that the instruction may // not have any in-operands. In such cases, we still need a entry for those // instructions so this manager knows it has seen the instruction later. - auto* used_ids = &inst_to_used_ids_[inst]; - if (used_ids->size()) { - EraseUseRecordsOfOperandIds(inst); - used_ids = &inst_to_used_ids_[inst]; - } - used_ids->clear(); // It might have existed before. + UsedIdList& used_ids = + inst_to_used_id_.insert({inst, UsedIdList(used_id_pool_.get())}) + .first->second; for (uint32_t i = 0; i < inst->NumOperands(); ++i) { switch (inst->GetOperand(i).type) { @@ -58,9 +65,18 @@ void DefUseManager::AnalyzeInstUse(Instruction* inst) { case SPV_OPERAND_TYPE_SCOPE_ID: { uint32_t use_id = inst->GetSingleWordOperand(i); Instruction* def = GetDef(use_id); - if (!def) assert(false && "Definition is not registered."); - id_to_users_.insert(UserEntry(def, inst)); - used_ids->push_back(use_id); + assert(def && "Definition is not registered."); + + // Add to inst's use records + used_ids.push_back(use_id); + + // Add to the users, taking care to avoid adding duplicates. We know + // the duplicate for this instruction will always be at the tail. + UseList& list = inst_to_users_.insert({def, UseList(use_pool_.get())}) + .first->second; + if (list.empty() || list.back() != inst) { + list.push_back(inst); + } } break; default: break; @@ -99,23 +115,6 @@ const Instruction* DefUseManager::GetDef(uint32_t id) const { return iter->second; } -DefUseManager::IdToUsersMap::const_iterator DefUseManager::UsersBegin( - const Instruction* def) const { - return id_to_users_.lower_bound( - UserEntry(const_cast<Instruction*>(def), nullptr)); -} - -bool DefUseManager::UsersNotEnd(const IdToUsersMap::const_iterator& iter, - const IdToUsersMap::const_iterator& cached_end, - const Instruction* inst) const { - return (iter != cached_end && iter->first == inst); -} - -bool DefUseManager::UsersNotEnd(const IdToUsersMap::const_iterator& iter, - const Instruction* inst) const { - return UsersNotEnd(iter, id_to_users_.end(), inst); -} - bool DefUseManager::WhileEachUser( const Instruction* def, const std::function<bool(Instruction*)>& f) const { // Ensure that |def| has been registered. @@ -123,9 +122,11 @@ bool DefUseManager::WhileEachUser( "Definition is not registered."); if (!def->HasResultId()) return true; - auto end = id_to_users_.end(); - for (auto iter = UsersBegin(def); UsersNotEnd(iter, end, def); ++iter) { - if (!f(iter->second)) return false; + auto iter = inst_to_users_.find(def); + if (iter != inst_to_users_.end()) { + for (Instruction* user : iter->second) { + if (!f(user)) return false; + } } return true; } @@ -156,14 +157,15 @@ bool DefUseManager::WhileEachUse( "Definition is not registered."); if (!def->HasResultId()) return true; - auto end = id_to_users_.end(); - for (auto iter = UsersBegin(def); UsersNotEnd(iter, end, def); ++iter) { - Instruction* user = iter->second; - for (uint32_t idx = 0; idx != user->NumOperands(); ++idx) { - const Operand& op = user->GetOperand(idx); - if (op.type != SPV_OPERAND_TYPE_RESULT_ID && spvIsIdType(op.type)) { - if (def->result_id() == op.words[0]) { - if (!f(user, idx)) return false; + auto iter = inst_to_users_.find(def); + if (iter != inst_to_users_.end()) { + for (Instruction* user : iter->second) { + for (uint32_t idx = 0; idx != user->NumOperands(); ++idx) { + const Operand& op = user->GetOperand(idx); + if (op.type != SPV_OPERAND_TYPE_RESULT_ID && spvIsIdType(op.type)) { + if (def->result_id() == op.words[0]) { + if (!f(user, idx)) return false; + } } } } @@ -235,17 +237,18 @@ void DefUseManager::AnalyzeDefUse(Module* module) { } void DefUseManager::ClearInst(Instruction* inst) { - auto iter = inst_to_used_ids_.find(inst); - if (iter != inst_to_used_ids_.end()) { + if (inst_to_used_id_.find(inst) != inst_to_used_id_.end()) { EraseUseRecordsOfOperandIds(inst); - if (inst->result_id() != 0) { - // Remove all uses of this inst. - auto users_begin = UsersBegin(inst); - auto end = id_to_users_.end(); - auto new_end = users_begin; - for (; UsersNotEnd(new_end, end, inst); ++new_end) { + uint32_t const result_id = inst->result_id(); + if (result_id != 0) { + // For each using instruction, remove result_id from their used ids. + auto iter = inst_to_users_.find(inst); + if (iter != inst_to_users_.end()) { + for (Instruction* use : iter->second) { + inst_to_used_id_.at(use).remove_first(result_id); + } + inst_to_users_.erase(iter); } - id_to_users_.erase(users_begin, new_end); id_to_def_.erase(inst->result_id()); } } @@ -254,59 +257,113 @@ void DefUseManager::ClearInst(Instruction* inst) { void DefUseManager::EraseUseRecordsOfOperandIds(const Instruction* inst) { // Go through all ids used by this instruction, remove this instruction's // uses of them. - auto iter = inst_to_used_ids_.find(inst); - if (iter != inst_to_used_ids_.end()) { - for (auto use_id : iter->second) { - id_to_users_.erase( - UserEntry(GetDef(use_id), const_cast<Instruction*>(inst))); + auto iter = inst_to_used_id_.find(inst); + if (iter != inst_to_used_id_.end()) { + const UsedIdList& used_ids = iter->second; + for (uint32_t def_id : used_ids) { + auto def_iter = inst_to_users_.find(GetDef(def_id)); + if (def_iter != inst_to_users_.end()) { + def_iter->second.remove_first(const_cast<Instruction*>(inst)); + } + } + inst_to_used_id_.erase(inst); + + // If we're using only a fraction of the space in used_ids_, compact storage + // to prevent memory usage from being unbounded. + if (used_id_pool_->total_nodes() > kCompactThresholdMinTotalIds && + used_id_pool_->used_nodes() < + used_id_pool_->total_nodes() / kCompactThresholdFractionFreeIds) { + CompactStorage(); } - inst_to_used_ids_.erase(inst); } } -bool operator==(const DefUseManager& lhs, const DefUseManager& rhs) { +void DefUseManager::CompactStorage() { + CompactUseRecords(); + CompactUsedIds(); +} + +void DefUseManager::CompactUseRecords() { + std::unique_ptr<UseListPool> new_pool = MakeUnique<UseListPool>(); + for (auto& iter : inst_to_users_) { + iter.second.move_nodes(new_pool.get()); + } + use_pool_ = std::move(new_pool); +} + +void DefUseManager::CompactUsedIds() { + std::unique_ptr<UsedIdListPool> new_pool = MakeUnique<UsedIdListPool>(); + for (auto& iter : inst_to_used_id_) { + iter.second.move_nodes(new_pool.get()); + } + used_id_pool_ = std::move(new_pool); +} + +bool CompareAndPrintDifferences(const DefUseManager& lhs, + const DefUseManager& rhs) { + bool same = true; + if (lhs.id_to_def_ != rhs.id_to_def_) { for (auto p : lhs.id_to_def_) { if (rhs.id_to_def_.find(p.first) == rhs.id_to_def_.end()) { - return false; + printf("Diff in id_to_def: missing value in rhs\n"); } } for (auto p : rhs.id_to_def_) { if (lhs.id_to_def_.find(p.first) == lhs.id_to_def_.end()) { - return false; + printf("Diff in id_to_def: missing value in lhs\n"); } } - return false; + same = false; } - if (lhs.id_to_users_ != rhs.id_to_users_) { - for (auto p : lhs.id_to_users_) { - if (rhs.id_to_users_.count(p) == 0) { - return false; - } + for (const auto& l : lhs.inst_to_used_id_) { + std::set<uint32_t> ul, ur; + lhs.ForEachUse(l.first, + [&ul](Instruction*, uint32_t id) { ul.insert(id); }); + rhs.ForEachUse(l.first, + [&ur](Instruction*, uint32_t id) { ur.insert(id); }); + if (ul.size() != ur.size()) { + printf( + "Diff in inst_to_used_id_: different number of used ids (%zu != %zu)", + ul.size(), ur.size()); + same = false; + } else if (ul != ur) { + printf("Diff in inst_to_used_id_: different used ids\n"); + same = false; } - for (auto p : rhs.id_to_users_) { - if (lhs.id_to_users_.count(p) == 0) { - return false; - } + } + for (const auto& r : rhs.inst_to_used_id_) { + auto iter_l = lhs.inst_to_used_id_.find(r.first); + if (r.second.empty() && + !(iter_l == lhs.inst_to_used_id_.end() || iter_l->second.empty())) { + printf("Diff in inst_to_used_id_: unexpected instr in rhs\n"); + same = false; } - return false; } - if (lhs.inst_to_used_ids_ != rhs.inst_to_used_ids_) { - for (auto p : lhs.inst_to_used_ids_) { - if (rhs.inst_to_used_ids_.count(p.first) == 0) { - return false; - } + for (const auto& l : lhs.inst_to_users_) { + std::set<Instruction*> ul, ur; + lhs.ForEachUser(l.first, [&ul](Instruction* use) { ul.insert(use); }); + rhs.ForEachUser(l.first, [&ur](Instruction* use) { ur.insert(use); }); + if (ul.size() != ur.size()) { + printf("Diff in inst_to_users_: different number of users (%zu != %zu)", + ul.size(), ur.size()); + same = false; + } else if (ul != ur) { + printf("Diff in inst_to_users_: different users\n"); + same = false; } - for (auto p : rhs.inst_to_used_ids_) { - if (lhs.inst_to_used_ids_.count(p.first) == 0) { - return false; - } + } + for (const auto& r : rhs.inst_to_users_) { + auto iter_l = lhs.inst_to_users_.find(r.first); + if (r.second.empty() && + !(iter_l == lhs.inst_to_users_.end() || iter_l->second.empty())) { + printf("Diff in inst_to_users_: unexpected instr in rhs\n"); + same = false; } - return false; } - return true; + return same; } } // namespace analysis diff --git a/source/opt/def_use_manager.h b/source/opt/def_use_manager.h index 0499e82b..cf6cbdf5 100644 --- a/source/opt/def_use_manager.h +++ b/source/opt/def_use_manager.h @@ -15,14 +15,13 @@ #ifndef SOURCE_OPT_DEF_USE_MANAGER_H_ #define SOURCE_OPT_DEF_USE_MANAGER_H_ -#include <list> #include <set> #include <unordered_map> -#include <utility> #include <vector> #include "source/opt/instruction.h" #include "source/opt/module.h" +#include "source/util/pooled_linked_list.h" #include "spirv-tools/libspirv.hpp" namespace spvtools { @@ -51,59 +50,16 @@ inline bool operator<(const Use& lhs, const Use& rhs) { return lhs.operand_index < rhs.operand_index; } -// Definition and user pair. -// -// The first element of the pair is the definition. -// The second element of the pair is the user. -// -// Definition should never be null. User can be null, however, such an entry -// should be used only for searching (e.g. all users of a particular definition) -// and never stored in a container. -using UserEntry = std::pair<Instruction*, Instruction*>; - -// Orders UserEntry for use in associative containers (i.e. less than ordering). -// -// The definition of an UserEntry is treated as the major key and the users as -// the minor key so that all the users of a particular definition are -// consecutive in a container. -// -// A null user always compares less than a real user. This is done to provide -// easy values to search for the beginning of the users of a particular -// definition (i.e. using {def, nullptr}). -struct UserEntryLess { - bool operator()(const UserEntry& lhs, const UserEntry& rhs) const { - // If lhs.first and rhs.first are both null, fall through to checking the - // second entries. - if (!lhs.first && rhs.first) return true; - if (lhs.first && !rhs.first) return false; - - // If neither definition is null, then compare unique ids. - if (lhs.first && rhs.first) { - if (lhs.first->unique_id() < rhs.first->unique_id()) return true; - if (rhs.first->unique_id() < lhs.first->unique_id()) return false; - } - - // Return false on equality. - if (!lhs.second && !rhs.second) return false; - if (!lhs.second) return true; - if (!rhs.second) return false; - - // If neither user is null then compare unique ids. - return lhs.second->unique_id() < rhs.second->unique_id(); - } -}; - // A class for analyzing and managing defs and uses in an Module. class DefUseManager { public: using IdToDefMap = std::unordered_map<uint32_t, Instruction*>; - using IdToUsersMap = std::set<UserEntry, UserEntryLess>; // Constructs a def-use manager from the given |module|. All internal messages // will be communicated to the outside via the given message |consumer|. This // instance only keeps a reference to the |consumer|, so the |consumer| should // outlive this instance. - DefUseManager(Module* module) { AnalyzeDefUse(module); } + DefUseManager(Module* module) : DefUseManager() { AnalyzeDefUse(module); } DefUseManager(const DefUseManager&) = delete; DefUseManager(DefUseManager&&) = delete; @@ -191,14 +147,12 @@ class DefUseManager { // Returns the annotation instrunctions which are a direct use of the given // |id|. This means when the decorations are applied through decoration // group(s), this function will just return the OpGroupDecorate - // instrcution(s) which refer to the given id as an operand. The OpDecorate + // instruction(s) which refer to the given id as an operand. The OpDecorate // instructions which decorate the decoration group will not be returned. std::vector<Instruction*> GetAnnotations(uint32_t id) const; // Returns the map from ids to their def instructions. const IdToDefMap& id_to_defs() const { return id_to_def_; } - // Returns the map from instructions to their users. - const IdToUsersMap& id_to_users() const { return id_to_users_; } // Clear the internal def-use record of the given instruction |inst|. This // method will update the use information of the operand ids of |inst|. The @@ -210,43 +164,43 @@ class DefUseManager { // Erases the records that a given instruction uses its operand ids. void EraseUseRecordsOfOperandIds(const Instruction* inst); - friend bool operator==(const DefUseManager&, const DefUseManager&); - friend bool operator!=(const DefUseManager& lhs, const DefUseManager& rhs) { - return !(lhs == rhs); - } + friend bool CompareAndPrintDifferences(const DefUseManager&, + const DefUseManager&); - // If |inst| has not already been analysed, then analyses its defintion and + // If |inst| has not already been analysed, then analyses its definition and // uses. void UpdateDefUse(Instruction* inst); + // Compacts any internal storage to save memory. + void CompactStorage(); + private: - using InstToUsedIdsMap = - std::unordered_map<const Instruction*, std::vector<uint32_t>>; + using UseList = spvtools::utils::PooledLinkedList<Instruction*>; + using UseListPool = spvtools::utils::PooledLinkedListNodes<Instruction*>; + // Stores linked lists of Instructions using a def. + using InstToUsersMap = std::unordered_map<const Instruction*, UseList>; - // Returns the first location that {|def|, nullptr} could be inserted into the - // users map without violating ordering. - IdToUsersMap::const_iterator UsersBegin(const Instruction* def) const; + using UsedIdList = spvtools::utils::PooledLinkedList<uint32_t>; + using UsedIdListPool = spvtools::utils::PooledLinkedListNodes<uint32_t>; + // Stores mapping from instruction to their UsedIdRange. + using InstToUsedIdMap = std::unordered_map<const Instruction*, UsedIdList>; - // Returns true if |iter| has not reached the end of |def|'s users. - // - // In the first version |iter| is compared against the end of the map for - // validity before other checks. In the second version, |iter| is compared - // against |cached_end| for validity before other checks. This allows caching - // the map's end which is a performance improvement on some platforms. - bool UsersNotEnd(const IdToUsersMap::const_iterator& iter, - const Instruction* def) const; - bool UsersNotEnd(const IdToUsersMap::const_iterator& iter, - const IdToUsersMap::const_iterator& cached_end, - const Instruction* def) const; + DefUseManager(); // Analyzes the defs and uses in the given |module| and populates data // structures in this class. Does nothing if |module| is nullptr. void AnalyzeDefUse(Module* module); - IdToDefMap id_to_def_; // Mapping from ids to their definitions - IdToUsersMap id_to_users_; // Mapping from ids to their users - // Mapping from instructions to the ids used in the instruction. - InstToUsedIdsMap inst_to_used_ids_; + // Removes unused entries in used_records_ and used_ids_. + void CompactUseRecords(); + void CompactUsedIds(); + + IdToDefMap id_to_def_; // Mapping from ids to their definitions + InstToUsersMap inst_to_users_; // Map from def to uses. + std::unique_ptr<UseListPool> use_pool_; + + std::unique_ptr<UsedIdListPool> used_id_pool_; + InstToUsedIdMap inst_to_used_id_; // Map from instruction to used ids. }; } // namespace analysis diff --git a/source/opt/desc_sroa.cpp b/source/opt/desc_sroa.cpp index bcbdde94..b130ca80 100644 --- a/source/opt/desc_sroa.cpp +++ b/source/opt/desc_sroa.cpp @@ -118,7 +118,7 @@ bool DescriptorScalarReplacement::ReplaceAccessChain(Instruction* var, if (use->NumInOperands() == 2) { // We are not indexing into the replacement variable. We can replaces the - // access chain with the replacement varibale itself. + // access chain with the replacement variable itself. context()->ReplaceAllUsesWith(use->result_id(), replacement_var); context()->KillInst(use); return true; @@ -135,8 +135,8 @@ bool DescriptorScalarReplacement::ReplaceAccessChain(Instruction* var, // Use the replacement variable as the base address. new_operands.push_back({SPV_OPERAND_TYPE_ID, {replacement_var}}); - // Drop the first index because it is consumed by the replacment, and copy the - // rest. + // Drop the first index because it is consumed by the replacement, and copy + // the rest. for (uint32_t i = 4; i < use->NumOperands(); i++) { new_operands.emplace_back(use->GetOperand(i)); } @@ -169,7 +169,7 @@ void DescriptorScalarReplacement::CopyDecorationsForNewVariable( Instruction* old_var, uint32_t index, uint32_t new_var_id, uint32_t new_var_ptr_type_id, const bool is_old_var_array, const bool is_old_var_struct, Instruction* old_var_type) { - // Handle OpDecorate instructions. + // Handle OpDecorate and OpDecorateString instructions. for (auto old_decoration : get_decoration_mgr()->GetDecorationsFor(old_var->result_id(), true)) { uint32_t new_binding = 0; @@ -212,7 +212,8 @@ uint32_t DescriptorScalarReplacement::GetNewBindingForElement( void DescriptorScalarReplacement::CreateNewDecorationForNewVariable( Instruction* old_decoration, uint32_t new_var_id, uint32_t new_binding) { - assert(old_decoration->opcode() == SpvOpDecorate); + assert(old_decoration->opcode() == SpvOpDecorate || + old_decoration->opcode() == SpvOpDecorateString); std::unique_ptr<Instruction> new_decoration(old_decoration->Clone(context())); new_decoration->SetInOperand(0, {new_var_id}); diff --git a/source/opt/desc_sroa.h b/source/opt/desc_sroa.h index fea06255..6a24fd87 100644 --- a/source/opt/desc_sroa.h +++ b/source/opt/desc_sroa.h @@ -115,10 +115,11 @@ class DescriptorScalarReplacement : public Pass { const bool is_old_var_struct, Instruction* old_var_type); - // Create a new OpDecorate instruction by cloning |old_decoration|. The new - // OpDecorate instruction will be used for a variable whose id is - // |new_var_ptr_type_id|. If |old_decoration| is a decoration for a binding, - // the new OpDecorate instruction will have |new_binding| as its binding. + // Create a new OpDecorate(String) instruction by cloning |old_decoration|. + // The new OpDecorate(String) instruction will be used for a variable whose id + // is |new_var_ptr_type_id|. If |old_decoration| is a decoration for a + // binding, the new OpDecorate(String) instruction will have |new_binding| as + // its binding. void CreateNewDecorationForNewVariable(Instruction* old_decoration, uint32_t new_var_id, uint32_t new_binding); @@ -131,7 +132,7 @@ class DescriptorScalarReplacement : public Pass { // A map from an OpVariable instruction to the set of variables that will be // used to replace it. The entry |replacement_variables_[var][i]| is the id of - // a variable that will be used in the place of the the ith element of the + // a variable that will be used in the place of the ith element of the // array |var|. If the entry is |0|, then the variable has not been // created yet. std::map<Instruction*, std::vector<uint32_t>> replacement_variables_; diff --git a/source/opt/dominator_tree.cpp b/source/opt/dominator_tree.cpp index 55287f44..d86de151 100644 --- a/source/opt/dominator_tree.cpp +++ b/source/opt/dominator_tree.cpp @@ -48,7 +48,7 @@ namespace { // BBType - BasicBlock type. Will either be BasicBlock or DominatorTreeNode // SuccessorLambda - Lamdba matching the signature of 'const // std::vector<BBType>*(const BBType *A)'. Will return a vector of the nodes -// succeding BasicBlock A. +// succeeding BasicBlock A. // PostLambda - Lamdba matching the signature of 'void (const BBType*)' will be // called on each node traversed AFTER their children. // PreLambda - Lamdba matching the signature of 'void (const BBType*)' will be @@ -69,7 +69,7 @@ static void DepthFirstSearch(const BBType* bb, SuccessorLambda successors, // BBType - BasicBlock type. Will either be BasicBlock or DominatorTreeNode // SuccessorLambda - Lamdba matching the signature of 'const // std::vector<BBType>*(const BBType *A)'. Will return a vector of the nodes -// succeding BasicBlock A. +// succeeding BasicBlock A. // PostLambda - Lamdba matching the signature of 'void (const BBType*)' will be // called on each node traversed after their children. template <typename BBType, typename SuccessorLambda, typename PostLambda> diff --git a/source/opt/dominator_tree.h b/source/opt/dominator_tree.h index 0024bc50..1674b228 100644 --- a/source/opt/dominator_tree.h +++ b/source/opt/dominator_tree.h @@ -278,7 +278,7 @@ class DominatorTree { private: // Wrapper function which gets the list of pairs of each BasicBlocks to its - // immediately dominating BasicBlock and stores the result in the the edges + // immediately dominating BasicBlock and stores the result in the edges // parameter. // // The |edges| vector will contain the dominator tree as pairs of nodes. diff --git a/source/opt/eliminate_dead_input_components_pass.cpp b/source/opt/eliminate_dead_input_components_pass.cpp new file mode 100644 index 00000000..f383136d --- /dev/null +++ b/source/opt/eliminate_dead_input_components_pass.cpp @@ -0,0 +1,146 @@ +// Copyright (c) 2022 The Khronos Group Inc. +// Copyright (c) 2022 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 "source/opt/eliminate_dead_input_components_pass.h" + +#include <set> +#include <vector> + +#include "source/opt/instruction.h" +#include "source/opt/ir_builder.h" +#include "source/opt/ir_context.h" +#include "source/util/bit_vector.h" + +namespace { + +const uint32_t kAccessChainBaseInIdx = 0; +const uint32_t kAccessChainIndex0InIdx = 1; +const uint32_t kConstantValueInIdx = 0; +const uint32_t kVariableStorageClassInIdx = 0; + +} // namespace + +namespace spvtools { +namespace opt { + +Pass::Status EliminateDeadInputComponentsPass::Process() { + // Current functionality assumes shader capability + if (!context()->get_feature_mgr()->HasCapability(SpvCapabilityShader)) + return Status::SuccessWithoutChange; + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + bool modified = false; + std::vector<std::pair<Instruction*, unsigned>> arrays_to_change; + for (auto& var : context()->types_values()) { + if (var.opcode() != SpvOpVariable) { + continue; + } + analysis::Type* var_type = type_mgr->GetType(var.type_id()); + analysis::Pointer* ptr_type = var_type->AsPointer(); + if (ptr_type == nullptr) { + continue; + } + if (ptr_type->storage_class() != SpvStorageClassInput) { + continue; + } + const analysis::Array* arr_type = ptr_type->pointee_type()->AsArray(); + if (arr_type == nullptr) { + continue; + } + unsigned arr_len_id = arr_type->LengthId(); + Instruction* arr_len_inst = def_use_mgr->GetDef(arr_len_id); + if (arr_len_inst->opcode() != SpvOpConstant) { + continue; + } + // SPIR-V requires array size is >= 1, so this works for signed or + // unsigned size + unsigned original_max = + arr_len_inst->GetSingleWordInOperand(kConstantValueInIdx) - 1; + unsigned max_idx = FindMaxIndex(var, original_max); + if (max_idx != original_max) { + ChangeArrayLength(var, max_idx + 1); + modified = true; + } + } + + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +unsigned EliminateDeadInputComponentsPass::FindMaxIndex(Instruction& var, + unsigned original_max) { + unsigned max = 0; + bool seen_non_const_ac = false; + assert(var.opcode() == SpvOpVariable && "must be variable"); + context()->get_def_use_mgr()->WhileEachUser( + var.result_id(), [&max, &seen_non_const_ac, var, this](Instruction* use) { + auto use_opcode = use->opcode(); + if (use_opcode == SpvOpLoad || use_opcode == SpvOpCopyMemory || + use_opcode == SpvOpCopyMemorySized || + use_opcode == SpvOpCopyObject) { + seen_non_const_ac = true; + return false; + } + if (use->opcode() != SpvOpAccessChain && + use->opcode() != SpvOpInBoundsAccessChain) { + return true; + } + // OpAccessChain with no indices currently not optimized + if (use->NumInOperands() == 1) { + seen_non_const_ac = true; + return false; + } + unsigned base_id = use->GetSingleWordInOperand(kAccessChainBaseInIdx); + USE_ASSERT(base_id == var.result_id() && "unexpected base"); + unsigned idx_id = use->GetSingleWordInOperand(kAccessChainIndex0InIdx); + Instruction* idx_inst = context()->get_def_use_mgr()->GetDef(idx_id); + if (idx_inst->opcode() != SpvOpConstant) { + seen_non_const_ac = true; + return false; + } + unsigned value = idx_inst->GetSingleWordInOperand(kConstantValueInIdx); + if (value > max) max = value; + return true; + }); + return seen_non_const_ac ? original_max : max; +} + +void EliminateDeadInputComponentsPass::ChangeArrayLength(Instruction& arr, + unsigned length) { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + analysis::Pointer* ptr_type = type_mgr->GetType(arr.type_id())->AsPointer(); + const analysis::Array* arr_ty = ptr_type->pointee_type()->AsArray(); + assert(arr_ty && "expecting array type"); + uint32_t length_id = const_mgr->GetUIntConst(length); + analysis::Array new_arr_ty(arr_ty->element_type(), + arr_ty->GetConstantLengthInfo(length_id, length)); + analysis::Type* reg_new_arr_ty = type_mgr->GetRegisteredType(&new_arr_ty); + analysis::Pointer new_ptr_ty(reg_new_arr_ty, SpvStorageClassInput); + analysis::Type* reg_new_ptr_ty = type_mgr->GetRegisteredType(&new_ptr_ty); + uint32_t new_ptr_ty_id = type_mgr->GetTypeInstruction(reg_new_ptr_ty); + arr.SetResultType(new_ptr_ty_id); + def_use_mgr->AnalyzeInstUse(&arr); + // Move array OpVariable instruction after its new type to preserve order + USE_ASSERT(arr.GetSingleWordInOperand(kVariableStorageClassInIdx) != + SpvStorageClassFunction && + "cannot move Function variable"); + Instruction* new_ptr_ty_inst = def_use_mgr->GetDef(new_ptr_ty_id); + arr.RemoveFromList(); + arr.InsertAfter(new_ptr_ty_inst); +} + +} // namespace opt +} // namespace spvtools diff --git a/source/opt/eliminate_dead_input_components_pass.h b/source/opt/eliminate_dead_input_components_pass.h new file mode 100644 index 00000000..b77857f4 --- /dev/null +++ b/source/opt/eliminate_dead_input_components_pass.h @@ -0,0 +1,59 @@ +// Copyright (c) 2022 The Khronos Group Inc. +// Copyright (c) 2022 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 SOURCE_OPT_ELIMINATE_DEAD_INPUT_COMPONENTS_H_ +#define SOURCE_OPT_ELIMINATE_DEAD_INPUT_COMPONENTS_H_ + +#include <unordered_map> + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class EliminateDeadInputComponentsPass : public Pass { + public: + explicit EliminateDeadInputComponentsPass() {} + + const char* name() const override { return "reduce-load-size"; } + Status Process() override; + + // Return the mask of preserved Analyses. + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisCombinators | IRContext::kAnalysisCFG | + IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisNameMap | + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + } + + private: + // Find the max constant used to index the variable declared by |var| + // through OpAccessChain or OpInBoundsAccessChain. If any non-constant + // indices or non-Op*AccessChain use of |var|, return |original_max|. + unsigned FindMaxIndex(Instruction& var, unsigned original_max); + + // Change the length of the array |inst| to |length| + void ChangeArrayLength(Instruction& inst, unsigned length); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_ELIMINATE_DEAD_INPUT_COMPONENTS_H_ diff --git a/source/opt/eliminate_dead_members_pass.cpp b/source/opt/eliminate_dead_members_pass.cpp index a24ba8f4..52aca525 100644 --- a/source/opt/eliminate_dead_members_pass.cpp +++ b/source/opt/eliminate_dead_members_pass.cpp @@ -38,7 +38,7 @@ Pass::Status EliminateDeadMembersPass::Process() { } void EliminateDeadMembersPass::FindLiveMembers() { - // Until we have implemented the rewritting of OpSpecConsantOp instructions, + // Until we have implemented the rewriting of OpSpecConsantOp instructions, // we have to mark them as fully used just to be safe. for (auto& inst : get_module()->types_values()) { if (inst.opcode() == SpvOpSpecConstantOp) { @@ -570,7 +570,7 @@ bool EliminateDeadMembersPass::UpdateCompsiteExtract(Instruction* inst) { Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); switch (type_inst->opcode()) { case SpvOpTypeStruct: - // The type will have already been rewriten, so use the new member + // The type will have already been rewritten, so use the new member // index. type_id = type_inst->GetSingleWordInOperand(new_member_idx); break; diff --git a/source/opt/feature_manager.cpp b/source/opt/feature_manager.cpp index 39a4a348..a5902716 100644 --- a/source/opt/feature_manager.cpp +++ b/source/opt/feature_manager.cpp @@ -39,8 +39,7 @@ void FeatureManager::AddExtension(Instruction* ext) { assert(ext->opcode() == SpvOpExtension && "Expecting an extension instruction."); - const std::string name = - reinterpret_cast<const char*>(ext->GetInOperand(0u).words.data()); + const std::string name = ext->GetInOperand(0u).AsString(); Extension extension; if (GetExtensionFromString(name.c_str(), &extension)) { extensions_.Add(extension); diff --git a/source/opt/fold.cpp b/source/opt/fold.cpp index 6550fb4f..b903da6a 100644 --- a/source/opt/fold.cpp +++ b/source/opt/fold.cpp @@ -540,7 +540,7 @@ std::vector<uint32_t> InstructionFolder::FoldVectors( // in 32-bit words here. The reason of not using FoldScalars() here // is that we do not create temporary null constants as components // when the vector operand is a NullConstant because Constant creation - // may need extra checks for the validity and that is not manageed in + // may need extra checks for the validity and that is not managed in // here. if (const analysis::ScalarConstant* scalar_component = vector_operand->GetComponents().at(d)->AsScalarConstant()) { diff --git a/source/opt/fold_spec_constant_op_and_composite_pass.cpp b/source/opt/fold_spec_constant_op_and_composite_pass.cpp index 8ab717ea..85f11fde 100644 --- a/source/opt/fold_spec_constant_op_and_composite_pass.cpp +++ b/source/opt/fold_spec_constant_op_and_composite_pass.cpp @@ -115,7 +115,7 @@ bool FoldSpecConstantOpAndCompositePass::ProcessOpSpecConstantOp( Instruction* folded_inst = nullptr; assert(inst->GetInOperand(0).type == SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER && - "The first in-operand of OpSpecContantOp instruction must be of " + "The first in-operand of OpSpecConstantOp instruction must be of " "SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER type"); switch (static_cast<SpvOp>(inst->GetSingleWordInOperand(0))) { diff --git a/source/opt/folding_rules.cpp b/source/opt/folding_rules.cpp index 4904f186..c879a0c5 100644 --- a/source/opt/folding_rules.cpp +++ b/source/opt/folding_rules.cpp @@ -2368,7 +2368,7 @@ FoldingRule VectorShuffleFeedingShuffle() { // fold. return false; } - } else { + } else if (component_index != undef_literal) { if (new_feeder_id == 0) { // First time through, save the id of the operand the element comes // from. @@ -2382,7 +2382,7 @@ FoldingRule VectorShuffleFeedingShuffle() { component_index -= feeder_op0_length; } - if (!feeder_is_op0) { + if (!feeder_is_op0 && component_index != undef_literal) { component_index += op0_length; } } diff --git a/source/opt/graphics_robust_access_pass.cpp b/source/opt/graphics_robust_access_pass.cpp index 1b28f9b5..4652d72d 100644 --- a/source/opt/graphics_robust_access_pass.cpp +++ b/source/opt/graphics_robust_access_pass.cpp @@ -13,7 +13,7 @@ // limitations under the License. // This pass injects code in a graphics shader to implement guarantees -// satisfying Vulkan's robustBufferAcces rules. Robust access rules permit +// satisfying Vulkan's robustBufferAccess rules. Robust access rules permit // an out-of-bounds access to be redirected to an access of the same type // (load, store, etc.) but within the same root object. // @@ -74,7 +74,7 @@ // Pointers are always (correctly) typed and so the address and number of // consecutive locations are fully determined by the pointer. // -// - A pointer value orginates as one of few cases: +// - A pointer value originates as one of few cases: // // - OpVariable for an interface object or an array of them: image, // buffer (UBO or SSBO), sampler, sampled-image, push-constant, input @@ -559,21 +559,17 @@ uint32_t GraphicsRobustAccessPass::GetGlslInsts() { if (module_status_.glsl_insts_id == 0) { // This string serves double-duty as raw data for a string and for a vector // of 32-bit words - const char glsl[] = "GLSL.std.450\0\0\0\0"; - const size_t glsl_str_byte_len = 16; + const char glsl[] = "GLSL.std.450"; // Use an existing import if we can. for (auto& inst : context()->module()->ext_inst_imports()) { - const auto& name_words = inst.GetInOperand(0).words; - if (0 == std::strncmp(reinterpret_cast<const char*>(name_words.data()), - glsl, glsl_str_byte_len)) { + if (inst.GetInOperand(0).AsString() == glsl) { module_status_.glsl_insts_id = inst.result_id(); } } if (module_status_.glsl_insts_id == 0) { // Make a new import instruction. module_status_.glsl_insts_id = TakeNextId(); - std::vector<uint32_t> words(glsl_str_byte_len / sizeof(uint32_t)); - std::memcpy(words.data(), glsl, glsl_str_byte_len); + std::vector<uint32_t> words = spvtools::utils::MakeVector(glsl); auto import_inst = MakeUnique<Instruction>( context(), SpvOpExtInstImport, 0, module_status_.glsl_insts_id, std::initializer_list<Operand>{ @@ -962,7 +958,7 @@ spv_result_t GraphicsRobustAccessPass::ClampCoordinateForImageTexelPointer( constant_mgr->GetDefiningInstruction(component_0)->result_id(); // If the image is a cube array, then the last component of the queried - // size is the layer count. In the query, we have to accomodate folding + // size is the layer count. In the query, we have to accommodate folding // in the face index ranging from 0 through 5. The inclusive upper bound // on the third coordinate therefore is multiplied by 6. auto* query_size_including_faces = query_size; diff --git a/source/opt/graphics_robust_access_pass.h b/source/opt/graphics_robust_access_pass.h index 6fc692c1..8f4c9dc7 100644 --- a/source/opt/graphics_robust_access_pass.h +++ b/source/opt/graphics_robust_access_pass.h @@ -111,7 +111,7 @@ class GraphicsRobustAccessPass : public Pass { Instruction* max, Instruction* where); // Returns a new instruction which evaluates to the length the runtime array - // referenced by the access chain at the specfied index. The instruction is + // referenced by the access chain at the specified index. The instruction is // inserted before the access chain instruction. Returns a null pointer in // some cases if assumptions are violated (rather than asserting out). opt::Instruction* MakeRuntimeArrayLengthInst(Instruction* access_chain, diff --git a/source/opt/inst_bindless_check_pass.cpp b/source/opt/inst_bindless_check_pass.cpp index 5607239a..c2c5d6cb 100644 --- a/source/opt/inst_bindless_check_pass.cpp +++ b/source/opt/inst_bindless_check_pass.cpp @@ -39,13 +39,6 @@ static const int kSpvTypeImageMS = 4; static const int kSpvTypeImageSampled = 5; } // anonymous namespace -// Avoid unused variable warning/error on Linux -#ifndef NDEBUG -#define USE_ASSERT(x) assert(x) -#else -#define USE_ASSERT(x) ((void)(x)) -#endif - namespace spvtools { namespace opt { diff --git a/source/opt/inst_bindless_check_pass.h b/source/opt/inst_bindless_check_pass.h index cd961805..e6e6ef4f 100644 --- a/source/opt/inst_bindless_check_pass.h +++ b/source/opt/inst_bindless_check_pass.h @@ -147,11 +147,11 @@ class InstBindlessCheckPass : public InstrumentPass { uint32_t GenLastByteIdx(RefAnalysis* ref, InstructionBuilder* builder); // Clone original image computation starting at |image_id| into |builder|. - // This may generate more than one instruction if neccessary. + // This may generate more than one instruction if necessary. uint32_t CloneOriginalImage(uint32_t image_id, InstructionBuilder* builder); // Clone original original reference encapsulated by |ref| into |builder|. - // This may generate more than one instruction if neccessary. + // This may generate more than one instruction if necessary. uint32_t CloneOriginalReference(RefAnalysis* ref, InstructionBuilder* builder); diff --git a/source/opt/inst_debug_printf_pass.cpp b/source/opt/inst_debug_printf_pass.cpp index c0e6bc3f..4218138f 100644 --- a/source/opt/inst_debug_printf_pass.cpp +++ b/source/opt/inst_debug_printf_pass.cpp @@ -16,6 +16,7 @@ #include "inst_debug_printf_pass.h" +#include "source/util/string_utils.h" #include "spirv/unified1/NonSemanticDebugPrintf.h" namespace spvtools { @@ -231,10 +232,8 @@ Pass::Status InstDebugPrintfPass::ProcessImpl() { 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))) { + const std::string set_name = c_itr->GetInOperand(0).AsString(); + if (spvtools::utils::starts_with(set_name, "NonSemantic.")) { non_sem_set_seen = true; break; } @@ -242,9 +241,8 @@ Pass::Status InstDebugPrintfPass::ProcessImpl() { 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")) { + const std::string ext_name = c_itr->GetInOperand(0).AsString(); + if (ext_name == "SPV_KHR_non_semantic_info") { context()->KillInst(&*c_itr); break; } diff --git a/source/opt/instruction.cpp b/source/opt/instruction.cpp index 2461e41e..418f1213 100644 --- a/source/opt/instruction.cpp +++ b/source/opt/instruction.cpp @@ -76,10 +76,9 @@ Instruction::Instruction(IRContext* c, const spv_parsed_instruction_t& inst, dbg_scope_(kNoDebugScope, kNoInlinedAt) { 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, + operands_.emplace_back( + current_payload.type, inst.words + current_payload.offset, inst.words + current_payload.offset + current_payload.num_words); - operands_.emplace_back(current_payload.type, std::move(words)); } assert((!IsLineInst() || dbg_line.empty()) && "Op(No)Line attaching to Op(No)Line found"); @@ -96,10 +95,9 @@ Instruction::Instruction(IRContext* c, const spv_parsed_instruction_t& inst, 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, + operands_.emplace_back( + current_payload.type, inst.words + current_payload.offset, inst.words + current_payload.offset + current_payload.num_words); - operands_.emplace_back(current_payload.type, std::move(words)); } } diff --git a/source/opt/instruction.h b/source/opt/instruction.h index ce568f66..2163d99b 100644 --- a/source/opt/instruction.h +++ b/source/opt/instruction.h @@ -24,6 +24,7 @@ #include "NonSemanticShaderDebugInfo100.h" #include "OpenCLDebugInfo100.h" +#include "source/binary.h" #include "source/common_debug_info.h" #include "source/latest_version_glsl_std_450_header.h" #include "source/latest_version_spirv_header.h" @@ -32,6 +33,7 @@ #include "source/opt/reflect.h" #include "source/util/ilist_node.h" #include "source/util/small_vector.h" +#include "source/util/string_utils.h" #include "spirv-tools/libspirv.h" const uint32_t kNoDebugScope = 0; @@ -82,21 +84,32 @@ struct Operand { Operand(spv_operand_type_t t, const OperandData& w) : type(t), words(w) {} + template <class InputIt> + Operand(spv_operand_type_t t, InputIt firstOperandData, + InputIt lastOperandData) + : type(t), words(firstOperandData, lastOperandData) {} + 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()); + uint32_t AsId() const { + assert(spvIsIdType(type)); + assert(words.size() == 1); + return words[0]; } // Returns a string operand as a std::string. - std::string AsString() const { return AsCString(); } + std::string AsString() const { + assert(type == SPV_OPERAND_TYPE_LITERAL_STRING); + return spvtools::utils::MakeString(words); + } // Returns a literal integer operand as a uint64_t uint64_t AsLiteralUint64() const { - assert(type == SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER); + assert(type == SPV_OPERAND_TYPE_LITERAL_INTEGER || + type == SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER || + type == SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER || + type == SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER); assert(1 <= words.size()); assert(words.size() <= 2); uint64_t result = 0; @@ -123,7 +136,7 @@ inline bool operator!=(const Operand& o1, const Operand& o2) { } // This structure is used to represent a DebugScope instruction from -// the OpenCL.100.DebugInfo extened instruction set. Note that we can +// the OpenCL.100.DebugInfo extended 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. @@ -295,6 +308,7 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> { inline void SetInOperands(OperandList&& new_operands); // Sets the result type id. inline void SetResultType(uint32_t ty_id); + inline bool HasResultType() const { return has_type_id_; } // Sets the result id inline void SetResultId(uint32_t res_id); inline bool HasResultId() const { return has_result_id_; } diff --git a/source/opt/instrument_pass.h b/source/opt/instrument_pass.h index 12b939d4..90c1dd47 100644 --- a/source/opt/instrument_pass.h +++ b/source/opt/instrument_pass.h @@ -50,7 +50,7 @@ // A validation pass may read or write multiple buffers. All such buffers // are located in a single debug descriptor set whose index is passed at the // creation of the instrumentation pass. The bindings of the buffers used by -// a validation pass are permanantly assigned and fixed and documented by +// a validation pass are permanently assigned and fixed and documented by // the kDebugOutput* static consts. namespace spvtools { @@ -179,8 +179,8 @@ class InstrumentPass : public Pass { // the error. Every stage will write a fixed number of words. Vertex shaders // will write the Vertex and Instance ID. Fragment shaders will write // FragCoord.xy. Compute shaders will write the GlobalInvocation ID. - // The tesselation eval shader will write the Primitive ID and TessCoords.uv. - // The tesselation control shader and geometry shader will write the + // The tessellation eval shader will write the Primitive ID and TessCoords.uv. + // The tessellation control shader and geometry shader will write the // Primitive ID and Invocation ID. // // The Validation Error Code specifies the exact error which has occurred. diff --git a/source/opt/interp_fixup_pass.cpp b/source/opt/interp_fixup_pass.cpp index ad29e6a7..e8cdd99f 100644 --- a/source/opt/interp_fixup_pass.cpp +++ b/source/opt/interp_fixup_pass.cpp @@ -31,13 +31,6 @@ namespace { // Input Operand Indices static const int kSpvVariableStorageClassInIdx = 0; -// Avoid unused variable warning/error on Linux -#ifndef NDEBUG -#define USE_ASSERT(x) assert(x) -#else -#define USE_ASSERT(x) ((void)(x)) -#endif - // Folding rule function which attempts to replace |op(OpLoad(a),...)| // by |op(a,...)|, where |op| is one of the GLSLstd450 InterpolateAt* // instructions. Returns true if replaced, false otherwise. diff --git a/source/opt/ir_context.cpp b/source/opt/ir_context.cpp index 612a831a..a80d4f2d 100644 --- a/source/opt/ir_context.cpp +++ b/source/opt/ir_context.cpp @@ -41,6 +41,8 @@ namespace spvtools { namespace opt { void IRContext::BuildInvalidAnalyses(IRContext::Analysis set) { + set = Analysis(set & ~valid_analyses_); + if (set & kAnalysisDefUse) { BuildDefUseManager(); } @@ -106,7 +108,7 @@ void IRContext::InvalidateAnalyses(IRContext::Analysis analyses_to_invalidate) { analyses_to_invalidate |= kAnalysisDebugInfo; } - // The dominator analysis hold the psuedo entry and exit nodes from the CFG. + // The dominator analysis hold the pseudo entry and exit nodes from the CFG. // Also if the CFG change the dominators many changed as well, so the // dominator analysis should be invalidated as well. if (analyses_to_invalidate & kAnalysisCFG) { @@ -317,7 +319,7 @@ bool IRContext::IsConsistent() { #else if (AreAnalysesValid(kAnalysisDefUse)) { analysis::DefUseManager new_def_use(module()); - if (*get_def_use_mgr() != new_def_use) { + if (!CompareAndPrintDifferences(*get_def_use_mgr(), new_def_use)) { return false; } } @@ -623,9 +625,8 @@ void IRContext::AddCombinatorsForCapability(uint32_t capability) { void IRContext::AddCombinatorsForExtension(Instruction* extension) { assert(extension->opcode() == SpvOpExtInstImport && "Expecting an import of an extension's instruction set."); - const char* extension_name = - reinterpret_cast<const char*>(&extension->GetInOperand(0).words[0]); - if (!strcmp(extension_name, "GLSL.std.450")) { + const std::string extension_name = extension->GetInOperand(0).AsString(); + if (extension_name == "GLSL.std.450") { combinator_ops_[extension->result_id()] = {GLSLstd450Round, GLSLstd450RoundEven, GLSLstd450Trunc, @@ -944,11 +945,11 @@ void IRContext::EmitErrorMessage(std::string message, Instruction* inst) { uint32_t line_number = 0; uint32_t col_number = 0; - char* source = nullptr; + std::string source; if (line_inst != nullptr) { Instruction* file_name = get_def_use_mgr()->GetDef(line_inst->GetSingleWordInOperand(0)); - source = reinterpret_cast<char*>(&file_name->GetInOperand(0).words[0]); + source = file_name->GetInOperand(0).AsString(); // Get the line number and column number. line_number = line_inst->GetSingleWordInOperand(1); @@ -957,7 +958,7 @@ void IRContext::EmitErrorMessage(std::string message, Instruction* inst) { message += "\n " + inst->PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); - consumer()(SPV_MSG_ERROR, source, {line_number, col_number, 0}, + consumer()(SPV_MSG_ERROR, source.c_str(), {line_number, col_number, 0}, message.c_str()); } diff --git a/source/opt/ir_context.h b/source/opt/ir_context.h index 65853476..946f9e9d 100644 --- a/source/opt/ir_context.h +++ b/source/opt/ir_context.h @@ -43,6 +43,7 @@ #include "source/opt/type_manager.h" #include "source/opt/value_number_table.h" #include "source/util/make_unique.h" +#include "source/util/string_utils.h" namespace spvtools { namespace opt { @@ -301,7 +302,7 @@ class IRContext { } } - // Returns a pointer the decoration manager. If the decoration manger is + // Returns a pointer the decoration manager. If the decoration manager is // invalid, it is rebuilt first. analysis::DecorationManager* get_decoration_mgr() { if (!AreAnalysesValid(kAnalysisDecorations)) { @@ -384,7 +385,7 @@ class IRContext { // Deletes the instruction defining the given |id|. Returns true on // success, false if the given |id| is not defined at all. This method also - // erases the name, decorations, and defintion of |id|. + // erases the name, decorations, and definition of |id|. // // Pointers and iterators pointing to the deleted instructions become invalid. // However other pointers and iterators are still valid. @@ -518,6 +519,18 @@ class IRContext { std::string message = "ID overflow. Try running compact-ids."; consumer()(SPV_MSG_ERROR, "", {0, 0, 0}, message.c_str()); } +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + // If TakeNextId returns 0, it is very likely that execution will + // subsequently fail. Such failures are false alarms from a fuzzing point + // of view: they are due to the fact that too many ids were used, rather + // than being due to an actual bug. Thus, during a fuzzing build, it is + // preferable to bail out when ID overflow occurs. + // + // A zero exit code is returned here because a non-zero code would cause + // ClusterFuzz/OSS-Fuzz to regard the termination as a crash, and spurious + // crash reports is what this guard aims to avoid. + exit(0); +#endif } return next_id; } @@ -789,7 +802,7 @@ class IRContext { // iterators to traverse instructions. std::unordered_map<uint32_t, Function*> id_to_func_; - // A bitset indicating which analyes are currently valid. + // A bitset indicating which analyzes are currently valid. Analysis valid_analyses_; // Opcodes of shader capability core executable instructions @@ -854,8 +867,7 @@ inline IRContext::Analysis operator|(IRContext::Analysis lhs, inline IRContext::Analysis& operator|=(IRContext::Analysis& lhs, IRContext::Analysis rhs) { - lhs = static_cast<IRContext::Analysis>(static_cast<int>(lhs) | - static_cast<int>(rhs)); + lhs = lhs | rhs; return lhs; } @@ -1020,11 +1032,7 @@ void IRContext::AddCapability(std::unique_ptr<Instruction>&& c) { } void IRContext::AddExtension(const std::string& ext_name) { - const auto num_chars = ext_name.size(); - // Compute num words, accommodate the terminating null character. - const auto num_words = (num_chars + 1 + 3) / 4; - std::vector<uint32_t> ext_words(num_words, 0u); - std::memcpy(ext_words.data(), ext_name.data(), num_chars); + std::vector<uint32_t> ext_words = spvtools::utils::MakeVector(ext_name); AddExtension(std::unique_ptr<Instruction>( new Instruction(this, SpvOpExtension, 0u, 0u, {{SPV_OPERAND_TYPE_LITERAL_STRING, ext_words}}))); @@ -1041,11 +1049,7 @@ void IRContext::AddExtension(std::unique_ptr<Instruction>&& e) { } void IRContext::AddExtInstImport(const std::string& name) { - const auto num_chars = name.size(); - // Compute num words, accommodate the terminating null character. - const auto num_words = (num_chars + 1 + 3) / 4; - std::vector<uint32_t> ext_words(num_words, 0u); - std::memcpy(ext_words.data(), name.data(), num_chars); + std::vector<uint32_t> ext_words = spvtools::utils::MakeVector(name); AddExtInstImport(std::unique_ptr<Instruction>( new Instruction(this, SpvOpExtInstImport, 0u, TakeNextId(), {{SPV_OPERAND_TYPE_LITERAL_STRING, ext_words}}))); diff --git a/source/opt/ir_loader.cpp b/source/opt/ir_loader.cpp index a82b530e..97db9d8f 100644 --- a/source/opt/ir_loader.cpp +++ b/source/opt/ir_loader.cpp @@ -189,7 +189,8 @@ bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) { module_->SetMemoryModel(std::move(spv_inst)); } else if (opcode == SpvOpEntryPoint) { module_->AddEntryPoint(std::move(spv_inst)); - } else if (opcode == SpvOpExecutionMode) { + } else if (opcode == SpvOpExecutionMode || + opcode == SpvOpExecutionModeId) { module_->AddExecutionMode(std::move(spv_inst)); } else if (IsDebug1Inst(opcode)) { module_->AddDebug1Inst(std::move(spv_inst)); diff --git a/source/opt/local_access_chain_convert_pass.cpp b/source/opt/local_access_chain_convert_pass.cpp index da9ba8cc..0c6d0c24 100644 --- a/source/opt/local_access_chain_convert_pass.cpp +++ b/source/opt/local_access_chain_convert_pass.cpp @@ -19,6 +19,7 @@ #include "ir_builder.h" #include "ir_context.h" #include "iterator.h" +#include "source/util/string_utils.h" namespace spvtools { namespace opt { @@ -328,8 +329,7 @@ bool LocalAccessChainConvertPass::AllExtensionsSupported() const { return false; // If any extension not in allowlist, return false for (auto& ei : get_module()->extensions()) { - const char* extName = - reinterpret_cast<const char*>(&ei.GetInOperand(0).words[0]); + const std::string extName = ei.GetInOperand(0).AsString(); if (extensions_allowlist_.find(extName) == extensions_allowlist_.end()) return false; } @@ -339,11 +339,9 @@ bool LocalAccessChainConvertPass::AllExtensionsSupported() const { for (auto& inst : context()->module()->ext_inst_imports()) { assert(inst.opcode() == SpvOpExtInstImport && "Expecting an import of an extension's instruction set."); - const char* extension_name = - reinterpret_cast<const char*>(&inst.GetInOperand(0).words[0]); - if (0 == std::strncmp(extension_name, "NonSemantic.", 12) && - 0 != std::strncmp(extension_name, "NonSemantic.Shader.DebugInfo.100", - 32)) { + const std::string extension_name = inst.GetInOperand(0).AsString(); + if (spvtools::utils::starts_with(extension_name, "NonSemantic.") && + extension_name != "NonSemantic.Shader.DebugInfo.100") { return false; } } @@ -436,6 +434,7 @@ void LocalAccessChainConvertPass::InitExtensions() { "SPV_KHR_integer_dot_product", "SPV_EXT_shader_image_int64", "SPV_KHR_non_semantic_info", + "SPV_KHR_uniform_group_instructions", }); } diff --git a/source/opt/local_access_chain_convert_pass.h b/source/opt/local_access_chain_convert_pass.h index 552062e5..a51660f1 100644 --- a/source/opt/local_access_chain_convert_pass.h +++ b/source/opt/local_access_chain_convert_pass.h @@ -81,7 +81,7 @@ class LocalAccessChainConvertPass : public MemPass { std::vector<Operand>* in_opnds); // Create a load/insert/store equivalent to a store of - // |valId| through (constant index) access chaing |ptrInst|. + // |valId| through (constant index) access chain |ptrInst|. // Append to |newInsts|. Returns true if successful. bool GenAccessChainStoreReplacement( const Instruction* ptrInst, uint32_t valId, diff --git a/source/opt/local_single_block_elim_pass.cpp b/source/opt/local_single_block_elim_pass.cpp index 5fd4f658..33c8bdf8 100644 --- a/source/opt/local_single_block_elim_pass.cpp +++ b/source/opt/local_single_block_elim_pass.cpp @@ -19,6 +19,7 @@ #include <vector> #include "source/opt/iterator.h" +#include "source/util/string_utils.h" namespace spvtools { namespace opt { @@ -183,8 +184,7 @@ void LocalSingleBlockLoadStoreElimPass::Initialize() { bool LocalSingleBlockLoadStoreElimPass::AllExtensionsSupported() const { // If any extension not in allowlist, return false for (auto& ei : get_module()->extensions()) { - const char* extName = - reinterpret_cast<const char*>(&ei.GetInOperand(0).words[0]); + const std::string extName = ei.GetInOperand(0).AsString(); if (extensions_allowlist_.find(extName) == extensions_allowlist_.end()) return false; } @@ -194,11 +194,9 @@ bool LocalSingleBlockLoadStoreElimPass::AllExtensionsSupported() const { for (auto& inst : context()->module()->ext_inst_imports()) { assert(inst.opcode() == SpvOpExtInstImport && "Expecting an import of an extension's instruction set."); - const char* extension_name = - reinterpret_cast<const char*>(&inst.GetInOperand(0).words[0]); - if (0 == std::strncmp(extension_name, "NonSemantic.", 12) && - 0 != std::strncmp(extension_name, "NonSemantic.Shader.DebugInfo.100", - 32)) { + const std::string extension_name = inst.GetInOperand(0).AsString(); + if (spvtools::utils::starts_with(extension_name, "NonSemantic.") && + extension_name != "NonSemantic.Shader.DebugInfo.100") { return false; } } @@ -288,6 +286,7 @@ void LocalSingleBlockLoadStoreElimPass::InitExtensions() { "SPV_KHR_integer_dot_product", "SPV_EXT_shader_image_int64", "SPV_KHR_non_semantic_info", + "SPV_KHR_uniform_group_instructions", }); } diff --git a/source/opt/local_single_store_elim_pass.cpp b/source/opt/local_single_store_elim_pass.cpp index 051bcada..f22b1911 100644 --- a/source/opt/local_single_store_elim_pass.cpp +++ b/source/opt/local_single_store_elim_pass.cpp @@ -19,6 +19,7 @@ #include "source/cfa.h" #include "source/latest_version_glsl_std_450_header.h" #include "source/opt/iterator.h" +#include "source/util/string_utils.h" namespace spvtools { namespace opt { @@ -48,8 +49,7 @@ bool LocalSingleStoreElimPass::LocalSingleStoreElim(Function* func) { bool LocalSingleStoreElimPass::AllExtensionsSupported() const { // If any extension not in allowlist, return false for (auto& ei : get_module()->extensions()) { - const char* extName = - reinterpret_cast<const char*>(&ei.GetInOperand(0).words[0]); + const std::string extName = ei.GetInOperand(0).AsString(); if (extensions_allowlist_.find(extName) == extensions_allowlist_.end()) return false; } @@ -59,11 +59,9 @@ bool LocalSingleStoreElimPass::AllExtensionsSupported() const { for (auto& inst : context()->module()->ext_inst_imports()) { assert(inst.opcode() == SpvOpExtInstImport && "Expecting an import of an extension's instruction set."); - const char* extension_name = - reinterpret_cast<const char*>(&inst.GetInOperand(0).words[0]); - if (0 == std::strncmp(extension_name, "NonSemantic.", 12) && - 0 != std::strncmp(extension_name, "NonSemantic.Shader.DebugInfo.100", - 32)) { + const std::string extension_name = inst.GetInOperand(0).AsString(); + if (spvtools::utils::starts_with(extension_name, "NonSemantic.") && + extension_name != "NonSemantic.Shader.DebugInfo.100") { return false; } } @@ -141,6 +139,7 @@ void LocalSingleStoreElimPass::InitExtensionAllowList() { "SPV_KHR_integer_dot_product", "SPV_EXT_shader_image_int64", "SPV_KHR_non_semantic_info", + "SPV_KHR_uniform_group_instructions", }); } bool LocalSingleStoreElimPass::ProcessVariable(Instruction* var_inst) { diff --git a/source/opt/loop_descriptor.cpp b/source/opt/loop_descriptor.cpp index b5b56309..9bc495e5 100644 --- a/source/opt/loop_descriptor.cpp +++ b/source/opt/loop_descriptor.cpp @@ -719,7 +719,7 @@ bool Loop::FindNumberOfIterations(const Instruction* induction, step_value = -step_value; } - // Find the inital value of the loop and make sure it is a constant integer. + // Find the initial value of the loop and make sure it is a constant integer. int64_t init_value = 0; if (!GetInductionInitValue(induction, &init_value)) return false; @@ -751,7 +751,7 @@ bool Loop::FindNumberOfIterations(const Instruction* induction, // We retrieve the number of iterations using the following formula, diff / // |step_value| where diff is calculated differently according to the // |condition| and uses the |condition_value| and |init_value|. If diff / -// |step_value| is NOT cleanly divisable then we add one to the sum. +// |step_value| is NOT cleanly divisible then we add one to the sum. int64_t Loop::GetIterations(SpvOp condition, int64_t condition_value, int64_t init_value, int64_t step_value) const { int64_t diff = 0; @@ -795,7 +795,7 @@ int64_t Loop::GetIterations(SpvOp condition, int64_t condition_value, // If the condition is not met to begin with the loop will never iterate. if (!(init_value >= condition_value)) return 0; - // We subract one to make it the same as SpvOpGreaterThan as it is + // We subtract one to make it the same as SpvOpGreaterThan as it is // functionally equivalent. diff = init_value - (condition_value - 1); diff --git a/source/opt/loop_descriptor.h b/source/opt/loop_descriptor.h index 4b4f8bc7..e88ff936 100644 --- a/source/opt/loop_descriptor.h +++ b/source/opt/loop_descriptor.h @@ -395,7 +395,7 @@ class Loop { // Sets |merge| as the loop merge block. No checks are performed here. inline void SetMergeBlockImpl(BasicBlock* merge) { loop_merge_ = merge; } - // Each differnt loop |condition| affects how we calculate the number of + // Each different loop |condition| affects how we calculate the number of // iterations using the |condition_value|, |init_value|, and |step_values| of // the induction variable. This method will return the number of iterations in // a loop with those values for a given |condition|. diff --git a/source/opt/loop_fission.cpp b/source/opt/loop_fission.cpp index 0678113c..b4df8c62 100644 --- a/source/opt/loop_fission.cpp +++ b/source/opt/loop_fission.cpp @@ -29,7 +29,7 @@ // 2 - For each loop in the list, group each instruction into a set of related // instructions by traversing each instructions users and operands recursively. // We stop if we encounter an instruction we have seen before or an instruction -// which we don't consider relevent (i.e OpLoopMerge). We then group these +// which we don't consider relevant (i.e OpLoopMerge). We then group these // groups into two different sets, one for the first loop and one for the // second. // @@ -453,7 +453,7 @@ Pass::Status LoopFissionPass::Process() { for (Function& f : *context()->module()) { // We collect all the inner most loops in the function and run the loop // splitting util on each. The reason we do this is to allow us to iterate - // over each, as creating new loops will invalidate the the loop iterator. + // over each, as creating new loops will invalidate the loop iterator. std::vector<Loop*> inner_most_loops{}; LoopDescriptor& loop_descriptor = *context()->GetLoopDescriptor(&f); for (Loop& loop : loop_descriptor) { diff --git a/source/opt/loop_fission.h b/source/opt/loop_fission.h index e7a59c18..9bc12c0f 100644 --- a/source/opt/loop_fission.h +++ b/source/opt/loop_fission.h @@ -33,7 +33,7 @@ namespace opt { class LoopFissionPass : public Pass { public: - // Fuction used to determine if a given loop should be split. Takes register + // Function used to determine if a given loop should be split. Takes register // pressure region for that loop as a parameter and returns true if the loop // should be split. using FissionCriteriaFunction = diff --git a/source/opt/loop_fusion.cpp b/source/opt/loop_fusion.cpp index 07d171a0..f3aab283 100644 --- a/source/opt/loop_fusion.cpp +++ b/source/opt/loop_fusion.cpp @@ -165,7 +165,7 @@ bool LoopFusion::AreCompatible() { // Check adjacency, |loop_0_| should come just before |loop_1_|. // There is always at least one block between loops, even if it's empty. - // We'll check at most 2 preceeding blocks. + // We'll check at most 2 preceding blocks. auto pre_header_1 = loop_1_->GetPreHeaderBlock(); @@ -712,7 +712,7 @@ void LoopFusion::Fuse() { ld->RemoveLoop(loop_1_); - // Kill unnessecary instructions and remove all empty blocks. + // Kill unnecessary instructions and remove all empty blocks. for (auto inst : instr_to_delete) { context_->KillInst(inst); } diff --git a/source/opt/loop_fusion.h b/source/opt/loop_fusion.h index d61d6783..769da5f1 100644 --- a/source/opt/loop_fusion.h +++ b/source/opt/loop_fusion.h @@ -40,7 +40,7 @@ class LoopFusion { // That means: // * they both have one induction variable // * they have the same upper and lower bounds - // - same inital value + // - same initial value // - same condition // * they have the same update step // * they are adjacent, with |loop_0| appearing before |loop_1| diff --git a/source/opt/loop_fusion_pass.h b/source/opt/loop_fusion_pass.h index 3a0be600..9d5b7ccd 100644 --- a/source/opt/loop_fusion_pass.h +++ b/source/opt/loop_fusion_pass.h @@ -33,7 +33,7 @@ class LoopFusionPass : public Pass { // Processes the given |module|. Returns Status::Failure if errors occur when // processing. Returns the corresponding Status::Success if processing is - // succesful to indicate whether changes have been made to the modue. + // successful to indicate whether changes have been made to the module. Status Process() override; private: diff --git a/source/opt/loop_peeling.h b/source/opt/loop_peeling.h index 413f896f..2a55fe44 100644 --- a/source/opt/loop_peeling.h +++ b/source/opt/loop_peeling.h @@ -261,7 +261,7 @@ class LoopPeelingPass : public Pass { // Processes the given |module|. Returns Status::Failure if errors occur when // processing. Returns the corresponding Status::Success if processing is - // succesful to indicate whether changes have been made to the modue. + // successful to indicate whether changes have been made to the module. Pass::Status Process() override; private: diff --git a/source/opt/loop_unroller.cpp b/source/opt/loop_unroller.cpp index aff191fe..28ff0729 100644 --- a/source/opt/loop_unroller.cpp +++ b/source/opt/loop_unroller.cpp @@ -163,7 +163,7 @@ struct LoopUnrollState { }; // This class implements the actual unrolling. It uses a LoopUnrollState to -// maintain the state of the unrolling inbetween steps. +// maintain the state of the unrolling in between steps. class LoopUnrollerUtilsImpl { public: using BasicBlockListTy = std::vector<std::unique_ptr<BasicBlock>>; @@ -209,7 +209,7 @@ class LoopUnrollerUtilsImpl { // Add all blocks_to_add_ to function_ at the |insert_point|. void AddBlocksToFunction(const BasicBlock* insert_point); - // Duplicates the |old_loop|, cloning each body and remaping the ids without + // Duplicates the |old_loop|, cloning each body and remapping the ids without // removing instructions or changing relative structure. Result will be stored // in |new_loop|. void DuplicateLoop(Loop* old_loop, Loop* new_loop); @@ -241,7 +241,7 @@ class LoopUnrollerUtilsImpl { // Remap all the in |basic_block| to new IDs and keep the mapping of new ids // to old // ids. |loop| is used to identify special loop blocks (header, continue, - // ect). + // etc). void AssignNewResultIds(BasicBlock* basic_block); // Using the map built by AssignNewResultIds, replace the uses in |inst| @@ -320,7 +320,7 @@ class LoopUnrollerUtilsImpl { // and then be remapped at the end. std::vector<Instruction*> loop_phi_instructions_; - // The number of loop iterations that the loop would preform pre-unroll. + // The number of loop iterations that the loop would perform pre-unroll. size_t number_of_loop_iterations_; // The amount that the loop steps each iteration. @@ -839,7 +839,7 @@ void LoopUnrollerUtilsImpl::DuplicateLoop(Loop* old_loop, Loop* new_loop) { new_loop->SetMergeBlock(new_merge); } -// Whenever the utility copies a block it stores it in a tempory buffer, this +// Whenever the utility copies a block it stores it in a temporary buffer, this // function adds the buffer into the Function. The blocks will be inserted // after the block |insert_point|. void LoopUnrollerUtilsImpl::AddBlocksToFunction( diff --git a/source/opt/loop_unswitch_pass.cpp b/source/opt/loop_unswitch_pass.cpp index d805ecf3..1ee7e5e2 100644 --- a/source/opt/loop_unswitch_pass.cpp +++ b/source/opt/loop_unswitch_pass.cpp @@ -118,7 +118,7 @@ class LoopUnswitch { // Find a value that can be used to select the default path. // If none are possible, then it will just use 0. The value does not matter - // because this path will never be taken becaues the new switch outside of + // because this path will never be taken because the new switch outside of // the loop cannot select this path either. std::vector<uint32_t> existing_values; for (uint32_t i = 2; i < switch_inst->NumInOperands(); i += 2) { diff --git a/source/opt/loop_unswitch_pass.h b/source/opt/loop_unswitch_pass.h index 3ecdd611..4f7295d4 100644 --- a/source/opt/loop_unswitch_pass.h +++ b/source/opt/loop_unswitch_pass.h @@ -30,7 +30,7 @@ class LoopUnswitchPass : public Pass { // Processes the given |module|. Returns Status::Failure if errors occur when // processing. Returns the corresponding Status::Success if processing is - // succesful to indicate whether changes have been made to the modue. + // successful to indicate whether changes have been made to the module. Pass::Status Process() override; private: diff --git a/source/opt/loop_utils.h b/source/opt/loop_utils.h index a4e61900..70060fc4 100644 --- a/source/opt/loop_utils.h +++ b/source/opt/loop_utils.h @@ -123,7 +123,7 @@ class LoopUtils { // Clone the |loop_| and make the new loop branch to the second loop on exit. Loop* CloneAndAttachLoopToHeader(LoopCloningResult* cloning_result); - // Perfom a partial unroll of |loop| by given |factor|. This will copy the + // Perform a partial unroll of |loop| by given |factor|. This will copy the // body of the loop |factor| times. So a |factor| of one would give a new loop // with the original body plus one unrolled copy body. bool PartiallyUnroll(size_t factor); @@ -139,7 +139,7 @@ class LoopUtils { // 1. That the loop is in structured order. // 2. That the continue block is a branch to the header. // 3. That the only phi used in the loop is the induction variable. - // TODO(stephen@codeplay.com): This is a temporary mesure, after the loop is + // TODO(stephen@codeplay.com): This is a temporary measure, after the loop is // converted into LCSAA form and has a single entry and exit we can rewrite // the other phis. // 4. That this is an inner most loop, or that loops contained within this diff --git a/source/opt/merge_return_pass.cpp b/source/opt/merge_return_pass.cpp index a962a7cc..7710deae 100644 --- a/source/opt/merge_return_pass.cpp +++ b/source/opt/merge_return_pass.cpp @@ -431,6 +431,7 @@ bool MergeReturnPass::BreakFromConstruct( std::list<BasicBlock*>* order, Instruction* break_merge_inst) { // Make sure the CFG is build here. If we don't then it becomes very hard // to know which new blocks need to be updated. + context()->InvalidateAnalyses(IRContext::kAnalysisCFG); context()->BuildInvalidAnalyses(IRContext::kAnalysisCFG); // When predicating, be aware of whether this block is a header block, a diff --git a/source/opt/merge_return_pass.h b/source/opt/merge_return_pass.h index 4096ce7d..a35cf269 100644 --- a/source/opt/merge_return_pass.h +++ b/source/opt/merge_return_pass.h @@ -247,7 +247,7 @@ class MergeReturnPass : public MemPass { // Add new phi nodes for any id that no longer dominate all of it uses. A phi // node is added to a block |bb| for an id if the id is defined between the - // original immediate dominator of |bb| and its new immidiate dominator. It + // original immediate dominator of |bb| and its new immediate dominator. It // is assumed that at this point there are no unreachable blocks in the // control flow graph. void AddNewPhiNodes(); @@ -273,7 +273,7 @@ class MergeReturnPass : public MemPass { void InsertAfterElement(BasicBlock* element, BasicBlock* new_element, std::list<BasicBlock*>* list); - // Creates a single case switch around all of the exectuable code of the + // Creates a single case switch around all of the executable code of the // current function where the switch and case value are both zero and the // default is the merge block. Returns after the switch is executed. Sets // |final_return_block_|. diff --git a/source/opt/module.cpp b/source/opt/module.cpp index f97defbd..5983abb1 100644 --- a/source/opt/module.cpp +++ b/source/opt/module.cpp @@ -139,7 +139,7 @@ void Module::ToBinary(std::vector<uint32_t>* binary, bool skip_nop) const { // TODO(antiagainst): should we change the generator number? binary->push_back(header_.generator); binary->push_back(header_.bound); - binary->push_back(header_.reserved); + binary->push_back(header_.schema); size_t bound_idx = binary->size() - 2; DebugScope last_scope(kNoDebugScope, kNoInlinedAt); @@ -260,9 +260,7 @@ bool Module::HasExplicitCapability(uint32_t cap) { uint32_t Module::GetExtInstImportId(const char* extstr) { for (auto& ei : ext_inst_imports_) - if (!strcmp(extstr, - reinterpret_cast<const char*>(&(ei.GetInOperand(0).words[0])))) - return ei.result_id(); + if (!ei.GetInOperand(0).AsString().compare(extstr)) return ei.result_id(); return 0; } diff --git a/source/opt/module.h b/source/opt/module.h index 0360b7d5..230be709 100644 --- a/source/opt/module.h +++ b/source/opt/module.h @@ -36,7 +36,7 @@ struct ModuleHeader { uint32_t version; uint32_t generator; uint32_t bound; - uint32_t reserved; + uint32_t schema; }; // A SPIR-V module. It contains all the information for a SPIR-V module and @@ -61,7 +61,7 @@ class Module { } // Returns the Id bound. - uint32_t IdBound() { return header_.bound; } + uint32_t IdBound() const { return header_.bound; } // Returns the current Id bound and increases it to the next available value. // If the id bound has already reached its maximum value, then 0 is returned. @@ -141,6 +141,8 @@ class Module { inline uint32_t id_bound() const { return header_.bound; } inline uint32_t version() const { return header_.version; } + inline uint32_t generator() const { return header_.generator; } + inline uint32_t schema() const { return header_.schema; } inline void set_version(uint32_t v) { header_.version = v; } diff --git a/source/opt/optimizer.cpp b/source/opt/optimizer.cpp index e74db26f..f28b1baf 100644 --- a/source/opt/optimizer.cpp +++ b/source/opt/optimizer.cpp @@ -288,6 +288,8 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) { RegisterPass(CreateStripDebugInfoPass()); } else if (pass_name == "strip-reflect") { RegisterPass(CreateStripReflectInfoPass()); + } else if (pass_name == "strip-nonsemantic") { + RegisterPass(CreateStripNonSemanticInfoPass()); } else if (pass_name == "set-spec-const-default-value") { if (pass_args.size() > 0) { auto spec_ids_vals = @@ -322,6 +324,8 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) { RegisterPass(CreateLocalAccessChainConvertPass()); } else if (pass_name == "replace-desc-array-access-using-var-index") { RegisterPass(CreateReplaceDescArrayAccessUsingVarIndexPass()); + } else if (pass_name == "spread-volatile-semantics") { + RegisterPass(CreateSpreadVolatileSemanticsPass()); } else if (pass_name == "descriptor-scalar-replacement") { RegisterPass(CreateDescriptorScalarReplacementPass()); } else if (pass_name == "eliminate-dead-code-aggressive") { @@ -517,6 +521,10 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) { RegisterPass(CreateAmdExtToKhrPass()); } else if (pass_name == "interpolate-fixup") { RegisterPass(CreateInterpolateFixupPass()); + } else if (pass_name == "remove-dont-inline") { + RegisterPass(CreateRemoveDontInlinePass()); + } else if (pass_name == "eliminate-dead-input-components") { + RegisterPass(CreateEliminateDeadInputComponentsPass()); } else if (pass_name == "convert-to-sampled-image") { if (pass_args.size() > 0) { auto descriptor_set_binding_pairs = @@ -653,8 +661,12 @@ Optimizer::PassToken CreateStripDebugInfoPass() { } Optimizer::PassToken CreateStripReflectInfoPass() { + return CreateStripNonSemanticInfoPass(); +} + +Optimizer::PassToken CreateStripNonSemanticInfoPass() { return MakeUnique<Optimizer::PassToken::Impl>( - MakeUnique<opt::StripReflectInfoPass>()); + MakeUnique<opt::StripNonSemanticInfoPass>()); } Optimizer::PassToken CreateEliminateDeadFunctionsPass() { @@ -764,6 +776,11 @@ Optimizer::PassToken CreateLocalMultiStoreElimPass() { MakeUnique<opt::SSARewritePass>()); } +Optimizer::PassToken CreateAggressiveDCEPass() { + return MakeUnique<Optimizer::PassToken::Impl>( + MakeUnique<opt::AggressiveDCEPass>(false)); +} + Optimizer::PassToken CreateAggressiveDCEPass(bool preserve_interface) { return MakeUnique<Optimizer::PassToken::Impl>( MakeUnique<opt::AggressiveDCEPass>(preserve_interface)); @@ -965,6 +982,11 @@ Optimizer::PassToken CreateReplaceDescArrayAccessUsingVarIndexPass() { MakeUnique<opt::ReplaceDescArrayAccessUsingVarIndex>()); } +Optimizer::PassToken CreateSpreadVolatileSemanticsPass() { + return MakeUnique<Optimizer::PassToken::Impl>( + MakeUnique<opt::SpreadVolatileSemantics>()); +} + Optimizer::PassToken CreateDescriptorScalarReplacementPass() { return MakeUnique<Optimizer::PassToken::Impl>( MakeUnique<opt::DescriptorScalarReplacement>()); @@ -984,6 +1006,11 @@ Optimizer::PassToken CreateInterpolateFixupPass() { MakeUnique<opt::InterpFixupPass>()); } +Optimizer::PassToken CreateEliminateDeadInputComponentsPass() { + return MakeUnique<Optimizer::PassToken::Impl>( + MakeUnique<opt::EliminateDeadInputComponentsPass>()); +} + Optimizer::PassToken CreateConvertToSampledImagePass( const std::vector<opt::DescriptorSetAndBinding>& descriptor_set_binding_pairs) { @@ -991,4 +1018,8 @@ Optimizer::PassToken CreateConvertToSampledImagePass( MakeUnique<opt::ConvertToSampledImagePass>(descriptor_set_binding_pairs)); } +Optimizer::PassToken CreateRemoveDontInlinePass() { + return MakeUnique<Optimizer::PassToken::Impl>( + MakeUnique<opt::RemoveDontInline>()); +} } // namespace spvtools diff --git a/source/opt/pass.h b/source/opt/pass.h index a8c9c4b4..b2303e23 100644 --- a/source/opt/pass.h +++ b/source/opt/pass.h @@ -28,6 +28,13 @@ #include "spirv-tools/libspirv.hpp" #include "types.h" +// Avoid unused variable warning/error on Linux +#ifndef NDEBUG +#define USE_ASSERT(x) assert(x) +#else +#define USE_ASSERT(x) ((void)(x)) +#endif + namespace spvtools { namespace opt { @@ -129,7 +136,7 @@ class Pass { // Processes the given |module|. Returns Status::Failure if errors occur when // processing. Returns the corresponding Status::Success if processing is - // succesful to indicate whether changes are made to the module. + // successful to indicate whether changes are made to the module. virtual Status Process() = 0; // Return the next available SSA id and increment it. diff --git a/source/opt/pass_manager.cpp b/source/opt/pass_manager.cpp index be53d344..a73ff7cf 100644 --- a/source/opt/pass_manager.cpp +++ b/source/opt/pass_manager.cpp @@ -35,10 +35,18 @@ Pass::Status PassManager::Run(IRContext* context) { if (print_all_stream_) { std::vector<uint32_t> binary; context->module()->ToBinary(&binary, false); - SpirvTools t(SPV_ENV_UNIVERSAL_1_2); + SpirvTools t(target_env_); + t.SetMessageConsumer(consumer()); std::string disassembly; - t.Disassemble(binary, &disassembly, 0); - *print_all_stream_ << preamble << (pass ? pass->name() : "") << "\n" + std::string pass_name = (pass ? pass->name() : ""); + if (!t.Disassemble(binary, &disassembly, 0)) { + std::string msg = "Disassembly failed before pass "; + msg += pass_name + "\n"; + spv_position_t null_pos{0, 0, 0}; + consumer()(SPV_MSG_WARNING, "", null_pos, msg.c_str()); + return; + } + *print_all_stream_ << preamble << pass_name << "\n" << disassembly << std::endl; } }; diff --git a/source/opt/pass_manager.h b/source/opt/pass_manager.h index 9686dddc..11961a33 100644 --- a/source/opt/pass_manager.h +++ b/source/opt/pass_manager.h @@ -54,7 +54,7 @@ class PassManager { // Adds an externally constructed pass. void AddPass(std::unique_ptr<Pass> pass); // Uses the argument |args| to construct a pass instance of type |T|, and adds - // the pass instance to this pass manger. The pass added will use this pass + // the pass instance to this pass manager. The pass added will use this pass // manager's message consumer. template <typename T, typename... Args> void AddPass(Args&&... args); @@ -70,7 +70,7 @@ class PassManager { // Runs all passes on the given |module|. Returns Status::Failure if errors // occur when processing using one of the registered passes. All passes // registered after the error-reporting pass will be skipped. Returns the - // corresponding Status::Success if processing is succesful to indicate + // corresponding Status::Success if processing is successful to indicate // whether changes are made to the module. // // After running all the passes, they are removed from the list. diff --git a/source/opt/passes.h b/source/opt/passes.h index f3c30d57..a12c76b8 100644 --- a/source/opt/passes.h +++ b/source/opt/passes.h @@ -34,6 +34,7 @@ #include "source/opt/desc_sroa.h" #include "source/opt/eliminate_dead_constant_pass.h" #include "source/opt/eliminate_dead_functions_pass.h" +#include "source/opt/eliminate_dead_input_components_pass.h" #include "source/opt/eliminate_dead_members_pass.h" #include "source/opt/empty_pass.h" #include "source/opt/fix_storage_class.h" @@ -64,6 +65,7 @@ #include "source/opt/reduce_load_size.h" #include "source/opt/redundancy_elimination.h" #include "source/opt/relax_float_ops_pass.h" +#include "source/opt/remove_dontinline_pass.h" #include "source/opt/remove_duplicates_pass.h" #include "source/opt/remove_unused_interface_variables_pass.h" #include "source/opt/replace_desc_array_access_using_var_index.h" @@ -71,10 +73,11 @@ #include "source/opt/scalar_replacement_pass.h" #include "source/opt/set_spec_constant_default_value_pass.h" #include "source/opt/simplification_pass.h" +#include "source/opt/spread_volatile_semantics.h" #include "source/opt/ssa_rewrite_pass.h" #include "source/opt/strength_reduction_pass.h" #include "source/opt/strip_debug_info_pass.h" -#include "source/opt/strip_reflect_info_pass.h" +#include "source/opt/strip_nonsemantic_info_pass.h" #include "source/opt/unify_const_pass.h" #include "source/opt/upgrade_memory_model.h" #include "source/opt/vector_dce.h" diff --git a/source/opt/private_to_local_pass.cpp b/source/opt/private_to_local_pass.cpp index 12a226d5..80fb4c53 100644 --- a/source/opt/private_to_local_pass.cpp +++ b/source/opt/private_to_local_pass.cpp @@ -135,7 +135,7 @@ bool PrivateToLocalPass::MoveVariable(Instruction* variable, // Place the variable at the start of the first basic block. context()->AnalyzeUses(variable); context()->set_instr_block(variable, &*function->begin()); - function->begin()->begin()->InsertBefore(move(var)); + function->begin()->begin()->InsertBefore(std::move(var)); // Update uses where the type may have changed. return UpdateUses(variable); diff --git a/source/opt/private_to_local_pass.h b/source/opt/private_to_local_pass.h index c6127d67..e96a965e 100644 --- a/source/opt/private_to_local_pass.h +++ b/source/opt/private_to_local_pass.h @@ -44,7 +44,7 @@ class PrivateToLocalPass : public Pass { // class of |function|. Returns false if the variable could not be moved. bool MoveVariable(Instruction* variable, Function* function); - // |inst| is an instruction declaring a varible. If that variable is + // |inst| is an instruction declaring a variable. If that variable is // referenced in a single function and all of uses are valid as defined by // |IsValidUse|, then that function is returned. Otherwise, the return // value is |nullptr|. diff --git a/source/opt/redundancy_elimination.h b/source/opt/redundancy_elimination.h index 91809b5d..40451f40 100644 --- a/source/opt/redundancy_elimination.h +++ b/source/opt/redundancy_elimination.h @@ -41,7 +41,7 @@ class RedundancyEliminationPass : public LocalRedundancyEliminationPass { // in the function containing |bb|. // // |value_to_ids| is a map from value number to ids. If {vn, id} is in - // |value_to_ids| then vn is the value number of id, and the defintion of id + // |value_to_ids| then vn is the value number of id, and the definition of id // dominates |bb|. // // Returns true if at least one instruction is deleted. diff --git a/source/opt/register_pressure.cpp b/source/opt/register_pressure.cpp index 5750c6d4..1ad33738 100644 --- a/source/opt/register_pressure.cpp +++ b/source/opt/register_pressure.cpp @@ -378,7 +378,7 @@ void RegisterLiveness::SimulateFusion( // The loop fusion is injecting the l1 before the l2, the latch of l1 will be // connected to the header of l2. // To compute the register usage, we inject the loop live-in (union of l1 and - // l2 live-in header blocks) into the the live in/out of each basic block of + // l2 live-in header blocks) into the live in/out of each basic block of // l1 to get the peak register usage. We then repeat the operation to for l2 // basic blocks but in this case we inject the live-out of the latch of l1. auto live_loop = MakeFilterIteratorRange( diff --git a/source/opt/remove_dontinline_pass.cpp b/source/opt/remove_dontinline_pass.cpp new file mode 100644 index 00000000..4dd1cd4f --- /dev/null +++ b/source/opt/remove_dontinline_pass.cpp @@ -0,0 +1,49 @@ +// Copyright (c) 2022 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/opt/remove_dontinline_pass.h" + +namespace spvtools { +namespace opt { + +Pass::Status RemoveDontInline::Process() { + bool modified = false; + modified = ClearDontInlineFunctionControl(); + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); +} + +bool RemoveDontInline::ClearDontInlineFunctionControl() { + bool modified = false; + for (auto& func : *get_module()) { + ClearDontInlineFunctionControl(&func); + } + return modified; +} + +bool RemoveDontInline::ClearDontInlineFunctionControl(Function* function) { + constexpr uint32_t kFunctionControlInOperandIdx = 0; + Instruction* function_inst = &function->DefInst(); + uint32_t function_control = + function_inst->GetSingleWordInOperand(kFunctionControlInOperandIdx); + + if ((function_control & SpvFunctionControlDontInlineMask) == 0) { + return false; + } + function_control &= ~SpvFunctionControlDontInlineMask; + function_inst->SetInOperand(kFunctionControlInOperandIdx, {function_control}); + return true; +} + +} // namespace opt +} // namespace spvtools diff --git a/source/opt/remove_dontinline_pass.h b/source/opt/remove_dontinline_pass.h new file mode 100644 index 00000000..16243199 --- /dev/null +++ b/source/opt/remove_dontinline_pass.h @@ -0,0 +1,42 @@ +// Copyright (c) 2022 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_OPT_REMOVE_DONTINLINE_PASS_H_ +#define SOURCE_OPT_REMOVE_DONTINLINE_PASS_H_ + +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class RemoveDontInline : public Pass { + public: + const char* name() const override { return "remove-dont-inline"; } + Status Process() override; + + private: + // Clears the DontInline function control from every function in the module. + // Returns true of a change was made. + bool ClearDontInlineFunctionControl(); + + // Clears the DontInline function control from |function|. + // Returns true of a change was made. + bool ClearDontInlineFunctionControl(Function* function); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_REMOVE_DONTINLINE_PASS_H_ diff --git a/source/opt/remove_duplicates_pass.cpp b/source/opt/remove_duplicates_pass.cpp index 0e65cc8d..1ed8e2a0 100644 --- a/source/opt/remove_duplicates_pass.cpp +++ b/source/opt/remove_duplicates_pass.cpp @@ -72,9 +72,8 @@ bool RemoveDuplicatesPass::RemoveDuplicatesExtInstImports() const { std::unordered_map<std::string, SpvId> ext_inst_imports; for (auto* i = &*context()->ext_inst_import_begin(); i;) { - auto res = ext_inst_imports.emplace( - reinterpret_cast<const char*>(i->GetInOperand(0u).words.data()), - i->result_id()); + auto res = ext_inst_imports.emplace(i->GetInOperand(0u).AsString(), + i->result_id()); if (res.second) { // Never seen before, keep it. i = i->NextNode(); diff --git a/source/opt/replace_desc_array_access_using_var_index.cpp b/source/opt/replace_desc_array_access_using_var_index.cpp index 1082e679..4cadf600 100644 --- a/source/opt/replace_desc_array_access_using_var_index.cpp +++ b/source/opt/replace_desc_array_access_using_var_index.cpp @@ -253,8 +253,12 @@ void ReplaceDescArrayAccessUsingVarIndex::ReplaceNonUniformAccessWithSwitchCase( Instruction* access_chain_final_user, Instruction* access_chain, uint32_t number_of_elements, const std::deque<Instruction*>& insts_to_be_cloned) const { - // Create merge block and add terminator auto* block = context()->get_instr_block(access_chain_final_user); + // If the instruction does not belong to a block (i.e. in the case of + // OpDecorate), no replacement is needed. + if (!block) return; + + // Create merge block and add terminator auto* merge_block = SeparateInstructionsIntoNewBlock( block, access_chain_final_user->NextNode()); diff --git a/source/opt/replace_desc_array_access_using_var_index.h b/source/opt/replace_desc_array_access_using_var_index.h index e18222c8..0c97f7eb 100644 --- a/source/opt/replace_desc_array_access_using_var_index.h +++ b/source/opt/replace_desc_array_access_using_var_index.h @@ -47,7 +47,7 @@ class ReplaceDescArrayAccessUsingVarIndex : public Pass { } private: - // Replaces all acceses to |var| using variable indices with constant + // Replaces all accesses to |var| using variable indices with constant // elements of the array |var|. Creates switch-case statements to determine // the value of the variable index for all the possible cases. Returns // whether replacement is done or not. @@ -170,7 +170,7 @@ class ReplaceDescArrayAccessUsingVarIndex : public Pass { // Creates and adds an OpSwitch used for the selection of OpAccessChain whose // first Indexes operand is |access_chain_index_var_id|. The OpSwitch will be // added at the end of |parent_block|. It will jump to |default_id| for the - // default case and jumps to one of case blocks whoes ids are |case_block_ids| + // default case and jumps to one of case blocks whose ids are |case_block_ids| // if |access_chain_index_var_id| matches the case number. |merge_id| is the // merge block id. void AddSwitchForAccessChain( diff --git a/source/opt/replace_invalid_opc.cpp b/source/opt/replace_invalid_opc.cpp index e3b9d3e4..1dcd06f5 100644 --- a/source/opt/replace_invalid_opc.cpp +++ b/source/opt/replace_invalid_opc.cpp @@ -112,8 +112,7 @@ bool ReplaceInvalidOpcodePass::RewriteFunction(Function* function, } Instruction* file_name = context()->get_def_use_mgr()->GetDef(file_name_id); - const char* source = reinterpret_cast<const char*>( - &file_name->GetInOperand(0).words[0]); + const std::string source = file_name->GetInOperand(0).AsString(); // Get the line number and column number. uint32_t line_number = @@ -121,7 +120,7 @@ bool ReplaceInvalidOpcodePass::RewriteFunction(Function* function, uint32_t col_number = last_line_dbg_inst->GetSingleWordInOperand(2); // Replace the instruction. - ReplaceInstruction(inst, source, line_number, col_number); + ReplaceInstruction(inst, source.c_str(), line_number, col_number); } } }, diff --git a/source/opt/scalar_analysis.cpp b/source/opt/scalar_analysis.cpp index 38555e64..2b0a824c 100644 --- a/source/opt/scalar_analysis.cpp +++ b/source/opt/scalar_analysis.cpp @@ -581,7 +581,7 @@ static void PushToString(T id, std::u32string* str) { // Implements the hashing of SENodes. size_t SENodeHash::operator()(const SENode* node) const { - // Concatinate the terms into a string which we can hash. + // Concatenate the terms into a string which we can hash. std::u32string hash_string{}; // Hashing the type as a string is safer than hashing the enum as the enum is diff --git a/source/opt/scalar_analysis_nodes.h b/source/opt/scalar_analysis_nodes.h index b0e3fefd..91ce446f 100644 --- a/source/opt/scalar_analysis_nodes.h +++ b/source/opt/scalar_analysis_nodes.h @@ -167,7 +167,7 @@ class SENode { const ChildContainerType& GetChildren() const { return children_; } ChildContainerType& GetChildren() { return children_; } - // Return true if this node is a cant compute node. + // Return true if this node is a can't compute node. bool IsCantCompute() const { return GetType() == CanNotCompute; } // Implements a casting method for each type. diff --git a/source/opt/scalar_analysis_simplification.cpp b/source/opt/scalar_analysis_simplification.cpp index 52f2d6ad..3c1ecc08 100644 --- a/source/opt/scalar_analysis_simplification.cpp +++ b/source/opt/scalar_analysis_simplification.cpp @@ -88,7 +88,7 @@ class SENodeSimplifyImpl { private: // Recursively descend through the graph to build up the accumulator objects - // which are used to flatten the graph. |child| is the node currenty being + // which are used to flatten the graph. |child| is the node currently being // traversed and the |negation| flag is used to signify that this operation // was preceded by a unary negative operation and as such the result should be // negated. @@ -134,7 +134,7 @@ class SENodeSimplifyImpl { // offset. SENode* EliminateZeroCoefficientRecurrents(SENode* node); - // A reference the the analysis which requested the simplification. + // A reference the analysis which requested the simplification. ScalarEvolutionAnalysis& analysis_; // The node being simplified. diff --git a/source/opt/scalar_replacement_pass.cpp b/source/opt/scalar_replacement_pass.cpp index 4d6a7aad..e27c828b 100644 --- a/source/opt/scalar_replacement_pass.cpp +++ b/source/opt/scalar_replacement_pass.cpp @@ -24,6 +24,7 @@ #include "source/opt/reflect.h" #include "source/opt/types.h" #include "source/util/make_unique.h" +#include "types.h" static const uint32_t kDebugValueOperandValueIndex = 5; static const uint32_t kDebugValueOperandExpressionIndex = 6; @@ -395,7 +396,7 @@ bool ScalarReplacementPass::CreateReplacementVariables( if (!components_used || components_used->count(elem)) { CreateVariable(*id, inst, elem, replacements); } else { - replacements->push_back(CreateNullConstant(*id)); + replacements->push_back(GetUndef(*id)); } elem++; }); @@ -406,8 +407,8 @@ bool ScalarReplacementPass::CreateReplacementVariables( CreateVariable(type->GetSingleWordInOperand(0u), inst, i, replacements); } else { - replacements->push_back( - CreateNullConstant(type->GetSingleWordInOperand(0u))); + uint32_t element_type_id = type->GetSingleWordInOperand(0); + replacements->push_back(GetUndef(element_type_id)); } } break; @@ -429,6 +430,10 @@ bool ScalarReplacementPass::CreateReplacementVariables( replacements->end(); } +Instruction* ScalarReplacementPass::GetUndef(uint32_t type_id) { + return get_def_use_mgr()->GetDef(Type2Undef(type_id)); +} + void ScalarReplacementPass::TransferAnnotations( const Instruction* source, std::vector<Instruction*>* replacements) { // Only transfer invariant and restrict decorations on the variable. There are @@ -981,20 +986,6 @@ ScalarReplacementPass::GetUsedComponents(Instruction* inst) { return result; } -Instruction* ScalarReplacementPass::CreateNullConstant(uint32_t type_id) { - analysis::TypeManager* type_mgr = context()->get_type_mgr(); - analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); - - const analysis::Type* type = type_mgr->GetType(type_id); - const analysis::Constant* null_const = const_mgr->GetConstant(type, {}); - Instruction* null_inst = - const_mgr->GetDefiningInstruction(null_const, type_id); - if (null_inst != nullptr) { - context()->UpdateDefUse(null_inst); - } - return null_inst; -} - uint64_t ScalarReplacementPass::GetMaxLegalIndex( const Instruction* var_inst) const { assert(var_inst->opcode() == SpvOpVariable && diff --git a/source/opt/scalar_replacement_pass.h b/source/opt/scalar_replacement_pass.h index 0928830c..76afc267 100644 --- a/source/opt/scalar_replacement_pass.h +++ b/source/opt/scalar_replacement_pass.h @@ -23,14 +23,14 @@ #include <vector> #include "source/opt/function.h" -#include "source/opt/pass.h" +#include "source/opt/mem_pass.h" #include "source/opt/type_manager.h" namespace spvtools { namespace opt { // Documented in optimizer.hpp -class ScalarReplacementPass : public Pass { +class ScalarReplacementPass : public MemPass { private: static const uint32_t kDefaultLimit = 100; @@ -234,10 +234,8 @@ class ScalarReplacementPass : public Pass { std::unique_ptr<std::unordered_set<int64_t>> GetUsedComponents( Instruction* inst); - // Returns an instruction defining a null constant with type |type_id|. If - // one already exists, it is returned. Otherwise a new one is created. - // Returns |nullptr| if the new constant could not be created. - Instruction* CreateNullConstant(uint32_t type_id); + // Returns an instruction defining an undefined value type |type_id|. + Instruction* GetUndef(uint32_t type_id); // Maps storage type to a pointer type enclosing that type. std::unordered_map<uint32_t, uint32_t> pointee_to_pointer_; diff --git a/source/opt/spread_volatile_semantics.cpp b/source/opt/spread_volatile_semantics.cpp new file mode 100644 index 00000000..a1d34329 --- /dev/null +++ b/source/opt/spread_volatile_semantics.cpp @@ -0,0 +1,318 @@ +// Copyright (c) 2022 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/opt/spread_volatile_semantics.h" + +#include "source/opt/decoration_manager.h" +#include "source/opt/ir_builder.h" +#include "source/spirv_constant.h" + +namespace spvtools { +namespace opt { +namespace { + +const uint32_t kOpDecorateInOperandBuiltinDecoration = 2u; +const uint32_t kOpLoadInOperandMemoryOperands = 1u; +const uint32_t kOpEntryPointInOperandEntryPoint = 1u; +const uint32_t kOpEntryPointInOperandInterface = 3u; + +bool HasBuiltinDecoration(analysis::DecorationManager* decoration_manager, + uint32_t var_id, uint32_t built_in) { + return decoration_manager->FindDecoration( + var_id, SpvDecorationBuiltIn, [built_in](const Instruction& inst) { + return built_in == inst.GetSingleWordInOperand( + kOpDecorateInOperandBuiltinDecoration); + }); +} + +bool IsBuiltInForRayTracingVolatileSemantics(uint32_t built_in) { + switch (built_in) { + case SpvBuiltInSMIDNV: + case SpvBuiltInWarpIDNV: + case SpvBuiltInSubgroupSize: + case SpvBuiltInSubgroupLocalInvocationId: + case SpvBuiltInSubgroupEqMask: + case SpvBuiltInSubgroupGeMask: + case SpvBuiltInSubgroupGtMask: + case SpvBuiltInSubgroupLeMask: + case SpvBuiltInSubgroupLtMask: + return true; + default: + return false; + } +} + +bool HasBuiltinForRayTracingVolatileSemantics( + analysis::DecorationManager* decoration_manager, uint32_t var_id) { + return decoration_manager->FindDecoration( + var_id, SpvDecorationBuiltIn, [](const Instruction& inst) { + uint32_t built_in = + inst.GetSingleWordInOperand(kOpDecorateInOperandBuiltinDecoration); + return IsBuiltInForRayTracingVolatileSemantics(built_in); + }); +} + +bool HasVolatileDecoration(analysis::DecorationManager* decoration_manager, + uint32_t var_id) { + return decoration_manager->HasDecoration(var_id, SpvDecorationVolatile); +} + +bool HasOnlyEntryPointsAsFunctions(IRContext* context, Module* module) { + std::unordered_set<uint32_t> entry_function_ids; + for (Instruction& entry_point : module->entry_points()) { + entry_function_ids.insert( + entry_point.GetSingleWordInOperand(kOpEntryPointInOperandEntryPoint)); + } + for (auto& function : *module) { + if (entry_function_ids.find(function.result_id()) == + entry_function_ids.end()) { + std::string message( + "Functions of SPIR-V for spread-volatile-semantics pass input must " + "be inlined except entry points"); + message += "\n " + function.DefInst().PrettyPrint( + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + context->consumer()(SPV_MSG_ERROR, "", {0, 0, 0}, message.c_str()); + return false; + } + } + return true; +} + +} // namespace + +Pass::Status SpreadVolatileSemantics::Process() { + if (HasNoExecutionModel()) { + return Status::SuccessWithoutChange; + } + + if (!HasOnlyEntryPointsAsFunctions(context(), get_module())) { + return Status::Failure; + } + + const bool is_vk_memory_model_enabled = + context()->get_feature_mgr()->HasCapability( + SpvCapabilityVulkanMemoryModel); + CollectTargetsForVolatileSemantics(is_vk_memory_model_enabled); + + // If VulkanMemoryModel capability is not enabled, we have to set Volatile + // decoration for interface variables instead of setting Volatile for load + // instructions. If an interface (or pointers to it) is used by two load + // instructions in two entry points and one must be volatile while another + // is not, we have to report an error for the conflict. + if (!is_vk_memory_model_enabled && + HasInterfaceInConflictOfVolatileSemantics()) { + return Status::Failure; + } + + return SpreadVolatileSemanticsToVariables(is_vk_memory_model_enabled); +} + +Pass::Status SpreadVolatileSemantics::SpreadVolatileSemanticsToVariables( + const bool is_vk_memory_model_enabled) { + Status status = Status::SuccessWithoutChange; + for (Instruction& var : context()->types_values()) { + auto entry_function_ids = + EntryFunctionsToSpreadVolatileSemanticsForVar(var.result_id()); + if (entry_function_ids.empty()) { + continue; + } + + if (is_vk_memory_model_enabled) { + SetVolatileForLoadsInEntries(&var, entry_function_ids); + } else { + DecorateVarWithVolatile(&var); + } + status = Status::SuccessWithChange; + } + return status; +} + +bool SpreadVolatileSemantics::IsTargetUsedByNonVolatileLoadInEntryPoint( + uint32_t var_id, Instruction* entry_point) { + uint32_t entry_function_id = + entry_point->GetSingleWordInOperand(kOpEntryPointInOperandEntryPoint); + return !VisitLoadsOfPointersToVariableInEntries( + var_id, + [](Instruction* load) { + // If it has a load without volatile memory operand, finish traversal + // and return false. + if (load->NumInOperands() <= kOpLoadInOperandMemoryOperands) { + return false; + } + uint32_t memory_operands = + load->GetSingleWordInOperand(kOpLoadInOperandMemoryOperands); + return (memory_operands & SpvMemoryAccessVolatileMask) != 0; + }, + {entry_function_id}); +} + +bool SpreadVolatileSemantics::HasInterfaceInConflictOfVolatileSemantics() { + for (Instruction& entry_point : get_module()->entry_points()) { + SpvExecutionModel execution_model = + static_cast<SpvExecutionModel>(entry_point.GetSingleWordInOperand(0)); + for (uint32_t operand_index = kOpEntryPointInOperandInterface; + operand_index < entry_point.NumInOperands(); ++operand_index) { + uint32_t var_id = entry_point.GetSingleWordInOperand(operand_index); + if (!EntryFunctionsToSpreadVolatileSemanticsForVar(var_id).empty() && + !IsTargetForVolatileSemantics(var_id, execution_model) && + IsTargetUsedByNonVolatileLoadInEntryPoint(var_id, &entry_point)) { + Instruction* inst = context()->get_def_use_mgr()->GetDef(var_id); + context()->EmitErrorMessage( + "Variable is a target for Volatile semantics for an entry point, " + "but it is not for another entry point", + inst); + return true; + } + } + } + return false; +} + +void SpreadVolatileSemantics::MarkVolatileSemanticsForVariable( + uint32_t var_id, Instruction* entry_point) { + uint32_t entry_function_id = + entry_point->GetSingleWordInOperand(kOpEntryPointInOperandEntryPoint); + auto itr = var_ids_to_entry_fn_for_volatile_semantics_.find(var_id); + if (itr == var_ids_to_entry_fn_for_volatile_semantics_.end()) { + var_ids_to_entry_fn_for_volatile_semantics_[var_id] = {entry_function_id}; + return; + } + itr->second.insert(entry_function_id); +} + +void SpreadVolatileSemantics::CollectTargetsForVolatileSemantics( + const bool is_vk_memory_model_enabled) { + for (Instruction& entry_point : get_module()->entry_points()) { + SpvExecutionModel execution_model = + static_cast<SpvExecutionModel>(entry_point.GetSingleWordInOperand(0)); + for (uint32_t operand_index = kOpEntryPointInOperandInterface; + operand_index < entry_point.NumInOperands(); ++operand_index) { + uint32_t var_id = entry_point.GetSingleWordInOperand(operand_index); + if (!IsTargetForVolatileSemantics(var_id, execution_model)) { + continue; + } + if (is_vk_memory_model_enabled || + IsTargetUsedByNonVolatileLoadInEntryPoint(var_id, &entry_point)) { + MarkVolatileSemanticsForVariable(var_id, &entry_point); + } + } + } +} + +void SpreadVolatileSemantics::DecorateVarWithVolatile(Instruction* var) { + analysis::DecorationManager* decoration_manager = + context()->get_decoration_mgr(); + uint32_t var_id = var->result_id(); + if (HasVolatileDecoration(decoration_manager, var_id)) { + return; + } + get_decoration_mgr()->AddDecoration( + SpvOpDecorate, {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {var_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, + {SpvDecorationVolatile}}}); +} + +bool SpreadVolatileSemantics::VisitLoadsOfPointersToVariableInEntries( + uint32_t var_id, const std::function<bool(Instruction*)>& handle_load, + const std::unordered_set<uint32_t>& entry_function_ids) { + std::vector<uint32_t> worklist({var_id}); + auto* def_use_mgr = context()->get_def_use_mgr(); + while (!worklist.empty()) { + uint32_t ptr_id = worklist.back(); + worklist.pop_back(); + bool finish_traversal = !def_use_mgr->WhileEachUser( + ptr_id, [this, &worklist, &ptr_id, handle_load, + &entry_function_ids](Instruction* user) { + BasicBlock* block = context()->get_instr_block(user); + if (block == nullptr || + entry_function_ids.find(block->GetParent()->result_id()) == + entry_function_ids.end()) { + return true; + } + + if (user->opcode() == SpvOpAccessChain || + user->opcode() == SpvOpInBoundsAccessChain || + user->opcode() == SpvOpPtrAccessChain || + user->opcode() == SpvOpInBoundsPtrAccessChain || + user->opcode() == SpvOpCopyObject) { + if (ptr_id == user->GetSingleWordInOperand(0)) + worklist.push_back(user->result_id()); + return true; + } + + if (user->opcode() != SpvOpLoad) { + return true; + } + + return handle_load(user); + }); + if (finish_traversal) return false; + } + return true; +} + +void SpreadVolatileSemantics::SetVolatileForLoadsInEntries( + Instruction* var, const std::unordered_set<uint32_t>& entry_function_ids) { + // Set Volatile memory operand for all load instructions if they do not have + // it. + VisitLoadsOfPointersToVariableInEntries( + var->result_id(), + [](Instruction* load) { + if (load->NumInOperands() <= kOpLoadInOperandMemoryOperands) { + load->AddOperand( + {SPV_OPERAND_TYPE_MEMORY_ACCESS, {SpvMemoryAccessVolatileMask}}); + return true; + } + uint32_t memory_operands = + load->GetSingleWordInOperand(kOpLoadInOperandMemoryOperands); + memory_operands |= SpvMemoryAccessVolatileMask; + load->SetInOperand(kOpLoadInOperandMemoryOperands, {memory_operands}); + return true; + }, + entry_function_ids); +} + +bool SpreadVolatileSemantics::IsTargetForVolatileSemantics( + uint32_t var_id, SpvExecutionModel execution_model) { + analysis::DecorationManager* decoration_manager = + context()->get_decoration_mgr(); + if (execution_model == SpvExecutionModelFragment) { + return get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 6) && + HasBuiltinDecoration(decoration_manager, var_id, + SpvBuiltInHelperInvocation); + } + + if (execution_model == SpvExecutionModelIntersectionKHR || + execution_model == SpvExecutionModelIntersectionNV) { + if (HasBuiltinDecoration(decoration_manager, var_id, + SpvBuiltInRayTmaxKHR)) { + return true; + } + } + + switch (execution_model) { + case SpvExecutionModelRayGenerationKHR: + case SpvExecutionModelClosestHitKHR: + case SpvExecutionModelMissKHR: + case SpvExecutionModelCallableKHR: + case SpvExecutionModelIntersectionKHR: + return HasBuiltinForRayTracingVolatileSemantics(decoration_manager, + var_id); + default: + return false; + } +} + +} // namespace opt +} // namespace spvtools diff --git a/source/opt/spread_volatile_semantics.h b/source/opt/spread_volatile_semantics.h new file mode 100644 index 00000000..531a21d5 --- /dev/null +++ b/source/opt/spread_volatile_semantics.h @@ -0,0 +1,117 @@ +// Copyright (c) 2022 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_OPT_SPREAD_VOLATILE_SEMANTICS_H_ +#define SOURCE_OPT_SPREAD_VOLATILE_SEMANTICS_H_ + +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class SpreadVolatileSemantics : public Pass { + public: + SpreadVolatileSemantics() {} + + const char* name() const override { return "spread-volatile-semantics"; } + + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | IRContext::kAnalysisDecorations | + IRContext::kAnalysisInstrToBlockMapping; + } + + private: + // Returns true if it does not have an execution model. Linkage shaders do not + // have an execution model. + bool HasNoExecutionModel() { + return get_module()->entry_points().empty() && + context()->get_feature_mgr()->HasCapability(SpvCapabilityLinkage); + } + + // Iterates interface variables and spreads the Volatile semantics if it has + // load instructions for the Volatile semantics. + Pass::Status SpreadVolatileSemanticsToVariables( + const bool is_vk_memory_model_enabled); + + // Returns whether |var_id| is the result id of a target builtin variable for + // the volatile semantics for |execution_model| based on the Vulkan spec + // VUID-StandaloneSpirv-VulkanMemoryModel-04678 or + // VUID-StandaloneSpirv-VulkanMemoryModel-04679. + bool IsTargetForVolatileSemantics(uint32_t var_id, + SpvExecutionModel execution_model); + + // Collects interface variables that need the volatile semantics. + // |is_vk_memory_model_enabled| is true if VulkanMemoryModel capability is + // enabled. + void CollectTargetsForVolatileSemantics( + const bool is_vk_memory_model_enabled); + + // Reports an error if an interface variable is used by two entry points and + // it needs the Volatile decoration for one but not for another. Returns true + // if the error must be reported. + bool HasInterfaceInConflictOfVolatileSemantics(); + + // Returns whether the variable whose result is |var_id| is used by a + // non-volatile load or a pointer to it is used by a non-volatile load in + // |entry_point| or not. + bool IsTargetUsedByNonVolatileLoadInEntryPoint(uint32_t var_id, + Instruction* entry_point); + + // Visits load instructions of pointers to variable whose result id is + // |var_id| if the load instructions are in entry points whose + // function id is one of |entry_function_ids|. |handle_load| is a function to + // do some actions for the load instructions. Finishes the traversal and + // returns false if |handle_load| returns false for a load instruction. + // Otherwise, returns true after running |handle_load| for all the load + // instructions. + bool VisitLoadsOfPointersToVariableInEntries( + uint32_t var_id, const std::function<bool(Instruction*)>& handle_load, + const std::unordered_set<uint32_t>& entry_function_ids); + + // Sets Memory Operands of OpLoad instructions that load |var| or pointers + // of |var| as Volatile if the function id of the OpLoad instruction is + // included in |entry_function_ids|. + void SetVolatileForLoadsInEntries( + Instruction* var, const std::unordered_set<uint32_t>& entry_function_ids); + + // Adds OpDecorate Volatile for |var| if it does not exist. + void DecorateVarWithVolatile(Instruction* var); + + // Returns a set of entry function ids to spread the volatile semantics for + // the variable with the result id |var_id|. + std::unordered_set<uint32_t> EntryFunctionsToSpreadVolatileSemanticsForVar( + uint32_t var_id) { + auto itr = var_ids_to_entry_fn_for_volatile_semantics_.find(var_id); + if (itr == var_ids_to_entry_fn_for_volatile_semantics_.end()) return {}; + return itr->second; + } + + // Specifies that we have to spread the volatile semantics for the + // variable with the result id |var_id| for the entry point |entry_point|. + void MarkVolatileSemanticsForVariable(uint32_t var_id, + Instruction* entry_point); + + // Result ids of variables to entry function ids for the volatile semantics + // spread. + std::unordered_map<uint32_t, std::unordered_set<uint32_t>> + var_ids_to_entry_fn_for_volatile_semantics_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_SPREAD_VOLATILE_SEMANTICS_H_ diff --git a/source/opt/strength_reduction_pass.h b/source/opt/strength_reduction_pass.h index 8dfeb307..1cbbbcc6 100644 --- a/source/opt/strength_reduction_pass.h +++ b/source/opt/strength_reduction_pass.h @@ -34,7 +34,7 @@ class StrengthReductionPass : public Pass { // Returns true if something changed. bool ReplaceMultiplyByPowerOf2(BasicBlock::iterator*); - // Scan the types and constants in the module looking for the the integer + // Scan the types and constants in the module looking for the integer // types that we are // interested in. The shift operation needs a small unsigned integer. We // need to find diff --git a/source/opt/strip_debug_info_pass.cpp b/source/opt/strip_debug_info_pass.cpp index c86ce578..6a0ebf24 100644 --- a/source/opt/strip_debug_info_pass.cpp +++ b/source/opt/strip_debug_info_pass.cpp @@ -14,6 +14,7 @@ #include "source/opt/strip_debug_info_pass.h" #include "source/opt/ir_context.h" +#include "source/util/string_utils.h" namespace spvtools { namespace opt { @@ -21,9 +22,8 @@ namespace opt { Pass::Status StripDebugInfoPass::Process() { bool uses_non_semantic_info = false; for (auto& inst : context()->module()->extensions()) { - const char* ext_name = - reinterpret_cast<const char*>(&inst.GetInOperand(0).words[0]); - if (0 == std::strcmp(ext_name, "SPV_KHR_non_semantic_info")) { + const std::string ext_name = inst.GetInOperand(0).AsString(); + if (ext_name == "SPV_KHR_non_semantic_info") { uses_non_semantic_info = true; } } @@ -46,9 +46,10 @@ Pass::Status StripDebugInfoPass::Process() { if (use->opcode() == SpvOpExtInst) { auto ext_inst_set = def_use->GetDef(use->GetSingleWordInOperand(0u)); - const char* extension_name = reinterpret_cast<const char*>( - &ext_inst_set->GetInOperand(0).words[0]); - if (0 == std::strncmp(extension_name, "NonSemantic.", 12)) { + const std::string extension_name = + ext_inst_set->GetInOperand(0).AsString(); + if (spvtools::utils::starts_with(extension_name, + "NonSemantic.")) { // found a non-semantic use, return false as we cannot // remove this OpString return false; diff --git a/source/opt/strip_reflect_info_pass.cpp b/source/opt/strip_nonsemantic_info_pass.cpp index 8b0f2db7..cd1fbb63 100644 --- a/source/opt/strip_reflect_info_pass.cpp +++ b/source/opt/strip_nonsemantic_info_pass.cpp @@ -12,18 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "source/opt/strip_reflect_info_pass.h" +#include "source/opt/strip_nonsemantic_info_pass.h" #include <cstring> #include <vector> #include "source/opt/instruction.h" #include "source/opt/ir_context.h" +#include "source/util/string_utils.h" namespace spvtools { namespace opt { -Pass::Status StripReflectInfoPass::Process() { +Pass::Status StripNonSemanticInfoPass::Process() { bool modified = false; std::vector<Instruction*> to_remove; @@ -32,7 +33,8 @@ Pass::Status StripReflectInfoPass::Process() { for (auto& inst : context()->module()->annotations()) { switch (inst.opcode()) { case SpvOpDecorateStringGOOGLE: - if (inst.GetSingleWordInOperand(1) == SpvDecorationHlslSemanticGOOGLE) { + if (inst.GetSingleWordInOperand(1) == SpvDecorationHlslSemanticGOOGLE || + inst.GetSingleWordInOperand(1) == SpvDecorationUserTypeGOOGLE) { to_remove.push_back(&inst); } else { other_uses_for_decorate_string = true; @@ -40,7 +42,8 @@ Pass::Status StripReflectInfoPass::Process() { break; case SpvOpMemberDecorateStringGOOGLE: - if (inst.GetSingleWordInOperand(2) == SpvDecorationHlslSemanticGOOGLE) { + if (inst.GetSingleWordInOperand(2) == SpvDecorationHlslSemanticGOOGLE || + inst.GetSingleWordInOperand(2) == SpvDecorationUserTypeGOOGLE) { to_remove.push_back(&inst); } else { other_uses_for_decorate_string = true; @@ -60,33 +63,26 @@ Pass::Status StripReflectInfoPass::Process() { } for (auto& inst : context()->module()->extensions()) { - const char* ext_name = - reinterpret_cast<const char*>(&inst.GetInOperand(0).words[0]); - if (0 == std::strcmp(ext_name, "SPV_GOOGLE_hlsl_functionality1")) { + const std::string ext_name = inst.GetInOperand(0).AsString(); + if (ext_name == "SPV_GOOGLE_hlsl_functionality1") { + to_remove.push_back(&inst); + } else if (ext_name == "SPV_GOOGLE_user_type") { to_remove.push_back(&inst); } else if (!other_uses_for_decorate_string && - 0 == std::strcmp(ext_name, "SPV_GOOGLE_decorate_string")) { + ext_name == "SPV_GOOGLE_decorate_string") { to_remove.push_back(&inst); - } else if (0 == std::strcmp(ext_name, "SPV_KHR_non_semantic_info")) { + } else if (ext_name == "SPV_KHR_non_semantic_info") { to_remove.push_back(&inst); } } - // clear all debug data now if it hasn't been cleared already, to remove any - // remaining OpString that may have been referenced by non-semantic extinsts - for (auto& dbg : context()->debugs1()) to_remove.push_back(&dbg); - for (auto& dbg : context()->debugs2()) to_remove.push_back(&dbg); - for (auto& dbg : context()->debugs3()) to_remove.push_back(&dbg); - for (auto& dbg : context()->ext_inst_debuginfo()) to_remove.push_back(&dbg); - // remove any extended inst imports that are non semantic std::unordered_set<uint32_t> non_semantic_sets; for (auto& inst : context()->module()->ext_inst_imports()) { assert(inst.opcode() == SpvOpExtInstImport && "Expecting an import of an extension's instruction set."); - const char* extension_name = - reinterpret_cast<const char*>(&inst.GetInOperand(0).words[0]); - if (0 == std::strncmp(extension_name, "NonSemantic.", 12)) { + const std::string extension_name = inst.GetInOperand(0).AsString(); + if (spvtools::utils::starts_with(extension_name, "NonSemantic.")) { non_semantic_sets.insert(inst.result_id()); to_remove.push_back(&inst); } @@ -103,19 +99,10 @@ Pass::Status StripReflectInfoPass::Process() { to_remove.push_back(inst); } } - }); + }, + true); } - // OpName must come first, since they may refer to other debug instructions. - // If they are after the instructions that refer to, then they will be killed - // when that instruction is killed, which will lead to a double kill. - std::sort(to_remove.begin(), to_remove.end(), - [](Instruction* lhs, Instruction* rhs) -> bool { - if (lhs->opcode() == SpvOpName && rhs->opcode() != SpvOpName) - return true; - return false; - }); - for (auto* inst : to_remove) { modified = true; context()->KillInst(inst); diff --git a/source/opt/strip_reflect_info_pass.h b/source/opt/strip_nonsemantic_info_pass.h index 4e1999ed..ff4e2e1d 100644 --- a/source/opt/strip_reflect_info_pass.h +++ b/source/opt/strip_nonsemantic_info_pass.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef SOURCE_OPT_STRIP_REFLECT_INFO_PASS_H_ -#define SOURCE_OPT_STRIP_REFLECT_INFO_PASS_H_ +#ifndef SOURCE_OPT_STRIP_NONSEMANTIC_INFO_PASS_H_ +#define SOURCE_OPT_STRIP_NONSEMANTIC_INFO_PASS_H_ #include "source/opt/ir_context.h" #include "source/opt/module.h" @@ -23,9 +23,9 @@ namespace spvtools { namespace opt { // See optimizer.hpp for documentation. -class StripReflectInfoPass : public Pass { +class StripNonSemanticInfoPass : public Pass { public: - const char* name() const override { return "strip-reflect"; } + const char* name() const override { return "strip-nonsemantic"; } Status Process() override; // Return the mask of preserved Analyses. @@ -41,4 +41,4 @@ class StripReflectInfoPass : public Pass { } // namespace opt } // namespace spvtools -#endif // SOURCE_OPT_STRIP_REFLECT_INFO_PASS_H_ +#endif // SOURCE_OPT_STRIP_NONSEMANTIC_INFO_PASS_H_ diff --git a/source/opt/type_manager.cpp b/source/opt/type_manager.cpp index 7935ad33..a0006f55 100644 --- a/source/opt/type_manager.cpp +++ b/source/opt/type_manager.cpp @@ -23,6 +23,7 @@ #include "source/opt/log.h" #include "source/opt/reflect.h" #include "source/util/make_unique.h" +#include "source/util/string_utils.h" namespace spvtools { namespace opt { @@ -234,6 +235,7 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) { DefineParameterlessCase(PipeStorage); DefineParameterlessCase(NamedBarrier); DefineParameterlessCase(AccelerationStructureNV); + DefineParameterlessCase(RayQueryKHR); #undef DefineParameterlessCase case Type::kInteger: typeInst = MakeUnique<Instruction>( @@ -349,11 +351,8 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) { } case Type::kOpaque: { const Opaque* opaque = type->AsOpaque(); - size_t size = opaque->name().size(); // Convert to null-terminated packed UTF-8 string. - std::vector<uint32_t> words(size / 4 + 1, 0); - char* dst = reinterpret_cast<char*>(words.data()); - strncpy(dst, opaque->name().c_str(), size); + std::vector<uint32_t> words = spvtools::utils::MakeVector(opaque->name()); typeInst = MakeUnique<Instruction>( context(), SpvOpTypeOpaque, 0, id, std::initializer_list<Operand>{ @@ -529,6 +528,7 @@ Type* TypeManager::RebuildType(const Type& type) { DefineNoSubtypeCase(PipeStorage); DefineNoSubtypeCase(NamedBarrier); DefineNoSubtypeCase(AccelerationStructureNV); + DefineNoSubtypeCase(RayQueryKHR); #undef DefineNoSubtypeCase case Type::kVector: { const Vector* vec_ty = type.AsVector(); @@ -781,8 +781,7 @@ Type* TypeManager::RecordIfTypeDefinition(const Instruction& inst) { } } break; case SpvOpTypeOpaque: { - const uint32_t* data = inst.GetInOperand(0).words.data(); - type = new Opaque(reinterpret_cast<const char*>(data)); + type = new Opaque(inst.GetInOperand(0).AsString()); } break; case SpvOpTypePointer: { uint32_t pointee_type_id = inst.GetSingleWordInOperand(1); diff --git a/source/opt/type_manager.h b/source/opt/type_manager.h index ce9d83d4..72e37f48 100644 --- a/source/opt/type_manager.h +++ b/source/opt/type_manager.h @@ -160,6 +160,13 @@ class TypeManager { uint32_t GetFloatTypeId() { return GetTypeInstruction(GetFloatType()); } + Type* GetDoubleType() { + Float float_type(64); + return GetRegisteredType(&float_type); + } + + uint32_t GetDoubleTypeId() { return GetTypeInstruction(GetDoubleType()); } + Type* GetUIntVectorType(uint32_t size) { Vector vec_type(GetUIntType(), size); return GetRegisteredType(&vec_type); diff --git a/source/opt/types.cpp b/source/opt/types.cpp index b1eb3a50..ebbdc367 100644 --- a/source/opt/types.cpp +++ b/source/opt/types.cpp @@ -21,6 +21,7 @@ #include <string> #include <unordered_set> +#include "source/util/hash_combine.h" #include "source/util/make_unique.h" #include "spirv/unified1/spirv.h" @@ -28,6 +29,7 @@ namespace spvtools { namespace opt { namespace analysis { +using spvtools::utils::hash_combine; using U32VecVec = std::vector<std::vector<uint32_t>>; namespace { @@ -182,23 +184,26 @@ bool Type::operator==(const Type& other) const { } } -void Type::GetHashWords(std::vector<uint32_t>* words, - std::unordered_set<const Type*>* seen) const { - if (!seen->insert(this).second) { - return; +size_t Type::ComputeHashValue(size_t hash, SeenTypes* seen) const { + // Linear search through a dense, cache coherent vector is faster than O(log + // n) search in a complex data structure (eg std::set) for the generally small + // number of nodes. It also skips the overhead of an new/delete per Type + // (when inserting/removing from a set). + if (std::find(seen->begin(), seen->end(), this) != seen->end()) { + return hash; } - words->push_back(kind_); + seen->push_back(this); + + hash = hash_combine(hash, uint32_t(kind_)); for (const auto& d : decorations_) { - for (auto w : d) { - words->push_back(w); - } + hash = hash_combine(hash, d); } switch (kind_) { -#define DeclareKindCase(type) \ - case k##type: \ - As##type()->GetExtraHashWords(words, seen); \ +#define DeclareKindCase(type) \ + case k##type: \ + hash = As##type()->ComputeExtraStateHash(hash, seen); \ break DeclareKindCase(Void); DeclareKindCase(Bool); @@ -232,18 +237,13 @@ void Type::GetHashWords(std::vector<uint32_t>* words, break; } - seen->erase(this); + seen->pop_back(); + return hash; } size_t Type::HashValue() const { - std::u32string h; - std::vector<uint32_t> words; - GetHashWords(&words); - for (auto w : words) { - h.push_back(w); - } - - return std::hash<std::u32string>()(h); + SeenTypes seen; + return ComputeHashValue(0, &seen); } bool Integer::IsSameImpl(const Type* that, IsSameCache*) const { @@ -258,10 +258,8 @@ std::string Integer::str() const { return oss.str(); } -void Integer::GetExtraHashWords(std::vector<uint32_t>* words, - std::unordered_set<const Type*>*) const { - words->push_back(width_); - words->push_back(signed_); +size_t Integer::ComputeExtraStateHash(size_t hash, SeenTypes*) const { + return hash_combine(hash, width_, signed_); } bool Float::IsSameImpl(const Type* that, IsSameCache*) const { @@ -275,9 +273,8 @@ std::string Float::str() const { return oss.str(); } -void Float::GetExtraHashWords(std::vector<uint32_t>* words, - std::unordered_set<const Type*>*) const { - words->push_back(width_); +size_t Float::ComputeExtraStateHash(size_t hash, SeenTypes*) const { + return hash_combine(hash, width_); } Vector::Vector(const Type* type, uint32_t count) @@ -299,10 +296,11 @@ std::string Vector::str() const { return oss.str(); } -void Vector::GetExtraHashWords(std::vector<uint32_t>* words, - std::unordered_set<const Type*>* seen) const { - element_type_->GetHashWords(words, seen); - words->push_back(count_); +size_t Vector::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const { + // prefer form that doesn't require push/pop from stack: add state and + // make tail call. + hash = hash_combine(hash, count_); + return element_type_->ComputeHashValue(hash, seen); } Matrix::Matrix(const Type* type, uint32_t count) @@ -324,10 +322,9 @@ std::string Matrix::str() const { return oss.str(); } -void Matrix::GetExtraHashWords(std::vector<uint32_t>* words, - std::unordered_set<const Type*>* seen) const { - element_type_->GetHashWords(words, seen); - words->push_back(count_); +size_t Matrix::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const { + hash = hash_combine(hash, count_); + return element_type_->ComputeHashValue(hash, seen); } Image::Image(Type* type, SpvDim dimen, uint32_t d, bool array, bool multisample, @@ -362,16 +359,10 @@ std::string Image::str() const { return oss.str(); } -void Image::GetExtraHashWords(std::vector<uint32_t>* words, - std::unordered_set<const Type*>* seen) const { - sampled_type_->GetHashWords(words, seen); - words->push_back(dim_); - words->push_back(depth_); - words->push_back(arrayed_); - words->push_back(ms_); - words->push_back(sampled_); - words->push_back(format_); - words->push_back(access_qualifier_); +size_t Image::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const { + hash = hash_combine(hash, uint32_t(dim_), depth_, arrayed_, ms_, sampled_, + uint32_t(format_), uint32_t(access_qualifier_)); + return sampled_type_->ComputeHashValue(hash, seen); } bool SampledImage::IsSameImpl(const Type* that, IsSameCache* seen) const { @@ -387,9 +378,8 @@ std::string SampledImage::str() const { return oss.str(); } -void SampledImage::GetExtraHashWords( - std::vector<uint32_t>* words, std::unordered_set<const Type*>* seen) const { - image_type_->GetHashWords(words, seen); +size_t SampledImage::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const { + return image_type_->ComputeHashValue(hash, seen); } Array::Array(const Type* type, const Array::LengthInfo& length_info_arg) @@ -422,16 +412,19 @@ std::string Array::str() const { return oss.str(); } -void Array::GetExtraHashWords(std::vector<uint32_t>* words, - std::unordered_set<const Type*>* seen) const { - element_type_->GetHashWords(words, seen); - // This should mirror the logic in IsSameImpl - words->insert(words->end(), length_info_.words.begin(), - length_info_.words.end()); +size_t Array::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const { + hash = hash_combine(hash, length_info_.words); + return element_type_->ComputeHashValue(hash, seen); } void Array::ReplaceElementType(const Type* type) { element_type_ = type; } +Array::LengthInfo Array::GetConstantLengthInfo(uint32_t const_id, + uint32_t length) const { + std::vector<uint32_t> extra_words{LengthInfo::Case::kConstant, length}; + return {const_id, extra_words}; +} + RuntimeArray::RuntimeArray(const Type* type) : Type(kRuntimeArray), element_type_(type) { assert(!type->AsVoid()); @@ -450,9 +443,8 @@ std::string RuntimeArray::str() const { return oss.str(); } -void RuntimeArray::GetExtraHashWords( - std::vector<uint32_t>* words, std::unordered_set<const Type*>* seen) const { - element_type_->GetHashWords(words, seen); +size_t RuntimeArray::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const { + return element_type_->ComputeHashValue(hash, seen); } void RuntimeArray::ReplaceElementType(const Type* type) { @@ -509,19 +501,14 @@ std::string Struct::str() const { return oss.str(); } -void Struct::GetExtraHashWords(std::vector<uint32_t>* words, - std::unordered_set<const Type*>* seen) const { +size_t Struct::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const { for (auto* t : element_types_) { - t->GetHashWords(words, seen); + hash = t->ComputeHashValue(hash, seen); } for (const auto& pair : element_decorations_) { - words->push_back(pair.first); - for (const auto& d : pair.second) { - for (auto w : d) { - words->push_back(w); - } - } + hash = hash_combine(hash, pair.first, pair.second); } + return hash; } bool Opaque::IsSameImpl(const Type* that, IsSameCache*) const { @@ -536,11 +523,8 @@ std::string Opaque::str() const { return oss.str(); } -void Opaque::GetExtraHashWords(std::vector<uint32_t>* words, - std::unordered_set<const Type*>*) const { - for (auto c : name_) { - words->push_back(static_cast<char32_t>(c)); - } +size_t Opaque::ComputeExtraStateHash(size_t hash, SeenTypes*) const { + return hash_combine(hash, name_); } Pointer::Pointer(const Type* type, SpvStorageClass sc) @@ -569,10 +553,9 @@ std::string Pointer::str() const { return os.str(); } -void Pointer::GetExtraHashWords(std::vector<uint32_t>* words, - std::unordered_set<const Type*>* seen) const { - pointee_type_->GetHashWords(words, seen); - words->push_back(storage_class_); +size_t Pointer::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const { + hash = hash_combine(hash, uint32_t(storage_class_)); + return pointee_type_->ComputeHashValue(hash, seen); } void Pointer::SetPointeeType(const Type* type) { pointee_type_ = type; } @@ -606,12 +589,11 @@ std::string Function::str() const { return oss.str(); } -void Function::GetExtraHashWords(std::vector<uint32_t>* words, - std::unordered_set<const Type*>* seen) const { - return_type_->GetHashWords(words, seen); +size_t Function::ComputeExtraStateHash(size_t hash, SeenTypes* seen) const { for (const auto* t : param_types_) { - t->GetHashWords(words, seen); + hash = t->ComputeHashValue(hash, seen); } + return return_type_->ComputeHashValue(hash, seen); } void Function::SetReturnType(const Type* type) { return_type_ = type; } @@ -628,9 +610,8 @@ std::string Pipe::str() const { return oss.str(); } -void Pipe::GetExtraHashWords(std::vector<uint32_t>* words, - std::unordered_set<const Type*>*) const { - words->push_back(access_qualifier_); +size_t Pipe::ComputeExtraStateHash(size_t hash, SeenTypes*) const { + return hash_combine(hash, uint32_t(access_qualifier_)); } bool ForwardPointer::IsSameImpl(const Type* that, IsSameCache*) const { @@ -653,11 +634,11 @@ std::string ForwardPointer::str() const { return oss.str(); } -void ForwardPointer::GetExtraHashWords( - std::vector<uint32_t>* words, std::unordered_set<const Type*>* seen) const { - words->push_back(target_id_); - words->push_back(storage_class_); - if (pointer_) pointer_->GetHashWords(words, seen); +size_t ForwardPointer::ComputeExtraStateHash(size_t hash, + SeenTypes* seen) const { + hash = hash_combine(hash, target_id_, uint32_t(storage_class_)); + if (pointer_) hash = pointer_->ComputeHashValue(hash, seen); + return hash; } CooperativeMatrixNV::CooperativeMatrixNV(const Type* type, const uint32_t scope, @@ -681,12 +662,10 @@ std::string CooperativeMatrixNV::str() const { return oss.str(); } -void CooperativeMatrixNV::GetExtraHashWords( - std::vector<uint32_t>* words, std::unordered_set<const Type*>* pSet) const { - component_type_->GetHashWords(words, pSet); - words->push_back(scope_id_); - words->push_back(rows_id_); - words->push_back(columns_id_); +size_t CooperativeMatrixNV::ComputeExtraStateHash(size_t hash, + SeenTypes* seen) const { + hash = hash_combine(hash, scope_id_, rows_id_, columns_id_); + return component_type_->ComputeHashValue(hash, seen); } bool CooperativeMatrixNV::IsSameImpl(const Type* that, diff --git a/source/opt/types.h b/source/opt/types.h index 9ecd41a6..f5a4a6be 100644 --- a/source/opt/types.h +++ b/source/opt/types.h @@ -28,6 +28,7 @@ #include "source/latest_version_spirv_header.h" #include "source/opt/instruction.h" +#include "source/util/small_vector.h" #include "spirv-tools/libspirv.h" namespace spvtools { @@ -67,6 +68,8 @@ class Type { public: typedef std::set<std::pair<const Pointer*, const Pointer*>> IsSameCache; + using SeenTypes = spvtools::utils::SmallVector<const Type*, 8>; + // Available subtypes. // // When adding a new derived class of Type, please add an entry to the enum. @@ -96,7 +99,8 @@ class Type { kNamedBarrier, kAccelerationStructureNV, kCooperativeMatrixNV, - kRayQueryKHR + kRayQueryKHR, + kLast }; Type(Kind k) : kind_(k) {} @@ -154,21 +158,7 @@ class Type { // Returns the hash value of this type. size_t HashValue() const; - // Adds the necessary words to compute a hash value of this type to |words|. - void GetHashWords(std::vector<uint32_t>* words) const { - std::unordered_set<const Type*> seen; - GetHashWords(words, &seen); - } - - // Adds the necessary words to compute a hash value of this type to |words|. - void GetHashWords(std::vector<uint32_t>* words, - std::unordered_set<const Type*>* seen) const; - - // Adds necessary extra words for a subtype to calculate a hash value into - // |words|. - virtual void GetExtraHashWords( - std::vector<uint32_t>* words, - std::unordered_set<const Type*>* pSet) const = 0; + size_t ComputeHashValue(size_t hash, SeenTypes* seen) const; // A bunch of methods for casting this type to a given type. Returns this if the // cast can be done, nullptr otherwise. @@ -204,6 +194,10 @@ class Type { DeclareCastMethod(RayQueryKHR) #undef DeclareCastMethod +protected: + // Add any type-specific state to |hash| and returns new hash. + virtual size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const = 0; + protected: // Decorations attached to this type. Each decoration is encoded as a vector // of uint32_t numbers. The first uint32_t number is the decoration value, @@ -232,8 +226,7 @@ class Integer : public Type { uint32_t width() const { return width_; } bool IsSigned() const { return signed_; } - void GetExtraHashWords(std::vector<uint32_t>* words, - std::unordered_set<const Type*>* pSet) const override; + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; private: bool IsSameImpl(const Type* that, IsSameCache*) const override; @@ -253,8 +246,7 @@ class Float : public Type { const Float* AsFloat() const override { return this; } uint32_t width() const { return width_; } - void GetExtraHashWords(std::vector<uint32_t>* words, - std::unordered_set<const Type*>* pSet) const override; + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; private: bool IsSameImpl(const Type* that, IsSameCache*) const override; @@ -274,8 +266,7 @@ class Vector : public Type { Vector* AsVector() override { return this; } const Vector* AsVector() const override { return this; } - void GetExtraHashWords(std::vector<uint32_t>* words, - std::unordered_set<const Type*>* pSet) const override; + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; private: bool IsSameImpl(const Type* that, IsSameCache*) const override; @@ -296,8 +287,7 @@ class Matrix : public Type { Matrix* AsMatrix() override { return this; } const Matrix* AsMatrix() const override { return this; } - void GetExtraHashWords(std::vector<uint32_t>* words, - std::unordered_set<const Type*>* pSet) const override; + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; private: bool IsSameImpl(const Type* that, IsSameCache*) const override; @@ -327,8 +317,7 @@ class Image : public Type { SpvImageFormat format() const { return format_; } SpvAccessQualifier access_qualifier() const { return access_qualifier_; } - void GetExtraHashWords(std::vector<uint32_t>* words, - std::unordered_set<const Type*>* pSet) const override; + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; private: bool IsSameImpl(const Type* that, IsSameCache*) const override; @@ -355,8 +344,7 @@ class SampledImage : public Type { const Type* image_type() const { return image_type_; } - void GetExtraHashWords(std::vector<uint32_t>* words, - std::unordered_set<const Type*>* pSet) const override; + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; private: bool IsSameImpl(const Type* that, IsSameCache*) const override; @@ -399,10 +387,10 @@ class Array : public Type { Array* AsArray() override { return this; } const Array* AsArray() const override { return this; } - void GetExtraHashWords(std::vector<uint32_t>* words, - std::unordered_set<const Type*>* pSet) const override; + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; void ReplaceElementType(const Type* element_type); + LengthInfo GetConstantLengthInfo(uint32_t const_id, uint32_t length) const; private: bool IsSameImpl(const Type* that, IsSameCache*) const override; @@ -422,8 +410,7 @@ class RuntimeArray : public Type { RuntimeArray* AsRuntimeArray() override { return this; } const RuntimeArray* AsRuntimeArray() const override { return this; } - void GetExtraHashWords(std::vector<uint32_t>* words, - std::unordered_set<const Type*>* pSet) const override; + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; void ReplaceElementType(const Type* element_type); @@ -459,8 +446,7 @@ class Struct : public Type { Struct* AsStruct() override { return this; } const Struct* AsStruct() const override { return this; } - void GetExtraHashWords(std::vector<uint32_t>* words, - std::unordered_set<const Type*>* pSet) const override; + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; private: bool IsSameImpl(const Type* that, IsSameCache*) const override; @@ -491,8 +477,7 @@ class Opaque : public Type { const std::string& name() const { return name_; } - void GetExtraHashWords(std::vector<uint32_t>* words, - std::unordered_set<const Type*>* pSet) const override; + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; private: bool IsSameImpl(const Type* that, IsSameCache*) const override; @@ -512,8 +497,7 @@ class Pointer : public Type { Pointer* AsPointer() override { return this; } const Pointer* AsPointer() const override { return this; } - void GetExtraHashWords(std::vector<uint32_t>* words, - std::unordered_set<const Type*>* pSet) const override; + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; void SetPointeeType(const Type* type); @@ -539,8 +523,7 @@ class Function : public Type { const std::vector<const Type*>& param_types() const { return param_types_; } std::vector<const Type*>& param_types() { return param_types_; } - void GetExtraHashWords(std::vector<uint32_t>* words, - std::unordered_set<const Type*>*) const override; + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; void SetReturnType(const Type* type); @@ -564,8 +547,7 @@ class Pipe : public Type { SpvAccessQualifier access_qualifier() const { return access_qualifier_; } - void GetExtraHashWords(std::vector<uint32_t>* words, - std::unordered_set<const Type*>* pSet) const override; + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; private: bool IsSameImpl(const Type* that, IsSameCache*) const override; @@ -592,8 +574,7 @@ class ForwardPointer : public Type { ForwardPointer* AsForwardPointer() override { return this; } const ForwardPointer* AsForwardPointer() const override { return this; } - void GetExtraHashWords(std::vector<uint32_t>* words, - std::unordered_set<const Type*>* pSet) const override; + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; private: bool IsSameImpl(const Type* that, IsSameCache*) const override; @@ -616,8 +597,7 @@ class CooperativeMatrixNV : public Type { return this; } - void GetExtraHashWords(std::vector<uint32_t>*, - std::unordered_set<const Type*>*) const override; + size_t ComputeExtraStateHash(size_t hash, SeenTypes* seen) const override; const Type* component_type() const { return component_type_; } uint32_t scope_id() const { return scope_id_; } @@ -633,24 +613,25 @@ class CooperativeMatrixNV : public Type { const uint32_t columns_id_; }; -#define DefineParameterlessType(type, name) \ - class type : public Type { \ - public: \ - type() : Type(k##type) {} \ - type(const type&) = default; \ - \ - std::string str() const override { return #name; } \ - \ - type* As##type() override { return this; } \ - const type* As##type() const override { return this; } \ - \ - void GetExtraHashWords(std::vector<uint32_t>*, \ - std::unordered_set<const Type*>*) const override {} \ - \ - private: \ - bool IsSameImpl(const Type* that, IsSameCache*) const override { \ - return that->As##type() && HasSameDecorations(that); \ - } \ +#define DefineParameterlessType(type, name) \ + class type : public Type { \ + public: \ + type() : Type(k##type) {} \ + type(const type&) = default; \ + \ + std::string str() const override { return #name; } \ + \ + type* As##type() override { return this; } \ + const type* As##type() const override { return this; } \ + \ + size_t ComputeExtraStateHash(size_t hash, SeenTypes*) const override { \ + return hash; \ + } \ + \ + private: \ + bool IsSameImpl(const Type* that, IsSameCache*) const override { \ + return that->As##type() && HasSameDecorations(that); \ + } \ } DefineParameterlessType(Void, void); DefineParameterlessType(Bool, bool); diff --git a/source/opt/unify_const_pass.cpp b/source/opt/unify_const_pass.cpp index 227fd61d..6bfa11a5 100644 --- a/source/opt/unify_const_pass.cpp +++ b/source/opt/unify_const_pass.cpp @@ -151,7 +151,7 @@ Pass::Status UnifyConstantPass::Process() { // 'SpecId' decoration and all of them should be treated as unique. // 'SpecId' is not applicable to SpecConstants defined with // OpSpecConstant{Op|Composite}, their values are not necessary to be - // unique. When all the operands/compoents are the same between two + // unique. When all the operands/components are the same between two // OpSpecConstant{Op|Composite} results, their result values must be the // same so are unifiable. case SpvOp::SpvOpSpecConstantOp: diff --git a/source/opt/upgrade_memory_model.cpp b/source/opt/upgrade_memory_model.cpp index ab252059..9d6a5bce 100644 --- a/source/opt/upgrade_memory_model.cpp +++ b/source/opt/upgrade_memory_model.cpp @@ -20,6 +20,7 @@ #include "source/opt/ir_context.h" #include "source/spirv_constant.h" #include "source/util/make_unique.h" +#include "source/util/string_utils.h" namespace spvtools { namespace opt { @@ -58,9 +59,7 @@ void UpgradeMemoryModel::UpgradeMemoryModelInstruction() { std::initializer_list<Operand>{ {SPV_OPERAND_TYPE_CAPABILITY, {SpvCapabilityVulkanMemoryModelKHR}}})); const std::string extension = "SPV_KHR_vulkan_memory_model"; - std::vector<uint32_t> words(extension.size() / 4 + 1, 0); - char* dst = reinterpret_cast<char*>(words.data()); - strncpy(dst, extension.c_str(), extension.size()); + std::vector<uint32_t> words = spvtools::utils::MakeVector(extension); context()->AddExtension( MakeUnique<Instruction>(context(), SpvOpExtension, 0, 0, std::initializer_list<Operand>{ @@ -85,8 +84,7 @@ void UpgradeMemoryModel::UpgradeInstructions() { if (ext_inst == GLSLstd450Modf || ext_inst == GLSLstd450Frexp) { auto import = get_def_use_mgr()->GetDef(inst->GetSingleWordInOperand(0u)); - if (reinterpret_cast<char*>(import->GetInOperand(0u).words.data()) == - std::string("GLSL.std.450")) { + if (import->GetInOperand(0u).AsString() == "GLSL.std.450") { UpgradeExtInst(inst); } } diff --git a/source/opt/vector_dce.h b/source/opt/vector_dce.h index 4d30b926..a55bda69 100644 --- a/source/opt/vector_dce.h +++ b/source/opt/vector_dce.h @@ -73,7 +73,7 @@ class VectorDCE : public MemPass { bool RewriteInstructions(Function* function, const LiveComponentMap& live_components); - // Makrs all DebugValue instructions that use |composite| for their values as + // Makes all DebugValue instructions that use |composite| for their values as // dead instructions by putting them into |dead_dbg_value|. void MarkDebugValueUsesAsDead(Instruction* composite, std::vector<Instruction*>* dead_dbg_value); diff --git a/source/parsed_operand.cpp b/source/parsed_operand.cpp index 7ad369cd..5f8e94db 100644 --- a/source/parsed_operand.cpp +++ b/source/parsed_operand.cpp @@ -24,7 +24,9 @@ namespace spvtools { void EmitNumericLiteral(std::ostream* out, const spv_parsed_instruction_t& inst, const spv_parsed_operand_t& operand) { if (operand.type != SPV_OPERAND_TYPE_LITERAL_INTEGER && - operand.type != SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER) + operand.type != SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER && + operand.type != SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER && + operand.type != SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER) return; if (operand.num_words < 1) return; // TODO(dneto): Support more than 64-bits at a time. diff --git a/source/reduce/remove_struct_member_reduction_opportunity.cpp b/source/reduce/remove_struct_member_reduction_opportunity.cpp index da096e1e..e72ed351 100644 --- a/source/reduce/remove_struct_member_reduction_opportunity.cpp +++ b/source/reduce/remove_struct_member_reduction_opportunity.cpp @@ -153,7 +153,7 @@ void RemoveStructMemberReductionOpportunity::AdjustAccessedIndices( next_type = type_inst->GetSingleWordInOperand(0); break; case SpvOpTypeStruct: { - // Struct types are special becuase (a) we may need to adjust the index + // Struct types are special because (a) we may need to adjust the index // being used, if the struct type is the one from which we are removing // a member, and (b) the type encountered by following the current index // is dependent on the value of the index. diff --git a/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.cpp b/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.cpp index e72be625..cd0c4e4d 100644 --- a/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.cpp +++ b/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.cpp @@ -136,9 +136,9 @@ RemoveUnusedStructMemberReductionOpportunityFinder::GetAvailableOpportunities( } } - // We now know those struct indices that are unsed, and we make a reduction + // We now know those struct indices that are unused, and we make a reduction // opportunity for each of them. By mapping each relevant member index to the - // structs in which it is unsed, we will group all opportunities to remove + // structs in which it is unused, we will group all opportunities to remove // member k of a struct (for some k) together. This reduces the likelihood // that opportunities to remove members from the same struct will be adjacent, // which is good because such opportunities mutually disable one another. diff --git a/source/reduce/structured_construct_to_block_reduction_opportunity_finder.cpp b/source/reduce/structured_construct_to_block_reduction_opportunity_finder.cpp index dc20f689..29fbe551 100644 --- a/source/reduce/structured_construct_to_block_reduction_opportunity_finder.cpp +++ b/source/reduce/structured_construct_to_block_reduction_opportunity_finder.cpp @@ -96,7 +96,7 @@ StructuredConstructToBlockReductionOpportunityFinder::GetAvailableOpportunities( // This also means that we don't add a region. continue; } - // We have a reachable header block with a rechable merge that + // We have a reachable header block with a reachable merge that // postdominates the header: this means we have a new region. regions.emplace(&block, std::unordered_set<opt::BasicBlock*>()); } @@ -128,7 +128,7 @@ bool StructuredConstructToBlockReductionOpportunityFinder:: if (!block->WhileEachInst( [context, &header, ®ion](opt::Instruction* inst) -> bool { if (inst->result_id() == 0) { - // The instruction does not genreate a result id, thus it cannot + // The instruction does not generate a result id, thus it cannot // be referred to outside the region - this is fine. return true; } diff --git a/source/spirv_definition.h b/source/spirv_definition.h index 63a4ef0d..5dbd6ab2 100644 --- a/source/spirv_definition.h +++ b/source/spirv_definition.h @@ -27,7 +27,7 @@ typedef struct spv_header_t { uint32_t generator; uint32_t bound; uint32_t schema; // NOTE: Reserved - const uint32_t* instructions; // NOTE: Unfixed pointer to instruciton stream + const uint32_t* instructions; // NOTE: Unfixed pointer to instruction stream } spv_header_t; #endif // SOURCE_SPIRV_DEFINITION_H_ diff --git a/source/spirv_endian.h b/source/spirv_endian.h index c2540bec..b4927f31 100644 --- a/source/spirv_endian.h +++ b/source/spirv_endian.h @@ -31,7 +31,7 @@ uint64_t spvFixDoubleWord(const uint32_t low, const uint32_t high, spv_result_t spvBinaryEndianness(const spv_const_binary binary, spv_endianness_t* endian); -// Returns true if the given endianness matches the host's native endiannes. +// Returns true if the given endianness matches the host's native endianness. bool spvIsHostEndian(spv_endianness_t endian); #endif // SOURCE_SPIRV_ENDIAN_H_ diff --git a/source/spirv_target_env.cpp b/source/spirv_target_env.cpp index 187ab61e..9a038174 100644 --- a/source/spirv_target_env.cpp +++ b/source/spirv_target_env.cpp @@ -72,6 +72,10 @@ const char* spvTargetEnvDescription(spv_target_env env) { return "SPIR-V 1.5"; case SPV_ENV_VULKAN_1_2: return "SPIR-V 1.5 (under Vulkan 1.2 semantics)"; + case SPV_ENV_UNIVERSAL_1_6: + return "SPIR-V 1.6"; + case SPV_ENV_VULKAN_1_3: + return "SPIR-V 1.6 (under Vulkan 1.3 semantics)"; case SPV_ENV_MAX: assert(false && "Invalid target environment value."); break; @@ -113,6 +117,9 @@ uint32_t spvVersionForTargetEnv(spv_target_env env) { case SPV_ENV_UNIVERSAL_1_5: case SPV_ENV_VULKAN_1_2: return SPV_SPIRV_VERSION_WORD(1, 5); + case SPV_ENV_UNIVERSAL_1_6: + case SPV_ENV_VULKAN_1_3: + return SPV_SPIRV_VERSION_WORD(1, 6); case SPV_ENV_MAX: assert(false && "Invalid target environment value."); break; @@ -125,12 +132,14 @@ static const std::pair<const char*, spv_target_env> spvTargetEnvNameMap[] = { {"vulkan1.0", SPV_ENV_VULKAN_1_0}, {"vulkan1.1", SPV_ENV_VULKAN_1_1}, {"vulkan1.2", SPV_ENV_VULKAN_1_2}, + {"vulkan1.3", SPV_ENV_VULKAN_1_3}, {"spv1.0", SPV_ENV_UNIVERSAL_1_0}, {"spv1.1", SPV_ENV_UNIVERSAL_1_1}, {"spv1.2", SPV_ENV_UNIVERSAL_1_2}, {"spv1.3", SPV_ENV_UNIVERSAL_1_3}, {"spv1.4", SPV_ENV_UNIVERSAL_1_4}, {"spv1.5", SPV_ENV_UNIVERSAL_1_5}, + {"spv1.6", SPV_ENV_UNIVERSAL_1_6}, {"opencl1.2embedded", SPV_ENV_OPENCL_EMBEDDED_1_2}, {"opencl1.2", SPV_ENV_OPENCL_1_2}, {"opencl2.0embedded", SPV_ENV_OPENCL_EMBEDDED_2_0}, @@ -177,7 +186,8 @@ static const VulkanEnv ordered_vulkan_envs[] = { {SPV_ENV_VULKAN_1_0, VULKAN_VER(1, 0), SPIRV_VER(1, 0)}, {SPV_ENV_VULKAN_1_1, VULKAN_VER(1, 1), SPIRV_VER(1, 3)}, {SPV_ENV_VULKAN_1_1_SPIRV_1_4, VULKAN_VER(1, 1), SPIRV_VER(1, 4)}, - {SPV_ENV_VULKAN_1_2, VULKAN_VER(1, 2), SPIRV_VER(1, 5)}}; + {SPV_ENV_VULKAN_1_2, VULKAN_VER(1, 2), SPIRV_VER(1, 5)}, + {SPV_ENV_VULKAN_1_3, VULKAN_VER(1, 3), SPIRV_VER(1, 6)}}; bool spvParseVulkanEnv(uint32_t vulkan_ver, uint32_t spirv_ver, spv_target_env* env) { @@ -211,11 +221,13 @@ bool spvIsVulkanEnv(spv_target_env env) { case SPV_ENV_UNIVERSAL_1_3: case SPV_ENV_UNIVERSAL_1_4: case SPV_ENV_UNIVERSAL_1_5: + case SPV_ENV_UNIVERSAL_1_6: return false; case SPV_ENV_VULKAN_1_0: case SPV_ENV_VULKAN_1_1: case SPV_ENV_VULKAN_1_1_SPIRV_1_4: case SPV_ENV_VULKAN_1_2: + case SPV_ENV_VULKAN_1_3: return true; case SPV_ENV_WEBGPU_0: assert(false && "Deprecated target environment value."); @@ -244,6 +256,8 @@ bool spvIsOpenCLEnv(spv_target_env env) { case SPV_ENV_VULKAN_1_1_SPIRV_1_4: case SPV_ENV_UNIVERSAL_1_5: case SPV_ENV_VULKAN_1_2: + case SPV_ENV_UNIVERSAL_1_6: + case SPV_ENV_VULKAN_1_3: return false; case SPV_ENV_OPENCL_1_2: case SPV_ENV_OPENCL_EMBEDDED_1_2: @@ -284,6 +298,8 @@ bool spvIsOpenGLEnv(spv_target_env env) { case SPV_ENV_VULKAN_1_1_SPIRV_1_4: case SPV_ENV_UNIVERSAL_1_5: case SPV_ENV_VULKAN_1_2: + case SPV_ENV_UNIVERSAL_1_6: + case SPV_ENV_VULKAN_1_3: return false; case SPV_ENV_OPENGL_4_0: case SPV_ENV_OPENGL_4_1: @@ -321,6 +337,8 @@ bool spvIsValidEnv(spv_target_env env) { case SPV_ENV_VULKAN_1_1_SPIRV_1_4: case SPV_ENV_UNIVERSAL_1_5: case SPV_ENV_VULKAN_1_2: + case SPV_ENV_UNIVERSAL_1_6: + case SPV_ENV_VULKAN_1_3: case SPV_ENV_OPENGL_4_0: case SPV_ENV_OPENGL_4_1: case SPV_ENV_OPENGL_4_2: @@ -355,16 +373,18 @@ std::string spvLogStringForEnv(spv_target_env env) { } case SPV_ENV_VULKAN_1_0: case SPV_ENV_VULKAN_1_1: - case SPV_ENV_VULKAN_1_1_SPIRV_1_4: { - case SPV_ENV_VULKAN_1_2: - return "Vulkan"; + case SPV_ENV_VULKAN_1_1_SPIRV_1_4: + case SPV_ENV_VULKAN_1_2: + case SPV_ENV_VULKAN_1_3: { + return "Vulkan"; } case SPV_ENV_UNIVERSAL_1_0: case SPV_ENV_UNIVERSAL_1_1: case SPV_ENV_UNIVERSAL_1_2: case SPV_ENV_UNIVERSAL_1_3: case SPV_ENV_UNIVERSAL_1_4: - case SPV_ENV_UNIVERSAL_1_5: { + case SPV_ENV_UNIVERSAL_1_5: + case SPV_ENV_UNIVERSAL_1_6: { return "Universal"; } case SPV_ENV_WEBGPU_0: diff --git a/source/spirv_target_env.h b/source/spirv_target_env.h index cc06deca..f3b0c2f6 100644 --- a/source/spirv_target_env.h +++ b/source/spirv_target_env.h @@ -40,7 +40,7 @@ std::string spvLogStringForEnv(spv_target_env env); // Returns a formatted list of all SPIR-V target environment names that // can be parsed by spvParseTargetEnv. -// |pad| is the number of space characters that the begining of each line +// |pad| is the number of space characters that the beginning of each line // except the first one will be padded with. // |wrap| is the max length of lines the user desires. Word-wrapping will // occur to satisfy this limit. diff --git a/source/table.cpp b/source/table.cpp index d4a2d7e9..822cefeb 100644 --- a/source/table.cpp +++ b/source/table.cpp @@ -41,6 +41,8 @@ spv_context spvContextCreate(spv_target_env env) { case SPV_ENV_UNIVERSAL_1_4: case SPV_ENV_UNIVERSAL_1_5: case SPV_ENV_VULKAN_1_2: + case SPV_ENV_UNIVERSAL_1_6: + case SPV_ENV_VULKAN_1_3: break; default: return nullptr; diff --git a/source/text.cpp b/source/text.cpp index 88a8e8ff..90f69c52 100644 --- a/source/text.cpp +++ b/source/text.cpp @@ -403,9 +403,10 @@ spv_result_t spvTextEncodeOperand(const spvtools::AssemblyGrammar& grammar, case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS: case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS: { uint32_t value; - if (grammar.parseMaskOperand(type, textValue, &value)) { - return context->diagnostic() << "Invalid " << spvOperandTypeStr(type) - << " operand '" << textValue << "'."; + if (auto error = grammar.parseMaskOperand(type, textValue, &value)) { + return context->diagnostic(error) + << "Invalid " << spvOperandTypeStr(type) << " operand '" + << textValue << "'."; } if (auto error = context->binaryEncodeU32(value, pInst)) return error; // Prepare to parse the operands for this logical operand. @@ -622,7 +623,8 @@ spv_result_t spvTextEncodeOpcode(const spvtools::AssemblyGrammar& grammar, break; } else { return context->diagnostic() - << "Expected operand, found end of stream."; + << "Expected operand for " << opcodeName + << " instruction, but found the end of the stream."; } } assert(error == SPV_SUCCESS && "Somebody added another way to fail"); @@ -632,7 +634,8 @@ spv_result_t spvTextEncodeOpcode(const spvtools::AssemblyGrammar& grammar, break; } else { return context->diagnostic() - << "Expected operand, found next instruction instead."; + << "Expected operand for " << opcodeName + << " instruction, but found the next instruction instead."; } } @@ -666,7 +669,7 @@ spv_result_t spvTextEncodeOpcode(const spvtools::AssemblyGrammar& grammar, if (pInst->words.size() > SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX) { return context->diagnostic() - << "Instruction too long: " << pInst->words.size() + << opcodeName << " Instruction too long: " << pInst->words.size() << " words, but the limit is " << SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX; } @@ -715,6 +718,12 @@ spv_result_t GetNumericIds(const spvtools::AssemblyGrammar& grammar, while (context.hasText()) { spv_instruction_t inst; + // Operand parsing sometimes involves knowing the opcode of the instruction + // being parsed. A malformed input might feature such an operand *before* + // the opcode is known. To guard against accessing an uninitialized opcode, + // the instruction's opcode is initialized to a default value. + inst.opcode = SpvOpMax; + if (spvTextEncodeOpcode(grammar, &context, &inst)) { return SPV_ERROR_INVALID_TEXT; } @@ -763,8 +772,8 @@ spv_result_t spvTextToBinaryInternal(const spvtools::AssemblyGrammar& grammar, instructions.push_back({}); spv_instruction_t& inst = instructions.back(); - if (spvTextEncodeOpcode(grammar, &context, &inst)) { - return SPV_ERROR_INVALID_TEXT; + if (auto error = spvTextEncodeOpcode(grammar, &context, &inst)) { + return error; } if (context.advance()) break; diff --git a/source/text_handler.cpp b/source/text_handler.cpp index 46b98456..fe12a26e 100644 --- a/source/text_handler.cpp +++ b/source/text_handler.cpp @@ -29,6 +29,7 @@ #include "source/util/bitutils.h" #include "source/util/hex_float.h" #include "source/util/parse_number.h" +#include "source/util/string_utils.h" namespace spvtools { namespace { @@ -307,14 +308,8 @@ spv_result_t AssemblyContext::binaryEncodeString(const char* value, << SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX << " words."; } - pInst->words.resize(newWordCount); - - // Make sure all the bytes in the last word are 0, in case we only - // write a partial word at the end. - pInst->words.back() = 0; - - char* dest = (char*)&pInst->words[oldWordCount]; - strncpy(dest, value, length + 1); + pInst->words.reserve(newWordCount); + spvtools::utils::AppendToVector(value, &pInst->words); return SPV_SUCCESS; } diff --git a/source/util/bit_vector.h b/source/util/bit_vector.h index 3e189cb1..826d62f0 100644 --- a/source/util/bit_vector.h +++ b/source/util/bit_vector.h @@ -32,7 +32,7 @@ class BitVector { enum { kInitialNumBits = 1024 }; public: - // Creates a bit vector contianing 0s. + // Creates a bit vector containing 0s. BitVector(uint32_t reserved_size = kInitialNumBits) : bits_((reserved_size - 1) / kBitContainerSize + 1, 0) {} diff --git a/source/util/hash_combine.h b/source/util/hash_combine.h new file mode 100644 index 00000000..1a2dbc33 --- /dev/null +++ b/source/util/hash_combine.h @@ -0,0 +1,53 @@ +// Copyright (c) 2022 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_UTIL_HASH_COMBINE_H_ +#define SOURCE_UTIL_HASH_COMBINE_H_ + +#include <cstddef> +#include <functional> +#include <vector> + +namespace spvtools { +namespace utils { + +// Helpers for incrementally computing hashes. +// For reference, see +// http://open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3876.pdf + +template <typename T> +inline size_t hash_combine(std::size_t seed, const T& val) { + return seed ^ (std::hash<T>()(val) + 0x9e3779b9 + (seed << 6) + (seed >> 2)); +} + +template <typename T> +inline size_t hash_combine(std::size_t hash, const std::vector<T>& vals) { + for (const T& val : vals) { + hash = hash_combine(hash, val); + } + return hash; +} + +inline size_t hash_combine(std::size_t hash) { return hash; } + +template <typename T, typename... Types> +inline size_t hash_combine(std::size_t hash, const T& val, + const Types&... args) { + return hash_combine(hash_combine(hash, val), args...); +} + +} // namespace utils +} // namespace spvtools + +#endif // SOURCE_UTIL_HASH_COMBINE_H_ diff --git a/source/util/hex_float.h b/source/util/hex_float.h index be28eae3..903b6288 100644 --- a/source/util/hex_float.h +++ b/source/util/hex_float.h @@ -199,7 +199,7 @@ bool operator==(const FloatProxy<T>& first, const FloatProxy<T>& second) { // Reads a FloatProxy value as a normal float from a stream. template <typename T> std::istream& operator>>(std::istream& is, FloatProxy<T>& value) { - T float_val; + T float_val = static_cast<T>(0.0); is >> float_val; value = FloatProxy<T>(float_val); return is; diff --git a/source/util/ilist.h b/source/util/ilist.h index 9837b09b..42d5e62b 100644 --- a/source/util/ilist.h +++ b/source/util/ilist.h @@ -59,7 +59,7 @@ class IntrusiveList { // Moves the contents of the given list to the list being constructed. IntrusiveList(IntrusiveList&&); - // Destorys the list. Note that the elements of the list will not be deleted, + // Destroys the list. Note that the elements of the list will not be deleted, // but they will be removed from the list. virtual ~IntrusiveList(); @@ -348,6 +348,7 @@ void IntrusiveList<NodeType>::Check(NodeType* start) { p = p->next_node_; } while (p != start); assert(sentinel_count == 1 && "List should have exactly 1 sentinel node."); + (void)sentinel_count; p = start; do { diff --git a/source/util/parse_number.h b/source/util/parse_number.h index 729aac54..d0f2a09a 100644 --- a/source/util/parse_number.h +++ b/source/util/parse_number.h @@ -220,7 +220,7 @@ EncodeNumberStatus ParseAndEncodeIntegerNumber( std::function<void(uint32_t)> emit, std::string* error_msg); // Parses a floating point value of a given |type| from the given |text| and -// encodes the number by the given |emit| funciton. On success, returns +// encodes the number by the given |emit| function. On success, returns // EncodeNumberStatus::kSuccess and the parsed number will be consumed by the // given |emit| function word by word (least significant word first). On // failure, this function returns the error code of the encoding status and diff --git a/source/util/pooled_linked_list.h b/source/util/pooled_linked_list.h new file mode 100644 index 00000000..faaa4c44 --- /dev/null +++ b/source/util/pooled_linked_list.h @@ -0,0 +1,236 @@ +// Copyright (c) 2021 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_UTIL_POOLED_LINKED_LIST_H_ +#define SOURCE_UTIL_POOLED_LINKED_LIST_H_ + +#include <cstdint> +#include <vector> + +namespace spvtools { +namespace utils { + +// Shared storage of nodes for PooledLinkedList. +template <typename T> +class PooledLinkedListNodes { + public: + struct Node { + Node(T e, int32_t n = -1) : element(e), next(n) {} + + T element = {}; + int32_t next = -1; + }; + + PooledLinkedListNodes() = default; + PooledLinkedListNodes(const PooledLinkedListNodes&) = delete; + PooledLinkedListNodes& operator=(const PooledLinkedListNodes&) = delete; + + PooledLinkedListNodes(PooledLinkedListNodes&& that) { + *this = std::move(that); + } + + PooledLinkedListNodes& operator=(PooledLinkedListNodes&& that) { + vec_ = std::move(that.vec_); + free_nodes_ = that.free_nodes_; + return *this; + } + + size_t total_nodes() { return vec_.size(); } + size_t free_nodes() { return free_nodes_; } + size_t used_nodes() { return total_nodes() - free_nodes(); } + + private: + template <typename ListT> + friend class PooledLinkedList; + + Node& at(int32_t index) { return vec_[index]; } + const Node& at(int32_t index) const { return vec_[index]; } + + int32_t insert(T element) { + int32_t index = int32_t(vec_.size()); + vec_.emplace_back(element); + return index; + } + + std::vector<Node> vec_; + size_t free_nodes_ = 0; +}; + +// Implements a linked-list where list nodes come from a shared pool. This is +// meant to be used in scenarios where it is desirable to avoid many small +// allocations. +// +// Instead of pointers, the list uses indices to allow the underlying storage +// to be modified without needing to modify the list. When removing elements +// from the list, nodes are not deleted or recycled: to reclaim unused space, +// perform a sequence of |move_nodes| operations into a temporary pool, which +// then is moved into the old pool. +// +// This does *not* attempt to implement a full stl-compatible interface. +template <typename T> +class PooledLinkedList { + public: + using NodePool = PooledLinkedListNodes<T>; + using Node = typename NodePool::Node; + + PooledLinkedList() = delete; + PooledLinkedList(NodePool* nodes) : nodes_(nodes) {} + + // Shared iterator implementation (for iterator and const_iterator). + template <typename ElementT, typename PoolT> + class iterator_base { + public: + iterator_base(const iterator_base& i) + : nodes_(i.nodes_), index_(i.index_) {} + + iterator_base& operator++() { + index_ = nodes_->at(index_).next; + return *this; + } + + iterator_base& operator=(const iterator_base& i) { + nodes_ = i.nodes_; + index_ = i.index_; + return *this; + } + + ElementT& operator*() const { return nodes_->at(index_).element; } + ElementT* operator->() const { return &nodes_->at(index_).element; } + + friend inline bool operator==(const iterator_base& lhs, + const iterator_base& rhs) { + return lhs.nodes_ == rhs.nodes_ && lhs.index_ == rhs.index_; + } + friend inline bool operator!=(const iterator_base& lhs, + const iterator_base& rhs) { + return lhs.nodes_ != rhs.nodes_ || lhs.index_ != rhs.index_; + } + + // Define standard iterator types needs so this class can be + // used with <algorithms>. + using iterator_category = std::forward_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = ElementT; + using pointer = ElementT*; + using const_pointer = const ElementT*; + using reference = ElementT&; + using const_reference = const ElementT&; + using size_type = size_t; + + private: + friend PooledLinkedList; + + iterator_base(PoolT* pool, int32_t index) : nodes_(pool), index_(index) {} + + PoolT* nodes_; + int32_t index_ = -1; + }; + + using iterator = iterator_base<T, std::vector<Node>>; + using const_iterator = iterator_base<const T, const std::vector<Node>>; + + bool empty() const { return head_ == -1; } + + T& front() { return nodes_->at(head_).element; } + T& back() { return nodes_->at(tail_).element; } + const T& front() const { return nodes_->at(head_).element; } + const T& back() const { return nodes_->at(tail_).element; } + + iterator begin() { return iterator(&nodes_->vec_, head_); } + iterator end() { return iterator(&nodes_->vec_, -1); } + const_iterator begin() const { return const_iterator(&nodes_->vec_, head_); } + const_iterator end() const { return const_iterator(&nodes_->vec_, -1); } + + // Inserts |element| at the back of the list. + void push_back(T element) { + int32_t new_tail = nodes_->insert(element); + if (head_ == -1) { + head_ = new_tail; + tail_ = new_tail; + } else { + nodes_->at(tail_).next = new_tail; + tail_ = new_tail; + } + } + + // Removes the first occurrence of |element| from the list. + // Returns if |element| was removed. + bool remove_first(T element) { + int32_t* prev_next = &head_; + for (int32_t prev_index = -1, index = head_; index != -1; /**/) { + auto& node = nodes_->at(index); + if (node.element == element) { + // Snip from of the list, optionally fixing up tail pointer. + if (tail_ == index) { + assert(node.next == -1); + tail_ = prev_index; + } + *prev_next = node.next; + nodes_->free_nodes_++; + return true; + } else { + prev_next = &node.next; + } + prev_index = index; + index = node.next; + } + return false; + } + + // Returns the PooledLinkedListNodes that owns this list's nodes. + NodePool* pool() { return nodes_; } + + // Moves the nodes in this list into |new_pool|, providing a way to compact + // storage and reclaim unused space. + // + // Upon completing a sequence of |move_nodes| calls, you must ensure you + // retain ownership of the new storage your lists point to. Example usage: + // + // unique_ptr<NodePool> new_pool = ...; + // for (PooledLinkedList& list : lists) { + // list.move_to(new_pool); + // } + // my_pool_ = std::move(new_pool); + void move_nodes(NodePool* new_pool) { + // Be sure to construct the list in the same order, instead of simply + // doing a sequence of push_backs. + int32_t prev_entry = -1; + int32_t nodes_freed = 0; + for (int32_t index = head_; index != -1; nodes_freed++) { + const auto& node = nodes_->at(index); + int32_t this_entry = new_pool->insert(node.element); + index = node.next; + if (prev_entry == -1) { + head_ = this_entry; + } else { + new_pool->at(prev_entry).next = this_entry; + } + prev_entry = this_entry; + } + tail_ = prev_entry; + // Update our old pool's free count, now we're a member of the new pool. + nodes_->free_nodes_ += nodes_freed; + nodes_ = new_pool; + } + + private: + NodePool* nodes_; + int32_t head_ = -1; + int32_t tail_ = -1; +}; + +} // namespace utils +} // namespace spvtools + +#endif // SOURCE_UTIL_POOLED_LINKED_LIST_H_
\ No newline at end of file diff --git a/source/util/small_vector.h b/source/util/small_vector.h index f2c1147b..648a3482 100644 --- a/source/util/small_vector.h +++ b/source/util/small_vector.h @@ -64,6 +64,11 @@ class SmallVector { } } + template <class InputIt> + SmallVector(InputIt first, InputIt last) : SmallVector() { + insert(end(), first, last); + } + SmallVector(std::vector<T>&& vec) : SmallVector() { if (vec.size() > small_size) { large_data_ = MakeUnique<std::vector<T>>(std::move(vec)); @@ -175,9 +180,12 @@ class SmallVector { return true; } +// Avoid infinite recursion from rewritten operators in C++20 +#if __cplusplus <= 201703L friend bool operator==(const std::vector<T>& lhs, const SmallVector& rhs) { return rhs == lhs; } +#endif friend bool operator!=(const SmallVector& lhs, const std::vector<T>& rhs) { return !(lhs == rhs); @@ -325,6 +333,15 @@ class SmallVector { ++size_; } + void pop_back() { + if (large_data_) { + large_data_->pop_back(); + } else { + --size_; + small_data_[size_].~T(); + } + } + template <class InputIt> iterator insert(iterator pos, InputIt first, InputIt last) { size_t element_idx = (pos - begin()); @@ -363,7 +380,7 @@ class SmallVector { } } - // Upate the size. + // Update the size. size_ += num_of_new_elements; return pos; } @@ -449,7 +466,7 @@ class SmallVector { T* small_data_; // The actual data used to store the array elements. It must never be used - // directly, but must only be accesed through |small_data_|. + // directly, but must only be accessed through |small_data_|. typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type buffer[small_size]; diff --git a/source/util/string_utils.h b/source/util/string_utils.h index 4282aa94..03e20b3d 100644 --- a/source/util/string_utils.h +++ b/source/util/string_utils.h @@ -16,6 +16,8 @@ #define SOURCE_UTIL_STRING_UTILS_H_ #include <assert.h> + +#include <cstring> #include <sstream> #include <string> #include <vector> @@ -44,9 +46,10 @@ std::string CardinalToOrdinal(size_t cardinal); // string will be empty. std::pair<std::string, std::string> SplitFlagArgs(const std::string& flag); -// Encodes a string as a sequence of words, using the SPIR-V encoding. -inline std::vector<uint32_t> MakeVector(std::string input) { - std::vector<uint32_t> result; +// Encodes a string as a sequence of words, using the SPIR-V encoding, appending +// to an existing vector. +inline void AppendToVector(const std::string& input, + std::vector<uint32_t>* result) { uint32_t word = 0; size_t num_bytes = input.size(); // SPIR-V strings are null-terminated. The byte_index == num_bytes @@ -56,24 +59,36 @@ inline std::vector<uint32_t> MakeVector(std::string input) { (byte_index < num_bytes ? uint8_t(input[byte_index]) : uint8_t(0)); word |= (new_byte << (8 * (byte_index % sizeof(uint32_t)))); if (3 == (byte_index % sizeof(uint32_t))) { - result.push_back(word); + result->push_back(word); word = 0; } } // Emit a trailing partial word. if ((num_bytes + 1) % sizeof(uint32_t)) { - result.push_back(word); + result->push_back(word); } +} + +// Encodes a string as a sequence of words, using the SPIR-V encoding. +inline std::vector<uint32_t> MakeVector(const std::string& input) { + std::vector<uint32_t> result; + AppendToVector(input, &result); return result; } -// Decode a string from a sequence of words, using the SPIR-V encoding. -template <class VectorType> -inline std::string MakeString(const VectorType& words) { +// Decode a string from a sequence of words between first and last, using the +// SPIR-V encoding. Assert that a terminating 0-byte was found (unless +// assert_found_terminating_null is passed as false). +template <class InputIt> +inline std::string MakeString(InputIt first, InputIt last, + bool assert_found_terminating_null = true) { std::string result; + constexpr size_t kCharsPerWord = sizeof(*first); + static_assert(kCharsPerWord == 4, "expect 4-byte word"); - for (uint32_t word : words) { - for (int byte_index = 0; byte_index < 4; byte_index++) { + for (InputIt pos = first; pos != last; ++pos) { + uint32_t word = *pos; + for (size_t byte_index = 0; byte_index < kCharsPerWord; byte_index++) { uint32_t extracted_word = (word >> (8 * byte_index)) & 0xFF; char c = static_cast<char>(extracted_word); if (c == 0) { @@ -82,9 +97,33 @@ inline std::string MakeString(const VectorType& words) { result += c; } } - assert(false && "Did not find terminating null for the string."); + assert(!assert_found_terminating_null && + "Did not find terminating null for the string."); + (void)assert_found_terminating_null; /* No unused parameters in release + builds. */ return result; -} // namespace utils +} + +// Decode a string from a sequence of words in a vector, using the SPIR-V +// encoding. +template <class VectorType> +inline std::string MakeString(const VectorType& words, + bool assert_found_terminating_null = true) { + return MakeString(words.cbegin(), words.cend(), + assert_found_terminating_null); +} + +// Decode a string from array words, consuming up to count words, using the +// SPIR-V encoding. +inline std::string MakeString(const uint32_t* words, size_t num_words, + bool assert_found_terminating_null = true) { + return MakeString(words, words + num_words, assert_found_terminating_null); +} + +// Check if str starts with prefix (only included since C++20) +inline bool starts_with(const std::string& str, const char* prefix) { + return 0 == str.compare(0, std::strlen(prefix), prefix); +} } // namespace utils } // namespace spvtools diff --git a/source/util/timer.h b/source/util/timer.h index fc4b747b..08083119 100644 --- a/source/util/timer.h +++ b/source/util/timer.h @@ -206,16 +206,16 @@ class Timer { // Variable to save the result of clock_gettime(CLOCK_PROCESS_CPUTIME_ID) when // Timer::Stop() is called. It is used as the last status of CPU time. The - // resouce usage is measured by subtracting |cpu_before_| from it. + // resource usage is measured by subtracting |cpu_before_| from it. timespec cpu_after_; // Variable to save the result of clock_gettime(CLOCK_MONOTONIC) when // Timer::Stop() is called. It is used as the last status of WALL time. The - // resouce usage is measured by subtracting |wall_before_| from it. + // resource usage is measured by subtracting |wall_before_| from it. timespec wall_after_; // Variable to save the result of getrusage() when Timer::Stop() is called. It - // is used as the last status of USR time, SYS time, and RSS. Those resouce + // is used as the last status of USR time, SYS time, and RSS. Those resource // usages are measured by subtracting |usage_before_| from it. rusage usage_after_; diff --git a/source/val/basic_block.h b/source/val/basic_block.h index 5af4b9e4..47cd06d0 100644 --- a/source/val/basic_block.h +++ b/source/val/basic_block.h @@ -84,26 +84,26 @@ class BasicBlock { type_.set(type); } - /// Sets the immedate dominator of this basic block + /// Sets the immediate dominator of this basic block /// /// @param[in] dom_block The dominator block void SetImmediateDominator(BasicBlock* dom_block); - /// Sets the immedate post dominator of this basic block + /// Sets the immediate post dominator of this basic block /// /// @param[in] pdom_block The post dominator block void SetImmediatePostDominator(BasicBlock* pdom_block); - /// Returns the immedate dominator of this basic block + /// Returns the immediate dominator of this basic block BasicBlock* immediate_dominator(); - /// Returns the immedate dominator of this basic block + /// Returns the immediate dominator of this basic block const BasicBlock* immediate_dominator() const; - /// Returns the immedate post dominator of this basic block + /// Returns the immediate post dominator of this basic block BasicBlock* immediate_post_dominator(); - /// Returns the immedate post dominator of this basic block + /// Returns the immediate post dominator of this basic block const BasicBlock* immediate_post_dominator() const; /// Returns the label instruction for the block, or nullptr if not set. diff --git a/source/val/function.cpp b/source/val/function.cpp index 9ad68e86..f3292b0e 100644 --- a/source/val/function.cpp +++ b/source/val/function.cpp @@ -57,7 +57,7 @@ spv_result_t Function::RegisterFunctionParameter(uint32_t parameter_id, uint32_t type_id) { assert(current_block_ == nullptr && "RegisterFunctionParameter can only be called when parsing the binary " - "ouside of a block"); + "outside of a block"); // TODO(umar): Validate function parameter type order and count // TODO(umar): Use these variables to validate parameter type (void)parameter_id; @@ -130,7 +130,7 @@ spv_result_t Function::RegisterBlock(uint32_t block_id, bool is_definition) { undefined_blocks_.erase(block_id); current_block_ = &inserted_block->second; ordered_blocks_.push_back(current_block_); - } else if (success) { // Block doesn't exsist but this is not a definition + } else if (success) { // Block doesn't exist but this is not a definition undefined_blocks_.insert(block_id); } diff --git a/source/val/function.h b/source/val/function.h index 400bb634..2fe30bdc 100644 --- a/source/val/function.h +++ b/source/val/function.h @@ -73,8 +73,8 @@ class Function { /// Registers a variable in the current block /// - /// @param[in] type_id The type ID of the varaible - /// @param[in] id The ID of the varaible + /// @param[in] type_id The type ID of the variable + /// @param[in] id The ID of the variable /// @param[in] storage The storage of the variable /// @param[in] init_id The initializer ID of the variable /// @@ -197,10 +197,10 @@ class Function { /// been identified and dominators have been computed. int GetBlockDepth(BasicBlock* bb); - /// Prints a GraphViz digraph of the CFG of the current funciton + /// Prints a GraphViz digraph of the CFG of the current function void PrintDotGraph() const; - /// Prints a directed graph of the CFG of the current funciton + /// Prints a directed graph of the CFG of the current function void PrintBlocks() const; /// Registers execution model limitation such as "Feature X is only available @@ -285,7 +285,7 @@ class Function { /// The type of the return value uint32_t result_type_id_; - /// The control fo the funciton + /// The control fo the function SpvFunctionControlMask function_control_; /// The type of declaration of each function diff --git a/source/val/instruction.cpp b/source/val/instruction.cpp index b9155898..f16fcd73 100644 --- a/source/val/instruction.cpp +++ b/source/val/instruction.cpp @@ -16,6 +16,9 @@ #include <utility> +#include "source/binary.h" +#include "source/util/string_utils.h" + namespace spvtools { namespace val { @@ -41,5 +44,12 @@ bool operator==(const Instruction& lhs, uint32_t rhs) { return lhs.id() == rhs; } +template <> +std::string Instruction::GetOperandAs<std::string>(size_t index) const { + const spv_parsed_operand_t& o = operands_.at(index); + assert(o.offset + o.num_words <= inst_.num_words); + return spvtools::utils::MakeString(words_.data() + o.offset, o.num_words); +} + } // namespace val } // namespace spvtools diff --git a/source/val/instruction.h b/source/val/instruction.h index 617cb066..6d1f9f4f 100644 --- a/source/val/instruction.h +++ b/source/val/instruction.h @@ -133,6 +133,9 @@ bool operator<(const Instruction& lhs, uint32_t rhs); bool operator==(const Instruction& lhs, const Instruction& rhs); bool operator==(const Instruction& lhs, uint32_t rhs); +template <> +std::string Instruction::GetOperandAs<std::string>(size_t index) const; + } // namespace val } // namespace spvtools diff --git a/source/val/validate.cpp b/source/val/validate.cpp index 45b6a463..ecc9fdb6 100644 --- a/source/val/validate.cpp +++ b/source/val/validate.cpp @@ -202,7 +202,7 @@ spv_result_t ValidateBinaryUsingContextAndValidationState( /* diagnostic = */ nullptr); // Parse the module and perform inline validation checks. These checks do - // not require the the knowledge of the whole module. + // not require the knowledge of the whole module. if (auto error = spvBinaryParse(&context, vstate, words, num_words, /*parsed_header =*/nullptr, ProcessInstruction, pDiagnostic)) { @@ -219,9 +219,7 @@ spv_result_t ValidateBinaryUsingContextAndValidationState( if (inst->opcode() == SpvOpEntryPoint) { const auto entry_point = inst->GetOperandAs<uint32_t>(1); const auto execution_model = inst->GetOperandAs<SpvExecutionModel>(0); - const char* str = reinterpret_cast<const char*>( - inst->words().data() + inst->operand(2).offset); - const std::string desc_name(str); + const std::string desc_name = inst->GetOperandAs<std::string>(2); ValidationState_t::EntryPointDescription desc; desc.name = desc_name; @@ -237,9 +235,8 @@ spv_result_t ValidateBinaryUsingContextAndValidationState( for (const Instruction* check_inst : visited_entry_points) { const auto check_execution_model = check_inst->GetOperandAs<SpvExecutionModel>(0); - const char* check_str = reinterpret_cast<const char*>( - check_inst->words().data() + inst->operand(2).offset); - const std::string check_name(check_str); + const std::string check_name = + check_inst->GetOperandAs<std::string>(2); if (desc_name == check_name && execution_model == check_execution_model) { @@ -351,7 +348,7 @@ spv_result_t ValidateBinaryUsingContextAndValidationState( } // Validate the preconditions involving adjacent instructions. e.g. SpvOpPhi - // must only be preceeded by SpvOpLabel, SpvOpPhi, or SpvOpLine. + // must only be preceded by SpvOpLabel, SpvOpPhi, or SpvOpLine. if (auto error = ValidateAdjacency(*vstate)) return error; if (auto error = ValidateEntryPoints(*vstate)) return error; diff --git a/source/val/validate.h b/source/val/validate.h index 3fc183de..cb1d05a5 100644 --- a/source/val/validate.h +++ b/source/val/validate.h @@ -70,7 +70,7 @@ spv_result_t CheckIdDefinitionDominateUse(ValidationState_t& _); /// /// This function will iterate over all instructions and check for any required /// predecessor and/or successor instructions. e.g. SpvOpPhi must only be -/// preceeded by SpvOpLabel, SpvOpPhi, or SpvOpLine. +/// preceded by SpvOpLabel, SpvOpPhi, or SpvOpLine. /// /// @param[in] _ the validation state of the module /// diff --git a/source/val/validate_annotation.cpp b/source/val/validate_annotation.cpp index 3a77552e..bef7ef9c 100644 --- a/source/val/validate_annotation.cpp +++ b/source/val/validate_annotation.cpp @@ -230,7 +230,7 @@ bool IsNotMemberDecoration(SpvDecoration dec) { spv_result_t ValidateDecorationTarget(ValidationState_t& _, SpvDecoration dec, const Instruction* inst, const Instruction* target) { - auto fail = [&_, dec, inst, target](uint32_t vuid = 0) -> DiagnosticStream { + auto fail = [&_, dec, inst, target](uint32_t vuid) -> DiagnosticStream { DiagnosticStream ds = std::move( _.diag(SPV_ERROR_INVALID_ID, inst) << _.VkErrorID(vuid) << LogStringForDecoration(dec) @@ -240,7 +240,7 @@ spv_result_t ValidateDecorationTarget(ValidationState_t& _, SpvDecoration dec, switch (dec) { case SpvDecorationSpecId: if (!spvOpcodeIsScalarSpecConstant(target->opcode())) { - return fail() << "must be a scalar specialization constant"; + return fail(0) << "must be a scalar specialization constant"; } break; case SpvDecorationBlock: @@ -249,14 +249,14 @@ spv_result_t ValidateDecorationTarget(ValidationState_t& _, SpvDecoration dec, case SpvDecorationGLSLPacked: case SpvDecorationCPacked: if (target->opcode() != SpvOpTypeStruct) { - return fail() << "must be a structure type"; + return fail(0) << "must be a structure type"; } break; case SpvDecorationArrayStride: if (target->opcode() != SpvOpTypeArray && target->opcode() != SpvOpTypeRuntimeArray && target->opcode() != SpvOpTypePointer) { - return fail() << "must be an array or pointer type"; + return fail(0) << "must be an array or pointer type"; } break; case SpvDecorationBuiltIn: @@ -269,10 +269,10 @@ spv_result_t ValidateDecorationTarget(ValidationState_t& _, SpvDecoration dec, if (_.HasCapability(SpvCapabilityShader) && inst->GetOperandAs<SpvBuiltIn>(2) == SpvBuiltInWorkgroupSize) { if (!spvOpcodeIsConstant(target->opcode())) { - return fail() << "must be a constant for WorkgroupSize"; + return fail(0) << "must be a constant for WorkgroupSize"; } } else if (target->opcode() != SpvOpVariable) { - return fail() << "must be a variable"; + return fail(0) << "must be a variable"; } break; case SpvDecorationNoPerspective: @@ -294,10 +294,10 @@ spv_result_t ValidateDecorationTarget(ValidationState_t& _, SpvDecoration dec, case SpvDecorationAliasedPointer: if (target->opcode() != SpvOpVariable && target->opcode() != SpvOpFunctionParameter) { - return fail() << "must be a memory object declaration"; + return fail(0) << "must be a memory object declaration"; } if (_.GetIdOpcode(target->type_id()) != SpvOpTypePointer) { - return fail() << "must be a pointer type"; + return fail(0) << "must be a pointer type"; } break; case SpvDecorationInvariant: @@ -308,7 +308,7 @@ spv_result_t ValidateDecorationTarget(ValidationState_t& _, SpvDecoration dec, case SpvDecorationDescriptorSet: case SpvDecorationInputAttachmentIndex: if (target->opcode() != SpvOpVariable) { - return fail() << "must be a variable"; + return fail(0) << "must be a variable"; } break; default: @@ -326,19 +326,22 @@ spv_result_t ValidateDecorationTarget(ValidationState_t& _, SpvDecoration dec, case SpvDecorationLocation: case SpvDecorationComponent: // Location is used for input, output and ray tracing stages. - if (sc == SpvStorageClassStorageBuffer || - sc == SpvStorageClassUniform || - sc == SpvStorageClassUniformConstant || - sc == SpvStorageClassWorkgroup || sc == SpvStorageClassPrivate || - sc == SpvStorageClassFunction) { + if (sc != SpvStorageClassInput && sc != SpvStorageClassOutput && + sc != SpvStorageClassRayPayloadKHR && + sc != SpvStorageClassIncomingRayPayloadKHR && + sc != SpvStorageClassHitAttributeKHR && + sc != SpvStorageClassCallableDataKHR && + sc != SpvStorageClassIncomingCallableDataKHR && + sc != SpvStorageClassShaderRecordBufferKHR) { return _.diag(SPV_ERROR_INVALID_ID, target) - << LogStringForDecoration(dec) + << _.VkErrorID(6672) << LogStringForDecoration(dec) << " decoration must not be applied to this storage class"; } break; case SpvDecorationIndex: + // Langauge from SPIR-V definition of Index if (sc != SpvStorageClassOutput) { - return fail() << "must be in the Output storage class"; + return fail(0) << "must be in the Output storage class"; } break; case SpvDecorationBinding: @@ -346,13 +349,13 @@ spv_result_t ValidateDecorationTarget(ValidationState_t& _, SpvDecoration dec, if (sc != SpvStorageClassStorageBuffer && sc != SpvStorageClassUniform && sc != SpvStorageClassUniformConstant) { - return fail() << "must be in the StorageBuffer, Uniform, or " - "UniformConstant storage class"; + return fail(6491) << "must be in the StorageBuffer, Uniform, or " + "UniformConstant storage class"; } break; case SpvDecorationInputAttachmentIndex: if (sc != SpvStorageClassUniformConstant) { - return fail() << "must be in the UniformConstant storage class"; + return fail(6678) << "must be in the UniformConstant storage class"; } break; case SpvDecorationFlat: diff --git a/source/val/validate_arithmetics.cpp b/source/val/validate_arithmetics.cpp index 433330d7..bae9b5dc 100644 --- a/source/val/validate_arithmetics.cpp +++ b/source/val/validate_arithmetics.cpp @@ -155,7 +155,7 @@ spv_result_t ArithmeticsPass(ValidationState_t& _, const Instruction* inst) { first_vector_num_components = num_components; } else if (num_components != first_vector_num_components) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected operands to have the same number of componenets: " + << "Expected operands to have the same number of components: " << spvOpcodeString(opcode); } } diff --git a/source/val/validate_atomics.cpp b/source/val/validate_atomics.cpp index cfa15d9f..3b0a7fa8 100644 --- a/source/val/validate_atomics.cpp +++ b/source/val/validate_atomics.cpp @@ -39,7 +39,7 @@ bool IsStorageClassAllowedByUniversalRules(uint32_t storage_class) { case SpvStorageClassAtomicCounter: case SpvStorageClassImage: case SpvStorageClassFunction: - case SpvStorageClassPhysicalStorageBufferEXT: + case SpvStorageClassPhysicalStorageBuffer: return true; break; default: diff --git a/source/val/validate_bitwise.cpp b/source/val/validate_bitwise.cpp index d46b3fca..e6e97c4a 100644 --- a/source/val/validate_bitwise.cpp +++ b/source/val/validate_bitwise.cpp @@ -14,16 +14,48 @@ // Validates correctness of bitwise instructions. -#include "source/val/validate.h" - #include "source/diagnostic.h" #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 { namespace val { +// Validates when base and result need to be the same type +spv_result_t ValidateBaseType(ValidationState_t& _, const Instruction* inst, + const uint32_t base_type) { + const SpvOp opcode = inst->opcode(); + + if (!_.IsIntScalarType(base_type) && !_.IsIntVectorType(base_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4781) + << "Expected int scalar or vector type for Base operand: " + << spvOpcodeString(opcode); + } + + // Vulkan has a restriction to 32 bit for base + if (spvIsVulkanEnv(_.context()->target_env)) { + if (_.GetBitWidth(base_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4781) + << "Expected 32-bit int type for Base operand: " + << spvOpcodeString(opcode); + } + } + + // OpBitCount just needs same number of components + if (base_type != inst->type_id() && opcode != SpvOpBitCount) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Base Type to be equal to Result Type: " + << spvOpcodeString(opcode); + } + + return SPV_SUCCESS; +} + // Validates correctness of bitwise instructions. spv_result_t BitwisePass(ValidationState_t& _, const Instruction* inst) { const SpvOp opcode = inst->opcode(); @@ -109,20 +141,14 @@ spv_result_t BitwisePass(ValidationState_t& _, const Instruction* inst) { } case SpvOpBitFieldInsert: { - if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected int scalar or vector type as Result Type: " - << spvOpcodeString(opcode); - const uint32_t base_type = _.GetOperandTypeId(inst, 2); const uint32_t insert_type = _.GetOperandTypeId(inst, 3); const uint32_t offset_type = _.GetOperandTypeId(inst, 4); const uint32_t count_type = _.GetOperandTypeId(inst, 5); - if (base_type != result_type) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Base Type to be equal to Result Type: " - << spvOpcodeString(opcode); + if (spv_result_t error = ValidateBaseType(_, inst, base_type)) { + return error; + } if (insert_type != result_type) return _.diag(SPV_ERROR_INVALID_DATA, inst) @@ -143,19 +169,13 @@ spv_result_t BitwisePass(ValidationState_t& _, const Instruction* inst) { case SpvOpBitFieldSExtract: case SpvOpBitFieldUExtract: { - if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected int scalar or vector type as Result Type: " - << spvOpcodeString(opcode); - const uint32_t base_type = _.GetOperandTypeId(inst, 2); const uint32_t offset_type = _.GetOperandTypeId(inst, 3); const uint32_t count_type = _.GetOperandTypeId(inst, 4); - if (base_type != result_type) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Base Type to be equal to Result Type: " - << spvOpcodeString(opcode); + if (spv_result_t error = ValidateBaseType(_, inst, base_type)) { + return error; + } if (!offset_type || !_.IsIntScalarType(offset_type)) return _.diag(SPV_ERROR_INVALID_DATA, inst) @@ -170,17 +190,12 @@ spv_result_t BitwisePass(ValidationState_t& _, const Instruction* inst) { } case SpvOpBitReverse: { - if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected int scalar or vector type as Result Type: " - << spvOpcodeString(opcode); - const uint32_t base_type = _.GetOperandTypeId(inst, 2); - if (base_type != result_type) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Base Type to be equal to Result Type: " - << spvOpcodeString(opcode); + if (spv_result_t error = ValidateBaseType(_, inst, base_type)) { + return error; + } + break; } @@ -191,15 +206,13 @@ spv_result_t BitwisePass(ValidationState_t& _, const Instruction* inst) { << spvOpcodeString(opcode); const uint32_t base_type = _.GetOperandTypeId(inst, 2); - if (!base_type || - (!_.IsIntScalarType(base_type) && !_.IsIntVectorType(base_type))) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Base Type to be int scalar or vector: " - << spvOpcodeString(opcode); - const uint32_t base_dimension = _.GetDimension(base_type); const uint32_t result_dimension = _.GetDimension(result_type); + if (spv_result_t error = ValidateBaseType(_, inst, base_type)) { + return error; + } + if (base_dimension != result_dimension) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected Base dimension to be equal to Result Type " diff --git a/source/val/validate_cfg.cpp b/source/val/validate_cfg.cpp index 7842e56d..dd605d2e 100644 --- a/source/val/validate_cfg.cpp +++ b/source/val/validate_cfg.cpp @@ -27,6 +27,7 @@ #include "source/cfa.h" #include "source/opcode.h" +#include "source/spirv_constant.h" #include "source/spirv_target_env.h" #include "source/spirv_validator_options.h" #include "source/val/basic_block.h" @@ -54,8 +55,7 @@ spv_result_t ValidatePhi(ValidationState_t& _, const Instruction* inst) { } if (_.IsPointerType(inst->type_id()) && _.addressing_model() == SpvAddressingModelLogical) { - if (!_.features().variable_pointers && - !_.features().variable_pointers_storage_buffer) { + if (!_.features().variable_pointers) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Using pointers with OpPhi requires capability " << "VariablePointers or VariablePointersStorageBuffer"; @@ -191,6 +191,12 @@ spv_result_t ValidateBranchConditional(ValidationState_t& _, "ID of an OpLabel instruction"; } + if (_.version() >= SPV_SPIRV_VERSION_WORD(1, 6) && true_id == false_id) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "In SPIR-V 1.6 or later, True Label and False Label must be " + "different labels"; + } + return SPV_SUCCESS; } @@ -242,13 +248,9 @@ spv_result_t ValidateReturnValue(ValidationState_t& _, << _.getIdName(value->type_id()) << "' is missing or void."; } - const bool uses_variable_pointer = - _.features().variable_pointers || - _.features().variable_pointers_storage_buffer; - if (_.addressing_model() == SpvAddressingModelLogical && - SpvOpTypePointer == value_type->opcode() && !uses_variable_pointer && - !_.options()->relax_logical_pointer) { + SpvOpTypePointer == value_type->opcode() && + !_.features().variable_pointers && !_.options()->relax_logical_pointer) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "OpReturnValue value's type <id> '" << _.getIdName(value->type_id()) @@ -668,7 +670,7 @@ spv_result_t ValidateStructuredSelections( } else if (terminator->opcode() == SpvOpSwitch) { if (!merge) { return _.diag(SPV_ERROR_INVALID_CFG, terminator) - << "OpSwitch must be preceeded by an OpSelectionMerge " + << "OpSwitch must be preceded by an OpSelectionMerge " "instruction"; } // Mark the targets as seen. @@ -910,7 +912,7 @@ spv_result_t PerformCfgChecks(ValidationState_t& _) { } } } - // If we have structed control flow, check that no block has a control + // If we have structured control flow, check that no block has a control // flow nesting depth larger than the limit. if (_.HasCapability(SpvCapabilityShader)) { const int control_flow_nesting_depth_limit = diff --git a/source/val/validate_decorations.cpp b/source/val/validate_decorations.cpp index 3cdb471c..73d512a8 100644 --- a/source/val/validate_decorations.cpp +++ b/source/val/validate_decorations.cpp @@ -21,11 +21,13 @@ #include <utility> #include <vector> +#include "source/binary.h" #include "source/diagnostic.h" #include "source/opcode.h" #include "source/spirv_constant.h" #include "source/spirv_target_env.h" #include "source/spirv_validator_options.h" +#include "source/util/string_utils.h" #include "source/val/validate_scopes.h" #include "source/val/validation_state.h" @@ -96,6 +98,14 @@ bool isBuiltInStruct(uint32_t struct_id, ValidationState_t& vstate) { }); } +// Returns true if the given structure type has a Block decoration. +bool isBlock(uint32_t struct_id, ValidationState_t& vstate) { + const auto& decorations = vstate.id_decorations(struct_id); + return std::any_of( + decorations.begin(), decorations.end(), + [](const Decoration& d) { return SpvDecorationBlock == d.dec_type(); }); +} + // Returns true if the given ID has the Import LinkageAttributes decoration. bool hasImportLinkageAttribute(uint32_t id, ValidationState_t& vstate) { const auto& decorations = vstate.id_decorations(id); @@ -455,7 +465,7 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str, return lhs.offset < rhs.offset; }); - // Now scan from lowest offest to highest offset. + // Now scan from lowest offset to highest offset. uint32_t nextValidOffset = 0; for (size_t ordered_member_idx = 0; ordered_member_idx < member_offsets.size(); ordered_member_idx++) { @@ -702,7 +712,7 @@ spv_result_t CheckBuiltInVariable(uint32_t var_id, ValidationState_t& vstate) { if (d.dec_type() == SpvDecorationLocation || d.dec_type() == SpvDecorationComponent) { return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id)) - << "A BuiltIn variable (id " << var_id + << vstate.VkErrorID(4915) << "A BuiltIn variable (id " << var_id << ") cannot have any Location or Component decorations"; } } @@ -710,12 +720,12 @@ spv_result_t CheckBuiltInVariable(uint32_t var_id, ValidationState_t& vstate) { return SPV_SUCCESS; } -// Checks whether proper decorations have been appied to the entry points. +// Checks whether proper decorations have been applied to the entry points. spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) { for (uint32_t entry_point : vstate.entry_points()) { const auto& descs = vstate.entry_point_descriptions(entry_point); - int num_builtin_inputs = 0; - int num_builtin_outputs = 0; + int num_builtin_block_inputs = 0; + int num_builtin_block_outputs = 0; int num_workgroup_variables = 0; int num_workgroup_variables_with_block = 0; int num_workgroup_variables_with_aliased = 0; @@ -766,9 +776,20 @@ spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) { Instruction* type_instr = vstate.FindDef(type_id); if (type_instr && SpvOpTypeStruct == type_instr->opcode() && isBuiltInStruct(type_id, vstate)) { - if (storage_class == SpvStorageClassInput) ++num_builtin_inputs; - if (storage_class == SpvStorageClassOutput) ++num_builtin_outputs; - if (num_builtin_inputs > 1 || num_builtin_outputs > 1) break; + if (!isBlock(type_id, vstate)) { + return vstate.diag(SPV_ERROR_INVALID_DATA, vstate.FindDef(type_id)) + << vstate.VkErrorID(4919) + << "Interface struct has no Block decoration but has " + "BuiltIn members. " + "Location decorations must be used on each member of " + "OpVariable with a structure type that is a block not " + "decorated with Location."; + } + if (storage_class == SpvStorageClassInput) ++num_builtin_block_inputs; + if (storage_class == SpvStorageClassOutput) + ++num_builtin_block_outputs; + if (num_builtin_block_inputs > 1 || num_builtin_block_outputs > 1) + break; if (auto error = CheckBuiltInVariable(interface, vstate)) return error; } else if (isBuiltInVar(interface, vstate)) { @@ -786,7 +807,7 @@ spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) { } } } - if (num_builtin_inputs > 1 || num_builtin_outputs > 1) { + if (num_builtin_block_inputs > 1 || num_builtin_block_outputs > 1) { return vstate.diag(SPV_ERROR_INVALID_BINARY, vstate.FindDef(entry_point)) << "There must be at most one object per Storage Class that can " @@ -798,8 +819,8 @@ spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) { // targeted by an OpEntryPoint instruction for (auto& decoration : vstate.id_decorations(entry_point)) { if (SpvDecorationLinkageAttributes == decoration.dec_type()) { - const char* linkage_name = - reinterpret_cast<const char*>(&decoration.params()[0]); + const std::string linkage_name = + spvtools::utils::MakeString(decoration.params()); return vstate.diag(SPV_ERROR_INVALID_BINARY, vstate.FindDef(entry_point)) << "The LinkageAttributes Decoration (Linkage name: " @@ -935,41 +956,41 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) { const bool storage_buffer = storageClass == SpvStorageClassStorageBuffer; if (spvIsVulkanEnv(vstate.context()->target_env)) { - // Vulkan 14.5.1: There must be no more than one PushConstant block - // per entry point. + // Vulkan: There must be no more than one PushConstant block per entry + // point. if (push_constant) { auto entry_points = vstate.EntryPointReferences(var_id); for (auto ep_id : entry_points) { const bool already_used = !uses_push_constant.insert(ep_id).second; if (already_used) { return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id)) - << "Entry point id '" << ep_id + << vstate.VkErrorID(6674) << "Entry point id '" << ep_id << "' uses more than one PushConstant interface.\n" - << "From Vulkan spec, section 14.5.1:\n" + << "From Vulkan spec:\n" << "There must be no more than one push constant block " << "statically used per shader entry point."; } } } - // Vulkan 14.5.2: Check DescriptorSet and Binding decoration for + // Vulkan: Check DescriptorSet and Binding decoration for // UniformConstant which cannot be a struct. if (uniform_constant) { auto entry_points = vstate.EntryPointReferences(var_id); if (!entry_points.empty() && !hasDecoration(var_id, SpvDecorationDescriptorSet, vstate)) { return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id)) - << "UniformConstant id '" << var_id + << vstate.VkErrorID(6677) << "UniformConstant id '" << var_id << "' is missing DescriptorSet decoration.\n" - << "From Vulkan spec, section 14.5.2:\n" + << "From Vulkan spec:\n" << "These variables must have DescriptorSet and Binding " "decorations specified"; } if (!entry_points.empty() && !hasDecoration(var_id, SpvDecorationBinding, vstate)) { return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id)) - << "UniformConstant id '" << var_id + << vstate.VkErrorID(6677) << "UniformConstant id '" << var_id << "' is missing Binding decoration.\n" - << "From Vulkan spec, section 14.5.2:\n" + << "From Vulkan spec:\n" << "These variables must have DescriptorSet and Binding " "decorations specified"; } @@ -996,7 +1017,7 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) { } const bool phys_storage_buffer = - storageClass == SpvStorageClassPhysicalStorageBufferEXT; + storageClass == SpvStorageClassPhysicalStorageBuffer; const bool workgroup = storageClass == SpvStorageClassWorkgroup && vstate.HasCapability(SpvCapabilityWorkgroupMemoryExplicitLayoutKHR); @@ -1030,55 +1051,55 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) { hasDecoration(id, SpvDecorationBufferBlock, vstate); if (storage_buffer && buffer_block) { return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id)) - << "Storage buffer id '" << var_id + << vstate.VkErrorID(6675) << "Storage buffer id '" << var_id << " In Vulkan, BufferBlock is disallowed on variables in " "the StorageBuffer storage class"; } - // Vulkan 14.5.1/2: Check Block decoration for PushConstant, Uniform + // Vulkan: Check Block decoration for PushConstant, Uniform // and StorageBuffer variables. Uniform can also use BufferBlock. if (push_constant && !block) { return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id)) - << "PushConstant id '" << id + << vstate.VkErrorID(6675) << "PushConstant id '" << id << "' is missing Block decoration.\n" - << "From Vulkan spec, section 14.5.1:\n" + << "From Vulkan spec:\n" << "Such variables must be identified with a Block " "decoration"; } if (storage_buffer && !block) { return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id)) - << "StorageBuffer id '" << id + << vstate.VkErrorID(6675) << "StorageBuffer id '" << id << "' is missing Block decoration.\n" - << "From Vulkan spec, section 14.5.2:\n" + << "From Vulkan spec:\n" << "Such variables must be identified with a Block " "decoration"; } if (uniform && !block && !buffer_block) { return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id)) - << "Uniform id '" << id + << vstate.VkErrorID(6676) << "Uniform id '" << id << "' is missing Block or BufferBlock decoration.\n" - << "From Vulkan spec, section 14.5.2:\n" + << "From Vulkan spec:\n" << "Such variables must be identified with a Block or " "BufferBlock decoration"; } - // Vulkan 14.5.2: Check DescriptorSet and Binding decoration for + // Vulkan: Check DescriptorSet and Binding decoration for // Uniform and StorageBuffer variables. if (uniform || storage_buffer) { auto entry_points = vstate.EntryPointReferences(var_id); if (!entry_points.empty() && !hasDecoration(var_id, SpvDecorationDescriptorSet, vstate)) { return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id)) - << sc_str << " id '" << var_id + << vstate.VkErrorID(6677) << sc_str << " id '" << var_id << "' is missing DescriptorSet decoration.\n" - << "From Vulkan spec, section 14.5.2:\n" + << "From Vulkan spec:\n" << "These variables must have DescriptorSet and Binding " "decorations specified"; } if (!entry_points.empty() && !hasDecoration(var_id, SpvDecorationBinding, vstate)) { return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id)) - << sc_str << " id '" << var_id + << vstate.VkErrorID(6677) << sc_str << " id '" << var_id << "' is missing Binding decoration.\n" - << "From Vulkan spec, section 14.5.2:\n" + << "From Vulkan spec:\n" << "These variables must have DescriptorSet and Binding " "decorations specified"; } @@ -1386,11 +1407,11 @@ spv_result_t CheckFPRoundingModeForShaders(ValidationState_t& vstate, storage != SpvStorageClassUniform && storage != SpvStorageClassPushConstant && storage != SpvStorageClassInput && storage != SpvStorageClassOutput && - storage != SpvStorageClassPhysicalStorageBufferEXT) { + storage != SpvStorageClassPhysicalStorageBuffer) { return vstate.diag(SPV_ERROR_INVALID_ID, &inst) << "FPRoundingMode decoration can be applied only to the " "Object operand of an OpStore in the StorageBuffer, " - "PhysicalStorageBufferEXT, Uniform, PushConstant, Input, or " + "PhysicalStorageBuffer, Uniform, PushConstant, Input, or " "Output Storage Classes."; } } diff --git a/source/val/validate_extensions.cpp b/source/val/validate_extensions.cpp index dccbe149..fcf04e20 100644 --- a/source/val/validate_extensions.cpp +++ b/source/val/validate_extensions.cpp @@ -129,7 +129,7 @@ spv_result_t ValidateUint32ConstantOperandForDebugInfo( } // True if the operand of a debug info instruction |inst| at |word_index| -// satisifies |expectation| that is given as a function. Otherwise, +// satisfies |expectation| that is given as a function. Otherwise, // returns false. bool DoesDebugInfoOperandMatchExpectation( const ValidationState_t& _, @@ -147,6 +147,24 @@ bool DoesDebugInfoOperandMatchExpectation( return true; } +// Overload for NonSemanticShaderDebugInfo100Instructions. +bool DoesDebugInfoOperandMatchExpectation( + const ValidationState_t& _, + const std::function<bool(NonSemanticShaderDebugInfo100Instructions)>& + expectation, + const Instruction* inst, uint32_t word_index) { + if (inst->words().size() <= word_index) return false; + auto* debug_inst = _.FindDef(inst->word(word_index)); + if (debug_inst->opcode() != SpvOpExtInst || + (debug_inst->ext_inst_type() != + SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) || + !expectation( + NonSemanticShaderDebugInfo100Instructions(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|. @@ -223,6 +241,18 @@ spv_result_t ValidateOperandDebugType( const Instruction* inst, uint32_t word_index, const std::function<std::string()>& ext_inst_name, bool allow_template_param) { + // Check for NonSemanticShaderDebugInfo100 specific types. + if (inst->ext_inst_type() == + SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) { + std::function<bool(NonSemanticShaderDebugInfo100Instructions)> expectation = + [](NonSemanticShaderDebugInfo100Instructions dbg_inst) { + return dbg_inst == NonSemanticShaderDebugInfo100DebugTypeMatrix; + }; + if (DoesDebugInfoOperandMatchExpectation(_, expectation, inst, word_index)) + return SPV_SUCCESS; + } + + // Check for common types. std::function<bool(CommonDebugInfoInstructions)> expectation = [&allow_template_param](CommonDebugInfoInstructions dbg_inst) { if (allow_template_param && @@ -280,8 +310,7 @@ spv_result_t ValidateClspvReflectionKernel(ValidationState_t& _, return _.diag(SPV_ERROR_INVALID_ID, inst) << "Name must be an OpString"; } - const std::string name_str = reinterpret_cast<const char*>( - name->words().data() + name->operands()[1].offset); + const std::string name_str = name->GetOperandAs<std::string>(1); bool found = false; for (auto& desc : _.entry_point_descriptions(kernel_id)) { if (name_str == desc.name) { @@ -740,9 +769,9 @@ spv_result_t ValidateExtension(ValidationState_t& _, const Instruction* inst) { spv_result_t ValidateExtInstImport(ValidationState_t& _, const Instruction* inst) { const auto name_id = 1; - if (!_.HasExtension(kSPV_KHR_non_semantic_info)) { - const std::string name(reinterpret_cast<const char*>( - inst->words().data() + inst->operands()[name_id].offset)); + if (_.version() <= SPV_SPIRV_VERSION_WORD(1, 5) && + !_.HasExtension(kSPV_KHR_non_semantic_info)) { + const std::string name = inst->GetOperandAs<std::string>(name_id); if (name.find("NonSemantic.") == 0) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "NonSemantic extended instruction sets cannot be declared " @@ -774,7 +803,7 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) { assert(import_inst); std::ostringstream ss; - ss << reinterpret_cast<const char*>(import_inst->words().data() + 2); + ss << import_inst->GetOperandAs<std::string>(1); ss << " "; ss << desc->name; @@ -2720,6 +2749,86 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) { auto num_words = inst->words().size(); + // Handle any non-common NonSemanticShaderDebugInfo instructions. + if (vulkanDebugInfo) { + const NonSemanticShaderDebugInfo100Instructions ext_inst_key = + NonSemanticShaderDebugInfo100Instructions(ext_inst_index); + switch (ext_inst_key) { + // The following block of instructions will be handled by the common + // validation. + case NonSemanticShaderDebugInfo100DebugInfoNone: + case NonSemanticShaderDebugInfo100DebugCompilationUnit: + case NonSemanticShaderDebugInfo100DebugTypeBasic: + case NonSemanticShaderDebugInfo100DebugTypePointer: + case NonSemanticShaderDebugInfo100DebugTypeQualifier: + case NonSemanticShaderDebugInfo100DebugTypeArray: + case NonSemanticShaderDebugInfo100DebugTypeVector: + case NonSemanticShaderDebugInfo100DebugTypedef: + case NonSemanticShaderDebugInfo100DebugTypeFunction: + case NonSemanticShaderDebugInfo100DebugTypeEnum: + case NonSemanticShaderDebugInfo100DebugTypeComposite: + case NonSemanticShaderDebugInfo100DebugTypeMember: + case NonSemanticShaderDebugInfo100DebugTypeInheritance: + case NonSemanticShaderDebugInfo100DebugTypePtrToMember: + case NonSemanticShaderDebugInfo100DebugTypeTemplate: + case NonSemanticShaderDebugInfo100DebugTypeTemplateParameter: + case NonSemanticShaderDebugInfo100DebugTypeTemplateTemplateParameter: + case NonSemanticShaderDebugInfo100DebugTypeTemplateParameterPack: + case NonSemanticShaderDebugInfo100DebugGlobalVariable: + case NonSemanticShaderDebugInfo100DebugFunctionDeclaration: + case NonSemanticShaderDebugInfo100DebugFunction: + case NonSemanticShaderDebugInfo100DebugLexicalBlock: + case NonSemanticShaderDebugInfo100DebugLexicalBlockDiscriminator: + case NonSemanticShaderDebugInfo100DebugScope: + case NonSemanticShaderDebugInfo100DebugNoScope: + case NonSemanticShaderDebugInfo100DebugInlinedAt: + case NonSemanticShaderDebugInfo100DebugLocalVariable: + case NonSemanticShaderDebugInfo100DebugInlinedVariable: + case NonSemanticShaderDebugInfo100DebugDeclare: + case NonSemanticShaderDebugInfo100DebugValue: + case NonSemanticShaderDebugInfo100DebugOperation: + case NonSemanticShaderDebugInfo100DebugExpression: + case NonSemanticShaderDebugInfo100DebugMacroDef: + case NonSemanticShaderDebugInfo100DebugMacroUndef: + case NonSemanticShaderDebugInfo100DebugImportedEntity: + case NonSemanticShaderDebugInfo100DebugSource: + break; + case NonSemanticShaderDebugInfo100DebugTypeMatrix: { + CHECK_DEBUG_OPERAND("Vector Type", CommonDebugInfoDebugTypeVector, 5); + + CHECK_CONST_UINT_OPERAND("Vector Count", 6); + + uint32_t vector_count = inst->word(6); + uint64_t const_val; + if (!_.GetConstantValUint64(vector_count, &const_val)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() + << ": Vector Count must be 32-bit integer OpConstant"; + } + + vector_count = const_val & 0xffffffff; + if (!vector_count || vector_count > 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": Vector Count must be positive " + << "integer less than or equal to 4"; + } + break; + } + // TODO: Add validation rules for remaining cases as well. + case NonSemanticShaderDebugInfo100DebugFunctionDefinition: + case NonSemanticShaderDebugInfo100DebugSourceContinued: + case NonSemanticShaderDebugInfo100DebugLine: + case NonSemanticShaderDebugInfo100DebugNoLine: + case NonSemanticShaderDebugInfo100DebugBuildIdentifier: + case NonSemanticShaderDebugInfo100DebugStoragePath: + case NonSemanticShaderDebugInfo100DebugEntryPoint: + break; + case NonSemanticShaderDebugInfo100InstructionsMax: + assert(0); + break; + } + } + // Handle any non-common OpenCL insts, then common if (ext_inst_type != SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 || OpenCLDebugInfo100Instructions(ext_inst_index) != @@ -2805,8 +2914,9 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) { bool invalid = false; auto* component_count = _.FindDef(inst->word(i)); if (IsConstIntScalarTypeWith32Or64Bits(_, component_count)) { - // TODO: We need a spec discussion for the bindless array. - if (!component_count->word(3)) { + // TODO: We need a spec discussion for the runtime array for + // OpenCL. + if (!vulkanDebugInfo && !component_count->word(3)) { invalid = true; } } else if (component_count->words().size() > 6 && @@ -3264,8 +3374,7 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) { } } else if (ext_inst_type == SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION) { auto import_inst = _.FindDef(inst->GetOperandAs<uint32_t>(2)); - const std::string name(reinterpret_cast<const char*>( - import_inst->words().data() + import_inst->operands()[1].offset)); + const std::string name = import_inst->GetOperandAs<std::string>(1); const std::string reflection = "NonSemantic.ClspvReflection."; char* end_ptr; auto version_string = name.substr(reflection.size()); diff --git a/source/val/validate_function.cpp b/source/val/validate_function.cpp index 596186bb..2a5fed8e 100644 --- a/source/val/validate_function.cpp +++ b/source/val/validate_function.cpp @@ -147,8 +147,8 @@ spv_result_t ValidateFunctionParameter(ValidationState_t& _, "type of the same index."; } - // Validate that PhysicalStorageBufferEXT have one of Restrict, Aliased, - // RestrictPointerEXT, or AliasedPointerEXT. + // Validate that PhysicalStorageBuffer have one of Restrict, Aliased, + // RestrictPointer, or AliasedPointer. auto param_nonarray_type_id = param_type->id(); while (_.GetIdOpcode(param_nonarray_type_id) == SpvOpTypeArray) { param_nonarray_type_id = @@ -157,7 +157,7 @@ spv_result_t ValidateFunctionParameter(ValidationState_t& _, if (_.GetIdOpcode(param_nonarray_type_id) == SpvOpTypePointer) { auto param_nonarray_type = _.FindDef(param_nonarray_type_id); if (param_nonarray_type->GetOperandAs<uint32_t>(1u) == - SpvStorageClassPhysicalStorageBufferEXT) { + SpvStorageClassPhysicalStorageBuffer) { // check for Aliased or Restrict const auto& decorations = _.id_decorations(inst->id()); @@ -174,14 +174,14 @@ spv_result_t ValidateFunctionParameter(ValidationState_t& _, if (!foundAliased && !foundRestrict) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "OpFunctionParameter " << inst->id() - << ": expected Aliased or Restrict for PhysicalStorageBufferEXT " + << ": expected Aliased or Restrict for PhysicalStorageBuffer " "pointer."; } if (foundAliased && foundRestrict) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "OpFunctionParameter " << inst->id() << ": can't specify both Aliased and Restrict for " - "PhysicalStorageBufferEXT pointer."; + "PhysicalStorageBuffer pointer."; } } else { const auto pointee_type_id = @@ -189,31 +189,31 @@ spv_result_t ValidateFunctionParameter(ValidationState_t& _, const auto pointee_type = _.FindDef(pointee_type_id); if (SpvOpTypePointer == pointee_type->opcode() && pointee_type->GetOperandAs<uint32_t>(1u) == - SpvStorageClassPhysicalStorageBufferEXT) { - // check for AliasedPointerEXT/RestrictPointerEXT + SpvStorageClassPhysicalStorageBuffer) { + // check for AliasedPointer/RestrictPointer const auto& decorations = _.id_decorations(inst->id()); bool foundAliased = std::any_of( decorations.begin(), decorations.end(), [](const Decoration& d) { - return SpvDecorationAliasedPointerEXT == d.dec_type(); + return SpvDecorationAliasedPointer == d.dec_type(); }); bool foundRestrict = std::any_of( decorations.begin(), decorations.end(), [](const Decoration& d) { - return SpvDecorationRestrictPointerEXT == d.dec_type(); + return SpvDecorationRestrictPointer == d.dec_type(); }); if (!foundAliased && !foundRestrict) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "OpFunctionParameter " << inst->id() - << ": expected AliasedPointerEXT or RestrictPointerEXT for " - "PhysicalStorageBufferEXT pointer."; + << ": expected AliasedPointer or RestrictPointer for " + "PhysicalStorageBuffer pointer."; } if (foundAliased && foundRestrict) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "OpFunctionParameter " << inst->id() - << ": can't specify both AliasedPointerEXT and " - "RestrictPointerEXT for PhysicalStorageBufferEXT pointer."; + << ": can't specify both AliasedPointer and " + "RestrictPointer for PhysicalStorageBuffer pointer."; } } } @@ -300,7 +300,7 @@ spv_result_t ValidateFunctionCall(ValidationState_t& _, // These are always allowed. break; case SpvStorageClassStorageBuffer: - if (!_.features().variable_pointers_storage_buffer) { + if (!_.features().variable_pointers) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "StorageBuffer pointer operand " << _.getIdName(argument_id) @@ -316,11 +316,10 @@ spv_result_t ValidateFunctionCall(ValidationState_t& _, // Validate memory object declaration requirements. if (argument->opcode() != SpvOpVariable && argument->opcode() != SpvOpFunctionParameter) { - const bool ssbo_vptr = - _.features().variable_pointers_storage_buffer && - sc == SpvStorageClassStorageBuffer; - const bool wg_vptr = - _.features().variable_pointers && sc == SpvStorageClassWorkgroup; + const bool ssbo_vptr = _.features().variable_pointers && + sc == SpvStorageClassStorageBuffer; + const bool wg_vptr = _.HasCapability(SpvCapabilityVariablePointers) && + sc == SpvStorageClassWorkgroup; const bool uc_ptr = sc == SpvStorageClassUniformConstant; if (!ssbo_vptr && !wg_vptr && !uc_ptr) { return _.diag(SPV_ERROR_INVALID_ID, inst) diff --git a/source/val/validate_image.cpp b/source/val/validate_image.cpp index 64f6ba7b..f6d7d103 100644 --- a/source/val/validate_image.cpp +++ b/source/val/validate_image.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017 Google Inc. +// Copyright (c) 2017 Google Inc. // Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights // reserved. // @@ -20,6 +20,7 @@ #include "source/diagnostic.h" #include "source/opcode.h" +#include "source/spirv_constant.h" #include "source/spirv_target_env.h" #include "source/util/bitutils.h" #include "source/val/instruction.h" @@ -71,6 +72,7 @@ bool CheckAllImageOperandsHandled() { // blocks other PRs. // https://github.com/KhronosGroup/SPIRV-Tools/issues/4565 case SpvImageOperandsOffsetsMask: + case SpvImageOperandsNontemporalMask: return true; } return false; @@ -258,7 +260,8 @@ spv_result_t ValidateImageOperands(ValidationState_t& _, mask & ~uint32_t(SpvImageOperandsNonPrivateTexelKHRMask | SpvImageOperandsVolatileTexelKHRMask | SpvImageOperandsSignExtendMask | - SpvImageOperandsZeroExtendMask); + SpvImageOperandsZeroExtendMask | + SpvImageOperandsNontemporalMask); size_t expected_num_image_operand_words = spvtools::utils::CountSetBits(mask_bits_having_operands); if (mask & SpvImageOperandsGradMask) { @@ -500,7 +503,7 @@ spv_result_t ValidateImageOperands(ValidationState_t& _, if (!_.IsIntVectorType(component_type) || _.GetDimension(component_type) != 2) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Image Operand ConstOffsets array componenets to be " + << "Expected Image Operand ConstOffsets array components to be " "int vectors of size 2"; } @@ -630,68 +633,71 @@ spv_result_t ValidateImageOperands(ValidationState_t& _, // TODO: add validation } + if (mask & SpvImageOperandsNontemporalMask) { + // Checked elsewhere: SPIR-V 1.6 version or later. + } + return SPV_SUCCESS; } -// Checks some of the validation rules which are common to multiple opcodes. -spv_result_t ValidateImageCommon(ValidationState_t& _, const Instruction* inst, - const ImageTypeInfo& info) { - const SpvOp opcode = inst->opcode(); - if (IsProj(opcode)) { - if (info.dim != SpvDim1D && info.dim != SpvDim2D && info.dim != SpvDim3D && - info.dim != SpvDimRect) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Image 'Dim' parameter to be 1D, 2D, 3D or Rect"; - } +// Validate OpImage*Proj* instructions +spv_result_t ValidateImageProj(ValidationState_t& _, const Instruction* inst, + const ImageTypeInfo& info) { + if (info.dim != SpvDim1D && info.dim != SpvDim2D && info.dim != SpvDim3D && + info.dim != SpvDimRect) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image 'Dim' parameter to be 1D, 2D, 3D or Rect"; + } - if (info.multisampled != 0) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Image 'MS' parameter to be 0"; - } + if (info.multisampled != 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image 'MS' parameter to be 0"; + } - if (info.arrayed != 0) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Image 'arrayed' parameter to be 0"; - } + if (info.arrayed != 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image 'arrayed' parameter to be 0"; } - if (opcode == SpvOpImageRead || opcode == SpvOpImageSparseRead || - opcode == SpvOpImageWrite) { - if (info.sampled == 0) { - } else if (info.sampled == 2) { - if (info.dim == SpvDim1D && !_.HasCapability(SpvCapabilityImage1D)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Capability Image1D is required to access storage image"; - } else if (info.dim == SpvDimRect && - !_.HasCapability(SpvCapabilityImageRect)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Capability ImageRect is required to access storage image"; - } else if (info.dim == SpvDimBuffer && - !_.HasCapability(SpvCapabilityImageBuffer)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Capability ImageBuffer is required to access storage image"; - } else if (info.dim == SpvDimCube && info.arrayed == 1 && - !_.HasCapability(SpvCapabilityImageCubeArray)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Capability ImageCubeArray is required to access " - << "storage image"; - } + return SPV_SUCCESS; +} - if (info.multisampled == 1 && - !_.HasCapability(SpvCapabilityImageMSArray)) { +// Validate OpImage*Read and OpImage*Write instructions +spv_result_t ValidateImageReadWrite(ValidationState_t& _, + const Instruction* inst, + const ImageTypeInfo& info) { + if (info.sampled == 2) { + if (info.dim == SpvDim1D && !_.HasCapability(SpvCapabilityImage1D)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Capability Image1D is required to access storage image"; + } else if (info.dim == SpvDimRect && + !_.HasCapability(SpvCapabilityImageRect)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Capability ImageRect is required to access storage image"; + } else if (info.dim == SpvDimBuffer && + !_.HasCapability(SpvCapabilityImageBuffer)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Capability ImageBuffer is required to access storage image"; + } else if (info.dim == SpvDimCube && info.arrayed == 1 && + !_.HasCapability(SpvCapabilityImageCubeArray)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Capability ImageCubeArray is required to access " + << "storage image"; + } + + if (info.multisampled == 1 && !_.HasCapability(SpvCapabilityImageMSArray)) { #if 0 - // TODO(atgoo@github.com) The description of this rule in the spec - // is unclear and Glslang doesn't declare ImageMSArray. Need to clarify - // and reenable. - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Capability ImageMSArray is required to access storage " - << "image"; -#endif - } - } else { + // TODO(atgoo@github.com) The description of this rule in the spec + // is unclear and Glslang doesn't declare ImageMSArray. Need to clarify + // and reenable. return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Image 'Sampled' parameter to be 0 or 2"; + << "Capability ImageMSArray is required to access storage " + << "image"; +#endif } + } else if (info.sampled != 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image 'Sampled' parameter to be 0 or 2"; } return SPV_SUCCESS; @@ -806,7 +812,8 @@ spv_result_t ValidateTypeImage(ValidationState_t& _, const Instruction* inst) { } } - // Dim is checked elsewhere. + // Universal checks on image type operands + // Dim and Format and Access Qualifier are checked elsewhere. if (info.depth > 2) { return _.diag(SPV_ERROR_INVALID_DATA, inst) @@ -818,62 +825,53 @@ spv_result_t ValidateTypeImage(ValidationState_t& _, const Instruction* inst) { << "Invalid Arrayed " << info.arrayed << " (must be 0 or 1)"; } - if (spvIsOpenCLEnv(target_env)) { - if ((info.arrayed == 1) && (info.dim != SpvDim1D) && - (info.dim != SpvDim2D)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "In the OpenCL environment, Arrayed may only be set to 1 " - << "when Dim is either 1D or 2D."; - } - } - if (info.multisampled > 1) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Invalid MS " << info.multisampled << " (must be 0 or 1)"; } - if (spvIsOpenCLEnv(target_env)) { - if (info.multisampled != 0) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "MS must be 0 in the OpenCL environment."; - } - } - if (info.sampled > 2) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Invalid Sampled " << info.sampled << " (must be 0, 1 or 2)"; } - if (spvIsVulkanEnv(target_env)) { - if (info.sampled == 0) { + if (info.dim == SpvDimSubpassData) { + if (info.sampled != 2) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << _.VkErrorID(4657) - << "Sampled must be 1 or 2 in the Vulkan environment."; + << _.VkErrorID(6214) << "Dim SubpassData requires Sampled to be 2"; } - } - if (spvIsOpenCLEnv(_.context()->target_env)) { - if (info.sampled != 0) { + if (info.format != SpvImageFormatUnknown) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Sampled must be 0 in the OpenCL environment."; + << "Dim SubpassData requires format Unknown"; + } + } else { + if (info.multisampled && (info.sampled == 2) && + !_.HasCapability(SpvCapabilityStorageImageMultisample)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Capability StorageImageMultisample is required when using " + "multisampled storage image"; } } - if (info.dim == SpvDimSubpassData) { - if (info.sampled != 2) { + if (spvIsOpenCLEnv(target_env)) { + if ((info.arrayed == 1) && (info.dim != SpvDim1D) && + (info.dim != SpvDim2D)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Dim SubpassData requires Sampled to be 2"; + << "In the OpenCL environment, Arrayed may only be set to 1 " + << "when Dim is either 1D or 2D."; } - if (info.format != SpvImageFormatUnknown) { + if (info.multisampled != 0) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Dim SubpassData requires format Unknown"; + << "MS must be 0 in the OpenCL environment."; } - } - // Format and Access Qualifier are also checked elsewhere. + if (info.sampled != 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Sampled must be 0 in the OpenCL environment."; + } - if (spvIsOpenCLEnv(_.context()->target_env)) { if (info.access_qualifier == SpvAccessQualifierMax) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "In the OpenCL environment, the optional Access Qualifier" @@ -881,12 +879,16 @@ spv_result_t ValidateTypeImage(ValidationState_t& _, const Instruction* inst) { } } - if (info.multisampled && (info.sampled == 2) && - (info.dim != SpvDimSubpassData)) { - if (!_.HasCapability(SpvCapabilityStorageImageMultisample)) { + if (spvIsVulkanEnv(target_env)) { + if (info.sampled == 0) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Capability StorageImageMultisample is required when using " - "multisampled storage image"; + << _.VkErrorID(4657) + << "Sampled must be 1 or 2 in the Vulkan environment."; + } + + if (info.dim == SpvDimSubpassData && info.arrayed != 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(6214) << "Dim SubpassData requires Arrayed to be 0"; } } @@ -915,6 +917,13 @@ spv_result_t ValidateTypeSampledImage(ValidationState_t& _, "operand set to 0 or 1"; } + // This covers both OpTypeSampledImage and OpSampledImage. + if (_.version() >= SPV_SPIRV_VERSION_WORD(1, 6) && info.dim == SpvDimBuffer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "In SPIR-V 1.6 or later, sampled image dimension must not be " + "Buffer"; + } + return SPV_SUCCESS; } @@ -971,8 +980,9 @@ spv_result_t ValidateSampledImage(ValidationState_t& _, if (spvIsVulkanEnv(_.context()->target_env)) { if (info.sampled != 1) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Image 'Sampled' parameter to be 1 " - << "for Vulkan environment."; + << _.VkErrorID(6671) + << "Expected Image 'Sampled' parameter to be 1 for Vulkan " + "environment."; } } else { if (info.sampled != 0 && info.sampled != 1) { @@ -1030,7 +1040,7 @@ spv_result_t ValidateSampledImage(ValidationState_t& _, << "Result <id> from OpSampledImage instruction must not appear " "as operand for Op" << spvOpcodeString(static_cast<SpvOp>(consumer_opcode)) - << ", since it is not specificed as taking an " + << ", since it is not specified as taking an " << "OpTypeSampledImage." << " Found result <id> '" << _.getIdName(inst->id()) << "' as an operand of <id> '" @@ -1191,7 +1201,9 @@ spv_result_t ValidateImageLod(ValidationState_t& _, const Instruction* inst) { << "Corrupt image type definition"; } - if (spv_result_t result = ValidateImageCommon(_, inst, info)) return result; + if (IsProj(opcode)) { + if (spv_result_t result = ValidateImageProj(_, inst, info)) return result; + } if (info.multisampled) { // When using image operands, the Sample image operand is required if and @@ -1254,6 +1266,27 @@ spv_result_t ValidateImageLod(ValidationState_t& _, const Instruction* inst) { return SPV_SUCCESS; } +// Validates anything OpImage*Dref* instruction +spv_result_t ValidateImageDref(ValidationState_t& _, const Instruction* inst, + const ImageTypeInfo& info) { + const uint32_t dref_type = _.GetOperandTypeId(inst, 4); + if (!_.IsFloatScalarType(dref_type) || _.GetBitWidth(dref_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Dref to be of 32-bit float type"; + } + + if (spvIsVulkanEnv(_.context()->target_env)) { + if (info.dim == SpvDim3D) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4777) + << "In Vulkan, OpImage*Dref* instructions must not use images " + "with a 3D Dim"; + } + } + + return SPV_SUCCESS; +} + spv_result_t ValidateImageDrefLod(ValidationState_t& _, const Instruction* inst) { const SpvOp opcode = inst->opcode(); @@ -1281,7 +1314,9 @@ spv_result_t ValidateImageDrefLod(ValidationState_t& _, << "Corrupt image type definition"; } - if (spv_result_t result = ValidateImageCommon(_, inst, info)) return result; + if (IsProj(opcode)) { + if (spv_result_t result = ValidateImageProj(_, inst, info)) return result; + } if (info.multisampled) { // When using image operands, the Sample image operand is required if and @@ -1311,11 +1346,7 @@ spv_result_t ValidateImageDrefLod(ValidationState_t& _, << " components, but given only " << actual_coord_size; } - const uint32_t dref_type = _.GetOperandTypeId(inst, 4); - if (!_.IsFloatScalarType(dref_type) || _.GetBitWidth(dref_type) != 32) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Dref to be of 32-bit float type"; - } + if (spv_result_t result = ValidateImageDref(_, inst, info)) return result; if (spv_result_t result = ValidateImageOperands(_, inst, info, /* word_index = */ 7)) @@ -1450,7 +1481,8 @@ spv_result_t ValidateImageGather(ValidationState_t& _, if (info.dim != SpvDim2D && info.dim != SpvDimCube && info.dim != SpvDimRect) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Image 'Dim' cannot be Cube"; + << _.VkErrorID(4777) + << "Expected Image 'Dim' to be 2D, Cube, or Rect"; } const uint32_t coord_type = _.GetOperandTypeId(inst, 3); @@ -1486,11 +1518,7 @@ spv_result_t ValidateImageGather(ValidationState_t& _, } else { assert(opcode == SpvOpImageDrefGather || opcode == SpvOpImageSparseDrefGather); - const uint32_t dref_type = _.GetOperandTypeId(inst, 4); - if (!_.IsFloatScalarType(dref_type) || _.GetBitWidth(dref_type) != 32) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected Dref to be of 32-bit float type"; - } + if (spv_result_t result = ValidateImageDref(_, inst, info)) return result; } if (spv_result_t result = @@ -1558,6 +1586,13 @@ spv_result_t ValidateImageRead(ValidationState_t& _, const Instruction* inst) { << " to have 4 components"; } } + + const uint32_t mask = inst->words().size() <= 5 ? 0 : inst->word(5); + if (mask & SpvImageOperandsConstOffsetMask) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "ConstOffset image operand not allowed " + << "in the OpenCL environment."; + } } if (info.dim == SpvDimSubpassData) { @@ -1583,7 +1618,8 @@ spv_result_t ValidateImageRead(ValidationState_t& _, const Instruction* inst) { } } - if (spv_result_t result = ValidateImageCommon(_, inst, info)) return result; + if (spv_result_t result = ValidateImageReadWrite(_, inst, info)) + return result; const uint32_t coord_type = _.GetOperandTypeId(inst, 3); if (!_.IsIntScalarOrVectorType(coord_type)) { @@ -1608,16 +1644,6 @@ spv_result_t ValidateImageRead(ValidationState_t& _, const Instruction* inst) { } } - const uint32_t mask = inst->words().size() <= 5 ? 0 : inst->word(5); - - if (mask & SpvImageOperandsConstOffsetMask) { - if (spvIsOpenCLEnv(_.context()->target_env)) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "ConstOffset image operand not allowed " - << "in the OpenCL environment."; - } - } - if (spv_result_t result = ValidateImageOperands(_, inst, info, /* word_index = */ 6)) return result; @@ -1643,7 +1669,8 @@ spv_result_t ValidateImageWrite(ValidationState_t& _, const Instruction* inst) { << "Image 'Dim' cannot be SubpassData"; } - if (spv_result_t result = ValidateImageCommon(_, inst, info)) return result; + if (spv_result_t result = ValidateImageReadWrite(_, inst, info)) + return result; const uint32_t coord_type = _.GetOperandTypeId(inst, 1); if (!_.IsIntScalarOrVectorType(coord_type)) { @@ -1659,7 +1686,7 @@ spv_result_t ValidateImageWrite(ValidationState_t& _, const Instruction* inst) { << " components, but given only " << actual_coord_size; } - // TODO(atgoo@github.com) The spec doesn't explicitely say what the type + // TODO(atgoo@github.com) The spec doesn't explicitly say what the type // of texel should be. const uint32_t texel_type = _.GetOperandTypeId(inst, 2); if (!_.IsIntScalarOrVectorType(texel_type) && diff --git a/source/val/validate_instruction.cpp b/source/val/validate_instruction.cpp index dad98673..3edf1637 100644 --- a/source/val/validate_instruction.cpp +++ b/source/val/validate_instruction.cpp @@ -297,7 +297,7 @@ spv_result_t VersionCheck(ValidationState_t& _, const Instruction* inst) { } // OpTerminateInvocation is special because it is enabled by Shader - // capability, but also requries a extension and/or version check. + // capability, but also requires an extension and/or version check. const bool capability_check_is_sufficient = inst->opcode() != SpvOpTerminateInvocation; @@ -406,7 +406,7 @@ spv_result_t LimitCheckSwitch(ValidationState_t& _, const Instruction* inst) { // The instruction syntax is as follows: // OpSwitch <selector ID> <Default ID> literal label literal label ... // literal,label pairs come after the first 2 operands. - // It is guaranteed at this point that num_operands is an even numner. + // It is guaranteed at this point that num_operands is an even number. size_t num_pairs = (inst->operands().size() - 2) / 2; const unsigned int num_pairs_limit = _.options()->universal_limits_.max_switch_branches; diff --git a/source/val/validate_interfaces.cpp b/source/val/validate_interfaces.cpp index 7ccb6371..adf2e472 100644 --- a/source/val/validate_interfaces.cpp +++ b/source/val/validate_interfaces.cpp @@ -154,7 +154,7 @@ spv_result_t NumConsumedLocations(ValidationState_t& _, const Instruction* type, // Members cannot have location decorations at this point. if (_.HasDecoration(type->id(), SpvDecorationLocation)) { return _.diag(SPV_ERROR_INVALID_DATA, type) - << "Members cannot be assigned a location"; + << _.VkErrorID(4918) << "Members cannot be assigned a location"; } // Structs consume locations equal to the sum of the locations consumed @@ -326,8 +326,9 @@ spv_result_t GetLocationsForVariable( // Only block-decorated structs don't need a location on the variable. const bool is_block = _.HasDecoration(type_id, SpvDecorationBlock); if (!has_location && !is_block) { + const auto vuid = (type->opcode() == SpvOpTypeStruct) ? 4917 : 4916; return _.diag(SPV_ERROR_INVALID_DATA, variable) - << "Variable must be decorated with a location"; + << _.VkErrorID(vuid) << "Variable must be decorated with a location"; } const std::string storage_class = is_output ? "output" : "input"; @@ -411,7 +412,7 @@ spv_result_t GetLocationsForVariable( auto where = member_locations.find(i - 1); if (where == member_locations.end()) { return _.diag(SPV_ERROR_INVALID_DATA, type) - << "Member index " << i - 1 + << _.VkErrorID(4919) << "Member index " << i - 1 << " is missing a location assignment"; } @@ -476,6 +477,9 @@ spv_result_t ValidateLocations(ValidationState_t& _, const Instruction* entry_point) { // According to Vulkan 14.1 only the following execution models have // locations assigned. + // TODO(dneto): SPV_NV_ray_tracing also uses locations on interface variables, + // in other shader stages. Similarly, the *provisional* version of + // SPV_KHR_ray_tracing did as well, but not the final version. switch (entry_point->GetOperandAs<SpvExecutionModel>(0)) { case SpvExecutionModelVertex: case SpvExecutionModelTessellationControl: diff --git a/source/val/validate_logicals.cpp b/source/val/validate_logicals.cpp index bb35f558..5307988e 100644 --- a/source/val/validate_logicals.cpp +++ b/source/val/validate_logicals.cpp @@ -163,8 +163,7 @@ spv_result_t LogicalsPass(ValidationState_t& _, const Instruction* inst) { switch (type_opcode) { case SpvOpTypePointer: { if (_.addressing_model() == SpvAddressingModelLogical && - !_.features().variable_pointers && - !_.features().variable_pointers_storage_buffer) + !_.features().variable_pointers) return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Using pointers with OpSelect requires capability " << "VariablePointers or VariablePointersStorageBuffer"; diff --git a/source/val/validate_memory.cpp b/source/val/validate_memory.cpp index a7b0f82d..af9da678 100644 --- a/source/val/validate_memory.cpp +++ b/source/val/validate_memory.cpp @@ -315,11 +315,12 @@ spv_result_t CheckMemoryAccess(ValidationState_t& _, const Instruction* inst, SpvStorageClass dst_sc, src_sc; std::tie(dst_sc, src_sc) = GetStorageClass(_, inst); if (inst->operands().size() <= index) { - if (src_sc == SpvStorageClassPhysicalStorageBufferEXT || - dst_sc == SpvStorageClassPhysicalStorageBufferEXT) { + // Cases where lack of some operand is invalid + if (src_sc == SpvStorageClassPhysicalStorageBuffer || + dst_sc == SpvStorageClassPhysicalStorageBuffer) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "Memory accesses with PhysicalStorageBufferEXT must use " - "Aligned."; + << _.VkErrorID(4708) + << "Memory accesses with PhysicalStorageBuffer must use Aligned."; } return SPV_SUCCESS; } @@ -368,7 +369,7 @@ spv_result_t CheckMemoryAccess(ValidationState_t& _, const Instruction* inst, dst_sc != SpvStorageClassCrossWorkgroup && dst_sc != SpvStorageClassGeneric && dst_sc != SpvStorageClassImage && dst_sc != SpvStorageClassStorageBuffer && - dst_sc != SpvStorageClassPhysicalStorageBufferEXT) { + dst_sc != SpvStorageClassPhysicalStorageBuffer) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "NonPrivatePointerKHR requires a pointer in Uniform, " << "Workgroup, CrossWorkgroup, Generic, Image or StorageBuffer " @@ -379,7 +380,7 @@ spv_result_t CheckMemoryAccess(ValidationState_t& _, const Instruction* inst, src_sc != SpvStorageClassCrossWorkgroup && src_sc != SpvStorageClassGeneric && src_sc != SpvStorageClassImage && src_sc != SpvStorageClassStorageBuffer && - src_sc != SpvStorageClassPhysicalStorageBufferEXT) { + src_sc != SpvStorageClassPhysicalStorageBuffer) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "NonPrivatePointerKHR requires a pointer in Uniform, " << "Workgroup, CrossWorkgroup, Generic, Image or StorageBuffer " @@ -388,11 +389,11 @@ spv_result_t CheckMemoryAccess(ValidationState_t& _, const Instruction* inst, } if (!(mask & SpvMemoryAccessAlignedMask)) { - if (src_sc == SpvStorageClassPhysicalStorageBufferEXT || - dst_sc == SpvStorageClassPhysicalStorageBufferEXT) { + if (src_sc == SpvStorageClassPhysicalStorageBuffer || + dst_sc == SpvStorageClassPhysicalStorageBuffer) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "Memory accesses with PhysicalStorageBufferEXT must use " - "Aligned."; + << _.VkErrorID(4708) + << "Memory accesses with PhysicalStorageBuffer must use Aligned."; } } @@ -519,20 +520,21 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { } } - // Vulkan 14.5.1: Check type of PushConstant variables. - // Vulkan 14.5.2: Check type of UniformConstant and Uniform variables. if (spvIsVulkanEnv(_.context()->target_env)) { + // Vulkan Push Constant Interface section: Check type of PushConstant + // variables. if (storage_class == SpvStorageClassPushConstant) { - if (!IsAllowedTypeOrArrayOfSame(_, pointee, {SpvOpTypeStruct})) { + if (pointee->opcode() != SpvOpTypeStruct) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "PushConstant OpVariable <id> '" << _.getIdName(inst->id()) << "' has illegal type.\n" - << "From Vulkan spec, section 14.5.1:\n" - << "Such variables must be typed as OpTypeStruct, " - << "or an array of this type"; + << "From Vulkan spec, Push Constant Interface section:\n" + << "Such variables must be typed as OpTypeStruct"; } } + // Vulkan Descriptor Set Interface: Check type of UniformConstant and + // Uniform variables. if (storage_class == SpvStorageClassUniformConstant) { if (!IsAllowedTypeOrArrayOfSame( _, pointee, @@ -596,23 +598,23 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { } } } - } - // Vulkan Appendix A: Check that if contains initializer, then - // storage class is Output, Private, or Function. - if (inst->operands().size() > 3 && storage_class != SpvStorageClassOutput && - storage_class != SpvStorageClassPrivate && - storage_class != SpvStorageClassFunction) { - if (spvIsVulkanEnv(_.context()->target_env)) { + // Initializers in Vulkan are only allowed in some storage clases + if (inst->operands().size() > 3) { if (storage_class == SpvStorageClassWorkgroup) { auto init_id = inst->GetOperandAs<uint32_t>(3); auto init = _.FindDef(init_id); if (init->opcode() != SpvOpConstantNull) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "Variable initializers in Workgroup storage class are " - "limited to OpConstantNull"; + << _.VkErrorID(4734) << "OpVariable, <id> '" + << _.getIdName(inst->id()) + << "', initializers are limited to OpConstantNull in " + "Workgroup " + "storage class"; } - } else { + } else if (storage_class != SpvStorageClassOutput && + storage_class != SpvStorageClassPrivate && + storage_class != SpvStorageClassFunction) { return _.diag(SPV_ERROR_INVALID_ID, inst) << _.VkErrorID(4651) << "OpVariable, <id> '" << _.getIdName(inst->id()) @@ -627,9 +629,9 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { } } - if (storage_class == SpvStorageClassPhysicalStorageBufferEXT) { + if (storage_class == SpvStorageClassPhysicalStorageBuffer) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "PhysicalStorageBufferEXT must not be used with OpVariable."; + << "PhysicalStorageBuffer must not be used with OpVariable."; } auto pointee_base = pointee; @@ -638,23 +640,23 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { } if (pointee_base->opcode() == SpvOpTypePointer) { if (pointee_base->GetOperandAs<uint32_t>(1u) == - SpvStorageClassPhysicalStorageBufferEXT) { - // check for AliasedPointerEXT/RestrictPointerEXT + SpvStorageClassPhysicalStorageBuffer) { + // check for AliasedPointer/RestrictPointer bool foundAliased = - _.HasDecoration(inst->id(), SpvDecorationAliasedPointerEXT); + _.HasDecoration(inst->id(), SpvDecorationAliasedPointer); bool foundRestrict = - _.HasDecoration(inst->id(), SpvDecorationRestrictPointerEXT); + _.HasDecoration(inst->id(), SpvDecorationRestrictPointer); if (!foundAliased && !foundRestrict) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "OpVariable " << inst->id() - << ": expected AliasedPointerEXT or RestrictPointerEXT for " - << "PhysicalStorageBufferEXT pointer."; + << ": expected AliasedPointer or RestrictPointer for " + << "PhysicalStorageBuffer pointer."; } if (foundAliased && foundRestrict) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "OpVariable " << inst->id() - << ": can't specify both AliasedPointerEXT and " - << "RestrictPointerEXT for PhysicalStorageBufferEXT pointer."; + << ": can't specify both AliasedPointer and " + << "RestrictPointer for PhysicalStorageBuffer pointer."; } } } @@ -667,7 +669,8 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { if (value_type && value_type->opcode() == SpvOpTypeRuntimeArray) { if (!_.HasCapability(SpvCapabilityRuntimeDescriptorArrayEXT)) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpVariable, <id> '" << _.getIdName(inst->id()) + << _.VkErrorID(4680) << "OpVariable, <id> '" + << _.getIdName(inst->id()) << "', is attempting to create memory for an illegal type, " << "OpTypeRuntimeArray.\nFor Vulkan OpTypeRuntimeArray can only " << "appear as the final member of an OpTypeStruct, thus cannot " @@ -679,6 +682,7 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { storage_class != SpvStorageClassUniform && storage_class != SpvStorageClassUniformConstant) { return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(4680) << "For Vulkan with RuntimeDescriptorArrayEXT, a variable " << "containing OpTypeRuntimeArray must have storage class of " << "StorageBuffer, Uniform, or UniformConstant."; @@ -692,25 +696,30 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { // as BufferBlock. if (value_type && value_type->opcode() == SpvOpTypeStruct) { if (DoesStructContainRTA(_, value_type)) { - if (storage_class == SpvStorageClassStorageBuffer) { + if (storage_class == SpvStorageClassStorageBuffer || + storage_class == SpvStorageClassPhysicalStorageBuffer) { if (!_.HasDecoration(value_id, SpvDecorationBlock)) { return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(4680) << "For Vulkan, an OpTypeStruct variable containing an " << "OpTypeRuntimeArray must be decorated with Block if it " - << "has storage class StorageBuffer."; + << "has storage class StorageBuffer or " + "PhysicalStorageBuffer."; } } else if (storage_class == SpvStorageClassUniform) { if (!_.HasDecoration(value_id, SpvDecorationBufferBlock)) { return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(4680) << "For Vulkan, an OpTypeStruct variable containing an " << "OpTypeRuntimeArray must be decorated with BufferBlock " << "if it has storage class Uniform."; } } else { return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(4680) << "For Vulkan, OpTypeStruct variables containing " << "OpTypeRuntimeArray must have storage class of " - << "StorageBuffer or Uniform."; + << "StorageBuffer, PhysicalStorageBuffer, or Uniform."; } } } @@ -745,7 +754,7 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { SPV_OPERAND_TYPE_STORAGE_CLASS, storage_class); switch (storage_class) { case SpvStorageClassStorageBuffer: - case SpvStorageClassPhysicalStorageBufferEXT: + case SpvStorageClassPhysicalStorageBuffer: if (!_.HasCapability(SpvCapabilityStorageBuffer16BitAccess)) { storage_class_ok = false; } @@ -807,7 +816,7 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { SPV_OPERAND_TYPE_STORAGE_CLASS, storage_class); switch (storage_class) { case SpvStorageClassStorageBuffer: - case SpvStorageClassPhysicalStorageBufferEXT: + case SpvStorageClassPhysicalStorageBuffer: if (!_.HasCapability(SpvCapabilityStorageBuffer8BitAccess)) { storage_class_ok = false; } @@ -861,17 +870,14 @@ spv_result_t ValidateLoad(ValidationState_t& _, const Instruction* inst) { << "' is not defined."; } - const bool uses_variable_pointers = - _.features().variable_pointers || - _.features().variable_pointers_storage_buffer; const auto pointer_index = 2; const auto pointer_id = inst->GetOperandAs<uint32_t>(pointer_index); const auto pointer = _.FindDef(pointer_id); if (!pointer || ((_.addressing_model() == SpvAddressingModelLogical) && - ((!uses_variable_pointers && + ((!_.features().variable_pointers && !spvOpcodeReturnsLogicalPointer(pointer->opcode())) || - (uses_variable_pointers && + (_.features().variable_pointers && !spvOpcodeReturnsLogicalVariablePointer(pointer->opcode()))))) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "OpLoad Pointer <id> '" << _.getIdName(pointer_id) @@ -917,17 +923,14 @@ spv_result_t ValidateLoad(ValidationState_t& _, const Instruction* inst) { } spv_result_t ValidateStore(ValidationState_t& _, const Instruction* inst) { - const bool uses_variable_pointer = - _.features().variable_pointers || - _.features().variable_pointers_storage_buffer; const auto pointer_index = 0; const auto pointer_id = inst->GetOperandAs<uint32_t>(pointer_index); const auto pointer = _.FindDef(pointer_id); if (!pointer || (_.addressing_model() == SpvAddressingModelLogical && - ((!uses_variable_pointer && + ((!_.features().variable_pointers && !spvOpcodeReturnsLogicalPointer(pointer->opcode())) || - (uses_variable_pointer && + (_.features().variable_pointers && !spvOpcodeReturnsLogicalVariablePointer(pointer->opcode()))))) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "OpStore Pointer <id> '" << _.getIdName(pointer_id) @@ -1138,8 +1141,6 @@ spv_result_t ValidateCopyMemory(ValidationState_t& _, const Instruction* inst) { << "'s type does not match Source <id> '" << _.getIdName(source_type->id()) << "'s type."; } - - if (auto error = CheckMemoryAccess(_, inst, 2)) return error; } else { const auto size_id = inst->GetOperandAs<uint32_t>(2); const auto size = _.FindDef(size_id); @@ -1182,8 +1183,6 @@ spv_result_t ValidateCopyMemory(ValidationState_t& _, const Instruction* inst) { // Cannot infer any other opcodes. break; } - - if (auto error = CheckMemoryAccess(_, inst, 3)) return error; } if (auto error = ValidateCopyMemoryMemoryAccess(_, inst)) return error; @@ -1357,8 +1356,7 @@ spv_result_t ValidateAccessChain(ValidationState_t& _, spv_result_t ValidatePtrAccessChain(ValidationState_t& _, const Instruction* inst) { if (_.addressing_model() == SpvAddressingModelLogical) { - if (!_.features().variable_pointers && - !_.features().variable_pointers_storage_buffer) { + if (!_.features().variable_pointers) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Generating variable pointers requires capability " << "VariablePointers or VariablePointersStorageBuffer"; @@ -1411,7 +1409,7 @@ spv_result_t ValidateArrayLength(ValidationState_t& state, << state.getIdName(inst->id()) << "' must be an OpTypeRuntimeArray."; } - // The array member must the the index of the last element (the run time + // The array member must the index of the last element (the run time // array). if (inst->GetOperandAs<uint32_t>(3) != num_of_members - 1) { return state.diag(SPV_ERROR_INVALID_ID, inst) @@ -1476,18 +1474,15 @@ spv_result_t ValidateCooperativeMatrixLoadStoreNV(ValidationState_t& _, } } - const bool uses_variable_pointers = - _.features().variable_pointers || - _.features().variable_pointers_storage_buffer; const auto pointer_index = (inst->opcode() == SpvOpCooperativeMatrixLoadNV) ? 2u : 0u; const auto pointer_id = inst->GetOperandAs<uint32_t>(pointer_index); const auto pointer = _.FindDef(pointer_id); if (!pointer || ((_.addressing_model() == SpvAddressingModelLogical) && - ((!uses_variable_pointers && + ((!_.features().variable_pointers && !spvOpcodeReturnsLogicalPointer(pointer->opcode())) || - (uses_variable_pointers && + (_.features().variable_pointers && !spvOpcodeReturnsLogicalVariablePointer(pointer->opcode()))))) { return _.diag(SPV_ERROR_INVALID_ID, inst) << opname << " Pointer <id> '" << _.getIdName(pointer_id) @@ -1508,7 +1503,7 @@ spv_result_t ValidateCooperativeMatrixLoadStoreNV(ValidationState_t& _, if (storage_class != SpvStorageClassWorkgroup && storage_class != SpvStorageClassStorageBuffer && - storage_class != SpvStorageClassPhysicalStorageBufferEXT) { + storage_class != SpvStorageClassPhysicalStorageBuffer) { return _.diag(SPV_ERROR_INVALID_ID, inst) << opname << " storage class for pointer type <id> '" << _.getIdName(pointer_type_id) @@ -1559,10 +1554,10 @@ spv_result_t ValidateCooperativeMatrixLoadStoreNV(ValidationState_t& _, spv_result_t ValidatePtrComparison(ValidationState_t& _, const Instruction* inst) { if (_.addressing_model() == SpvAddressingModelLogical && - !_.features().variable_pointers_storage_buffer) { + !_.features().variable_pointers) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "Instruction cannot be used without a variable pointers " - "capability"; + << "Instruction cannot for logical addressing model be used without " + "a variable pointers capability"; } const auto result_type = _.FindDef(inst->type_id()); @@ -1597,7 +1592,8 @@ spv_result_t ValidatePtrComparison(ValidationState_t& _, << "Invalid pointer storage class"; } - if (sc == SpvStorageClassWorkgroup && !_.features().variable_pointers) { + if (sc == SpvStorageClassWorkgroup && + !_.HasCapability(SpvCapabilityVariablePointers)) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "Workgroup storage class pointer requires VariablePointers " "capability to be specified"; diff --git a/source/val/validate_scopes.cpp b/source/val/validate_scopes.cpp index 29ba5831..1c5f70a3 100644 --- a/source/val/validate_scopes.cpp +++ b/source/val/validate_scopes.cpp @@ -225,7 +225,7 @@ spv_result_t ValidateMemoryScope(ValidationState_t& _, const Instruction* inst, << _.VkErrorID(4638) << spvOpcodeString(opcode) << ": in Vulkan environment, Memory Scope cannot be CrossDevice"; } - // Vulkan 1.0 specifc rules + // Vulkan 1.0 specific rules if (_.context()->target_env == SPV_ENV_VULKAN_1_0 && value != SpvScopeDevice && value != SpvScopeWorkgroup && value != SpvScopeInvocation) { @@ -234,7 +234,7 @@ spv_result_t ValidateMemoryScope(ValidationState_t& _, const Instruction* inst, << ": in Vulkan 1.0 environment Memory Scope is limited to " << "Device, Workgroup and Invocation"; } - // Vulkan 1.1 specifc rules + // Vulkan 1.1 specific rules if ((_.context()->target_env == SPV_ENV_VULKAN_1_1 || _.context()->target_env == SPV_ENV_VULKAN_1_2) && value != SpvScopeDevice && value != SpvScopeWorkgroup && diff --git a/source/val/validate_type.cpp b/source/val/validate_type.cpp index 4376b52c..2aded616 100644 --- a/source/val/validate_type.cpp +++ b/source/val/validate_type.cpp @@ -228,8 +228,8 @@ spv_result_t ValidateTypeArray(ValidationState_t& _, const Instruction* inst) { if (spvIsVulkanEnv(_.context()->target_env) && element_type->opcode() == SpvOpTypeRuntimeArray) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpTypeArray Element Type <id> '" << _.getIdName(element_type_id) - << "' is not valid in " + << _.VkErrorID(4680) << "OpTypeArray Element Type <id> '" + << _.getIdName(element_type_id) << "' is not valid in " << spvLogStringForEnv(_.context()->target_env) << " environments."; } @@ -298,7 +298,7 @@ spv_result_t ValidateTypeRuntimeArray(ValidationState_t& _, if (spvIsVulkanEnv(_.context()->target_env) && element_type->opcode() == SpvOpTypeRuntimeArray) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "OpTypeRuntimeArray Element Type <id> '" + << _.VkErrorID(4680) << "OpTypeRuntimeArray Element Type <id> '" << _.getIdName(element_id) << "' is not valid in " << spvLogStringForEnv(_.context()->target_env) << " environments."; } @@ -373,7 +373,8 @@ spv_result_t ValidateTypeStruct(ValidationState_t& _, const Instruction* inst) { member_type_index == inst->operands().size() - 1; if (!is_last_member) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "In " << spvLogStringForEnv(_.context()->target_env) + << _.VkErrorID(4680) << "In " + << spvLogStringForEnv(_.context()->target_env) << ", OpTypeRuntimeArray must only be used for the last member " "of an OpTypeStruct"; } diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp index 8d1a0d3f..9aa6c63e 100644 --- a/source/val/validation_state.cpp +++ b/source/val/validation_state.cpp @@ -175,8 +175,18 @@ ValidationState_t::ValidationState_t(const spv_const_context ctx, } } - // LocalSizeId is always allowed in non-Vulkan environments. - features_.env_allow_localsizeid = !spvIsVulkanEnv(env); + // LocalSizeId is only disallowed prior to Vulkan 1.3 without maintenance4. + switch (env) { + case SPV_ENV_VULKAN_1_0: + case SPV_ENV_VULKAN_1_1: + case SPV_ENV_VULKAN_1_1_SPIRV_1_4: + case SPV_ENV_VULKAN_1_2: + features_.env_allow_localsizeid = false; + break; + default: + features_.env_allow_localsizeid = true; + break; + } // Only attempt to count if we have words, otherwise let the other validation // fail and generate an error. @@ -382,13 +392,19 @@ void ValidationState_t::RegisterCapability(SpvCapability cap) { features_.free_fp_rounding_mode = true; break; case SpvCapabilityVariablePointers: - features_.variable_pointers = true; - features_.variable_pointers_storage_buffer = true; - break; case SpvCapabilityVariablePointersStorageBuffer: - features_.variable_pointers_storage_buffer = true; + features_.variable_pointers = true; break; default: + // TODO(dneto): For now don't validate SPV_NV_ray_tracing, which uses + // capability SpvCapabilityRayTracingNV. + // SpvCapabilityRayTracingProvisionalKHR would need the same treatment. + // One of the differences going from SPV_KHR_ray_tracing from + // provisional to final spec was the provisional spec uses Locations + // for variables in certain storage classes, just like the + // SPV_NV_ray_tracing extension. So it mimics the NVIDIA extension. + // The final SPV_KHR_ray_tracing uses a different capability token + // number, so it doesn't fall into this case. break; } } @@ -441,7 +457,7 @@ void ValidationState_t::set_addressing_model(SpvAddressingModel am) { default: // fall through case SpvAddressingModelPhysical64: - case SpvAddressingModelPhysicalStorageBuffer64EXT: + case SpvAddressingModelPhysicalStorageBuffer64: pointer_size_and_alignment_ = 8; break; } @@ -479,7 +495,7 @@ spv_result_t ValidationState_t::RegisterFunctionEnd() { "inside of another function"); assert(in_block() == false && "RegisterFunctionParameter can only be called when parsing the binary " - "ouside of a block"); + "outside of a block"); current_function().RegisterFunctionEnd(); in_function_ = false; return SPV_SUCCESS; @@ -497,15 +513,13 @@ void ValidationState_t::RegisterDebugInstruction(const Instruction* inst) { switch (inst->opcode()) { case SpvOpName: { const auto target = inst->GetOperandAs<uint32_t>(0); - const auto* str = reinterpret_cast<const char*>(inst->words().data() + - inst->operand(1).offset); + const std::string str = inst->GetOperandAs<std::string>(1); AssignNameToId(target, str); break; } case SpvOpMemberName: { const auto target = inst->GetOperandAs<uint32_t>(0); - const auto* str = reinterpret_cast<const char*>(inst->words().data() + - inst->operand(2).offset); + const std::string str = inst->GetOperandAs<std::string>(2); AssignNameToId(target, str); break; } @@ -524,7 +538,7 @@ void ValidationState_t::RegisterInstruction(Instruction* inst) { if (inst->id()) all_definitions_.insert(std::make_pair(inst->id(), inst)); // Some validation checks are easier by getting all the consumers - for (uint16_t i = 0; i < inst->operands().size(); ++i) { + for (size_t i = 0; i < inst->operands().size(); ++i) { const spv_parsed_operand_t& operand = inst->operand(i); if ((SPV_OPERAND_TYPE_ID == operand.type) || (SPV_OPERAND_TYPE_TYPE_ID == operand.type)) { @@ -593,7 +607,7 @@ void ValidationState_t::RegisterStorageClassConsumer( if (message) { *message = errorVUID + - "in Vulkan evironment, Output Storage Class must not be " + "in Vulkan environment, Output Storage Class must not be " "used in GLCompute, RayGenerationKHR, IntersectionKHR, " "AnyHitKHR, ClosestHitKHR, MissKHR, or CallableKHR " "execution models"; @@ -615,7 +629,7 @@ void ValidationState_t::RegisterStorageClassConsumer( if (message) { *message = errorVUID + - "in Vulkan evironment, Workgroup Storage Class is limited " + "in Vulkan environment, Workgroup Storage Class is limited " "to MeshNV, TaskNV, and GLCompute execution model"; } return false; @@ -1390,7 +1404,7 @@ std::string ValidationState_t::VkErrorID(uint32_t id, return ""; } - // This large switch case is only searched when an error has occured. + // This large switch case is only searched when an error has occurred. // If an id is changed, the old case must be modified or removed. Each string // here is interpreted as being "implemented" @@ -1845,6 +1859,8 @@ std::string ValidationState_t::VkErrorID(uint32_t id, return VUID_WRAP(VUID-StandaloneSpirv-FPRoundingMode-04675); case 4677: return VUID_WRAP(VUID-StandaloneSpirv-Invariant-04677); + case 4680: + return VUID_WRAP( VUID-StandaloneSpirv-OpTypeRuntimeArray-04680); case 4682: return VUID_WRAP(VUID-StandaloneSpirv-OpControlBarrier-04682); case 6426: @@ -1853,6 +1869,8 @@ std::string ValidationState_t::VkErrorID(uint32_t id, return VUID_WRAP(VUID-StandaloneSpirv-OpGroupNonUniformBallotBitCount-04685); case 4686: return VUID_WRAP(VUID-StandaloneSpirv-None-04686); + case 4708: + return VUID_WRAP(VUID-StandaloneSpirv-PhysicalStorageBuffer64-04708); case 4710: return VUID_WRAP(VUID-StandaloneSpirv-PhysicalStorageBuffer64-04710); case 4711: @@ -1865,8 +1883,42 @@ std::string ValidationState_t::VkErrorID(uint32_t id, return VUID_WRAP(VUID-StandaloneSpirv-OpMemoryBarrier-04732); case 4733: return VUID_WRAP(VUID-StandaloneSpirv-OpMemoryBarrier-04733); + case 4734: + return VUID_WRAP(VUID-StandaloneSpirv-OpVariable-04734); + case 4777: + return VUID_WRAP(VUID-StandaloneSpirv-OpImage-04777); case 4780: return VUID_WRAP(VUID-StandaloneSpirv-Result-04780); + case 4781: + return VUID_WRAP(VUID-StandaloneSpirv-Base-04781); + case 4915: + return VUID_WRAP(VUID-StandaloneSpirv-Location-04915); + case 4916: + return VUID_WRAP(VUID-StandaloneSpirv-Location-04916); + case 4917: + return VUID_WRAP(VUID-StandaloneSpirv-Location-04917); + case 4918: + return VUID_WRAP(VUID-StandaloneSpirv-Location-04918); + case 4919: + return VUID_WRAP(VUID-StandaloneSpirv-Location-04919); + case 6214: + return VUID_WRAP(VUID-StandaloneSpirv-OpTypeImage-06214); + case 6491: + return VUID_WRAP(VUID-StandaloneSpirv-DescriptorSet-06491); + case 6671: + return VUID_WRAP(VUID-StandaloneSpirv-OpTypeSampledImage-06671); + case 6672: + return VUID_WRAP(VUID-StandaloneSpirv-Location-06672); + case 6674: + return VUID_WRAP(VUID-StandaloneSpirv-OpEntryPoint-06674); + case 6675: + return VUID_WRAP(VUID-StandaloneSpirv-PushConstant-06675); + case 6676: + return VUID_WRAP(VUID-StandaloneSpirv-Uniform-06676); + case 6677: + return VUID_WRAP(VUID-StandaloneSpirv-UniformConstant-06677); + case 6678: + return VUID_WRAP(VUID-StandaloneSpirv-InputAttachmentIndex-06678); default: return ""; // unknown id } @@ -1874,4 +1926,4 @@ std::string ValidationState_t::VkErrorID(uint32_t id, } } // namespace val -} // namespace spvtools +} // namespace spvtools
\ No newline at end of file diff --git a/source/val/validation_state.h b/source/val/validation_state.h index 2ddfa4a9..4888840b 100644 --- a/source/val/validation_state.h +++ b/source/val/validation_state.h @@ -67,14 +67,12 @@ class ValidationState_t { bool declare_int16_type = false; // Allow OpTypeInt with 16 bit width? bool declare_float16_type = false; // Allow OpTypeFloat with 16 bit width? bool free_fp_rounding_mode = false; // Allow the FPRoundingMode decoration - // and its vaules to be used without + // and its values to be used without // requiring any capability - // Allow functionalities enabled by VariablePointers capability. + // Allow functionalities enabled by VariablePointers or + // VariablePointersStorageBuffer capability. bool variable_pointers = false; - // Allow functionalities enabled by VariablePointersStorageBuffer - // capability. - bool variable_pointers_storage_buffer = false; // Permit group oerations Reduce, InclusiveScan, ExclusiveScan bool group_ops_reduce_and_scans = false; @@ -797,7 +795,7 @@ class ValidationState_t { /// IDs that are entry points, ie, arguments to OpEntryPoint. std::vector<uint32_t> entry_points_; - /// Maps an entry point id to its desciptions. + /// Maps an entry point id to its descriptions. std::unordered_map<uint32_t, std::vector<EntryPointDescription>> entry_point_descriptions_; @@ -844,7 +842,7 @@ class ValidationState_t { // have the same pointer size (for physical pointer types). uint32_t pointer_size_and_alignment_; - /// NOTE: See correspoding getter functions + /// NOTE: See corresponding getter functions bool in_function_; /// The state of optional features. These are determined by capabilities diff --git a/source/wasm/spirv-tools.cpp b/source/wasm/spirv-tools.cpp index 90407f32..33f2f05f 100644 --- a/source/wasm/spirv-tools.cpp +++ b/source/wasm/spirv-tools.cpp @@ -78,7 +78,8 @@ EMSCRIPTEN_BINDINGS(my_module) { constant("SPV_ENV_VULKAN_1_1_SPIRV_1_4", static_cast<uint32_t>(SPV_ENV_VULKAN_1_1_SPIRV_1_4)); constant("SPV_ENV_UNIVERSAL_1_5", static_cast<uint32_t>(SPV_ENV_UNIVERSAL_1_5)); constant("SPV_ENV_VULKAN_1_2", static_cast<uint32_t>(SPV_ENV_VULKAN_1_2)); - + constant("SPV_ENV_UNIVERSAL_1_6", + static_cast<uint32_t>(SPV_ENV_UNIVERSAL_1_6)); constant("SPV_BINARY_TO_TEXT_OPTION_NONE", static_cast<uint32_t>(SPV_BINARY_TO_TEXT_OPTION_NONE)); constant("SPV_BINARY_TO_TEXT_OPTION_PRINT", static_cast<uint32_t>(SPV_BINARY_TO_TEXT_OPTION_PRINT)); @@ -90,4 +91,4 @@ EMSCRIPTEN_BINDINGS(my_module) { constant("SPV_TEXT_TO_BINARY_OPTION_NONE", static_cast<uint32_t>(SPV_TEXT_TO_BINARY_OPTION_NONE)); constant("SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS", static_cast<uint32_t>(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS)); -}
\ No newline at end of file +} diff --git a/source/wasm/spirv-tools.d.ts b/source/wasm/spirv-tools.d.ts index 9c197973..c06bdf18 100644 --- a/source/wasm/spirv-tools.d.ts +++ b/source/wasm/spirv-tools.d.ts @@ -40,6 +40,7 @@ declare interface SpirvTools { SPV_ENV_VULKAN_1_1_SPIRV_1_4: number; SPV_ENV_UNIVERSAL_1_5: number; SPV_ENV_VULKAN_1_2: number; + SPV_ENV_UNIVERSAL_1_6: number; SPV_TEXT_TO_BINARY_OPTION_NONE: number; SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS: number; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e88df04d..4ca8ef8f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -185,6 +185,7 @@ add_spvtools_unittest( endif() +add_subdirectory(diff) add_subdirectory(link) add_subdirectory(lint) add_subdirectory(opt) diff --git a/test/binary_header_get_test.cpp b/test/binary_header_get_test.cpp index 3ce0b63a..f94f0c1a 100644 --- a/test/binary_header_get_test.cpp +++ b/test/binary_header_get_test.cpp @@ -51,8 +51,8 @@ TEST_F(BinaryHeaderGet, Default) { ASSERT_EQ(SPV_SUCCESS, spvBinaryHeaderGet(&const_bin, endian, &header)); ASSERT_EQ(static_cast<uint32_t>(SpvMagicNumber), header.magic); - // Expect SPIRV-Headers updated to SPIR-V 1.5. - ASSERT_EQ(0x00010500u, header.version); + // Expect SPIRV-Headers updated to SPIR-V 1.6. + ASSERT_EQ(0x00010600u, header.version); ASSERT_EQ(static_cast<uint32_t>(SPV_GENERATOR_CODEPLAY), header.generator); ASSERT_EQ(1u, header.bound); ASSERT_EQ(0u, header.schema); diff --git a/test/binary_parse_test.cpp b/test/binary_parse_test.cpp index 9a13f22c..f0810a35 100644 --- a/test/binary_parse_test.cpp +++ b/test/binary_parse_test.cpp @@ -203,16 +203,7 @@ class BinaryParseTest : public spvtest::TextToBinaryTestBase<::testing::Test> { void Parse(const SpirvVector& words, spv_result_t expected_result, bool flip_words = false) { SpirvVector flipped_words(words); - SCOPED_TRACE(flip_words ? "Flipped Endianness" : "Normal Endianness"); - if (flip_words) { - std::transform(flipped_words.begin(), flipped_words.end(), - flipped_words.begin(), [](const uint32_t raw_word) { - return spvFixWord(raw_word, - I32_ENDIAN_HOST == I32_ENDIAN_BIG - ? SPV_ENDIANNESS_LITTLE - : SPV_ENDIANNESS_BIG); - }); - } + MaybeFlipWords(flip_words, flipped_words.begin(), flipped_words.end()); EXPECT_EQ(expected_result, spvBinaryParse(ScopedContext().context, &client_, flipped_words.data(), flipped_words.size(), @@ -486,27 +477,27 @@ TEST_F(BinaryParseTest, EarlyReturnWithTwoPassingCallbacks) { } TEST_F(BinaryParseTest, InstructionWithStringOperand) { - const std::string str = - "the future is already here, it's just not evenly distributed"; - const auto str_words = MakeVector(str); - const auto instruction = MakeInstruction(SpvOpName, {99}, str_words); - const auto words = Concatenate({ExpectedHeaderForBound(100), instruction}); - InSequence calls_expected_in_specific_order; - EXPECT_HEADER(100).WillOnce(Return(SPV_SUCCESS)); - const auto operands = std::vector<spv_parsed_operand_t>{ - MakeSimpleOperand(1, SPV_OPERAND_TYPE_ID), - MakeLiteralStringOperand(2, static_cast<uint16_t>(str_words.size()))}; - EXPECT_CALL(client_, - Instruction(ParsedInstruction(spv_parsed_instruction_t{ - instruction.data(), static_cast<uint16_t>(instruction.size()), - SpvOpName, SPV_EXT_INST_TYPE_NONE, 0 /*type id*/, - 0 /* No result id for OpName*/, operands.data(), - static_cast<uint16_t>(operands.size())}))) - .WillOnce(Return(SPV_SUCCESS)); - // Since we are actually checking the output, don't test the - // endian-swapped version. - Parse(words, SPV_SUCCESS, false); - EXPECT_EQ(nullptr, diagnostic_); + for (bool endian_swap : kSwapEndians) { + const std::string str = + "the future is already here, it's just not evenly distributed"; + const auto str_words = MakeVector(str); + const auto instruction = MakeInstruction(SpvOpName, {99}, str_words); + const auto words = Concatenate({ExpectedHeaderForBound(100), instruction}); + InSequence calls_expected_in_specific_order; + EXPECT_HEADER(100).WillOnce(Return(SPV_SUCCESS)); + const auto operands = std::vector<spv_parsed_operand_t>{ + MakeSimpleOperand(1, SPV_OPERAND_TYPE_ID), + MakeLiteralStringOperand(2, static_cast<uint16_t>(str_words.size()))}; + EXPECT_CALL(client_, Instruction(ParsedInstruction(spv_parsed_instruction_t{ + instruction.data(), + static_cast<uint16_t>(instruction.size()), + SpvOpName, SPV_EXT_INST_TYPE_NONE, 0 /*type id*/, + 0 /* No result id for OpName*/, operands.data(), + static_cast<uint16_t>(operands.size())}))) + .WillOnce(Return(SPV_SUCCESS)); + Parse(words, SPV_SUCCESS, endian_swap); + EXPECT_EQ(nullptr, diagnostic_); + } } // Checks for non-zero values for the result_id and ext_inst_type members @@ -613,7 +604,7 @@ INSTANTIATE_TEST_SUITE_P( MakeInstruction(SpvOpNop, {42})}), "Invalid instruction OpNop starting at word 5: expected " "no more operands after 1 words, but stated word count is 2."}, - // Supply several more unexpectd words. + // Supply several more unexpected words. {Concatenate({ExpectedHeaderForBound(1), MakeInstruction(SpvOpNop, {42, 43, 44, 45, 46, 47})}), "Invalid instruction OpNop starting at word 5: expected " diff --git a/test/binary_to_text.literal_test.cpp b/test/binary_to_text.literal_test.cpp index 02daac76..5956984b 100644 --- a/test/binary_to_text.literal_test.cpp +++ b/test/binary_to_text.literal_test.cpp @@ -27,8 +27,15 @@ using ::testing::Eq; using RoundTripLiteralsTest = spvtest::TextToBinaryTestBase<::testing::TestWithParam<std::string>>; +static const bool kSwapEndians[] = {false, true}; + TEST_P(RoundTripLiteralsTest, Sample) { - EXPECT_THAT(EncodeAndDecodeSuccessfully(GetParam()), Eq(GetParam())); + for (bool endian_swap : kSwapEndians) { + EXPECT_THAT( + EncodeAndDecodeSuccessfully(GetParam(), SPV_BINARY_TO_TEXT_OPTION_NONE, + SPV_ENV_UNIVERSAL_1_0, endian_swap), + Eq(GetParam())); + } } // clang-format off @@ -58,8 +65,12 @@ using RoundTripSpecialCaseLiteralsTest = spvtest::TextToBinaryTestBase< // Test case where the generated disassembly is not the same as the // assembly passed in. TEST_P(RoundTripSpecialCaseLiteralsTest, Sample) { - EXPECT_THAT(EncodeAndDecodeSuccessfully(std::get<0>(GetParam())), - Eq(std::get<1>(GetParam()))); + for (bool endian_swap : kSwapEndians) { + EXPECT_THAT(EncodeAndDecodeSuccessfully(std::get<0>(GetParam()), + SPV_BINARY_TO_TEXT_OPTION_NONE, + SPV_ENV_UNIVERSAL_1_0, endian_swap), + Eq(std::get<1>(GetParam()))); + } } // clang-format off diff --git a/test/c_interface_test.cpp b/test/c_interface_test.cpp index 841bb2c6..4424d7f5 100644 --- a/test/c_interface_test.cpp +++ b/test/c_interface_test.cpp @@ -117,12 +117,15 @@ TEST(CInterface, SpecifyConsumerNullDiagnosticForAssembling) { const spv_position_t& position, const char* message) { ++invocation; EXPECT_EQ(SPV_MSG_ERROR, level); - // The error happens at scanning the begining of second line. + // The error happens at scanning the beginning of second line. EXPECT_STREQ("input", source); EXPECT_EQ(1u, position.line); EXPECT_EQ(0u, position.column); EXPECT_EQ(12u, position.index); - EXPECT_STREQ("Expected operand, found end of stream.", message); + EXPECT_STREQ( + "Expected operand for OpName instruction, but found the end of the " + "stream.", + message); }); spv_binary binary = nullptr; @@ -228,7 +231,10 @@ TEST(CInterface, SpecifyConsumerSpecifyDiagnosticForAssembling) { spvTextToBinary(context, input_text, sizeof(input_text), &binary, &diagnostic)); EXPECT_EQ(0, invocation); // Consumer should not be invoked at all. - EXPECT_STREQ("Expected operand, found end of stream.", diagnostic->error); + EXPECT_STREQ( + "Expected operand for OpName instruction, but found the end of the " + "stream.", + diagnostic->error); spvDiagnosticDestroy(diagnostic); spvBinaryDestroy(binary); diff --git a/test/diff/CMakeLists.txt b/test/diff/CMakeLists.txt new file mode 100644 index 00000000..811805bd --- /dev/null +++ b/test/diff/CMakeLists.txt @@ -0,0 +1,26 @@ +# Copyright (c) 2022 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(diff_files/diff_test_files_autogen.cmake) + +add_spvtools_unittest(TARGET lcs + SRCS lcs_test.cpp + LIBS SPIRV-Tools-diff +) + +add_spvtools_unittest(TARGET diff + SRCS diff_test.cpp diff_test_utils.h diff_test_utils.cpp + ${DIFF_TEST_FILES} ${spirv-tools_SOURCE_DIR}/tools/util/cli_consumer.cpp + LIBS SPIRV-Tools-diff +) diff --git a/test/diff/diff_files/.gitignore b/test/diff/diff_files/.gitignore new file mode 100644 index 00000000..727526ef --- /dev/null +++ b/test/diff/diff_files/.gitignore @@ -0,0 +1,3 @@ +# To aid debugging no-dbg variants, the temporary files used to strip debug information are placed +# in a hidden directory and aren't removed after generation. +.no_dbg/ diff --git a/test/diff/diff_files/OpExtInst_in_dst_only_autogen.cpp b/test/diff/diff_files/OpExtInst_in_dst_only_autogen.cpp new file mode 100644 index 00000000..ce899edd --- /dev/null +++ b/test/diff/diff_files/OpExtInst_in_dst_only_autogen.cpp @@ -0,0 +1,242 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Tests a diff where the src shader doesn't have OpExtImport while the +// dst shader does (and uses OpExtInst). This test ensures that when matching, +// the OpExtImport instruction from the correct module is referenced. +constexpr char kSrc[] = R"( OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "color" + OpName %11 "v" + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 + %13 = OpCompositeConstruct %7 %12 %12 %12 %12 + OpStore %9 %13 + OpReturn + OpFunctionEnd +)"; +constexpr char kDst[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "color" + OpName %11 "v" + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + OpDecorate %14 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 + %13 = OpExtInst %6 %1 Log2 %12 + %14 = OpCompositeConstruct %7 %13 %13 %13 %13 + OpStore %9 %14 + OpReturn + OpFunctionEnd + +)"; + +TEST(DiffTest, OpextinstInDstOnly) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 14 ++; Bound: 16 + ; Schema: 0 + OpCapability Shader ++%14 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "color" + OpName %11 "v" + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + OpDecorate %12 RelaxedPrecision ++OpDecorate %15 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 ++%15 = OpExtInst %6 %14 Log2 %12 +-%13 = OpCompositeConstruct %7 %12 %12 %12 %12 ++%13 = OpCompositeConstruct %7 %15 %15 %15 %15 + OpStore %9 %13 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, OpextinstInDstOnlyNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 + %13 = OpCompositeConstruct %7 %12 %12 %12 %12 + OpStore %9 %13 + OpReturn + OpFunctionEnd + +)"; + constexpr char kDstNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + OpDecorate %14 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 + %13 = OpExtInst %6 %1 Log2 %12 + %14 = OpCompositeConstruct %7 %13 %13 %13 %13 + OpStore %9 %14 + OpReturn + OpFunctionEnd + +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 14 ++; Bound: 16 + ; Schema: 0 + OpCapability Shader ++%14 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + OpDecorate %12 RelaxedPrecision ++OpDecorate %15 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 ++%15 = OpExtInst %6 %14 Log2 %12 +-%13 = OpCompositeConstruct %7 %12 %12 %12 %12 ++%13 = OpCompositeConstruct %7 %15 %15 %15 %15 + OpStore %9 %13 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/OpExtInst_in_dst_only_dst.spvasm b/test/diff/diff_files/OpExtInst_in_dst_only_dst.spvasm new file mode 100644 index 00000000..e599d1e9 --- /dev/null +++ b/test/diff/diff_files/OpExtInst_in_dst_only_dst.spvasm @@ -0,0 +1,33 @@ + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "color" + OpName %11 "v" + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + OpDecorate %14 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 + %13 = OpExtInst %6 %1 Log2 %12 + %14 = OpCompositeConstruct %7 %13 %13 %13 %13 + OpStore %9 %14 + OpReturn + OpFunctionEnd + diff --git a/test/diff/diff_files/OpExtInst_in_dst_only_src.spvasm b/test/diff/diff_files/OpExtInst_in_dst_only_src.spvasm new file mode 100644 index 00000000..9f6fc18c --- /dev/null +++ b/test/diff/diff_files/OpExtInst_in_dst_only_src.spvasm @@ -0,0 +1,33 @@ +;; Tests a diff where the src shader doesn't have OpExtImport while the +;; dst shader does (and uses OpExtInst). This test ensures that when matching, +;; the OpExtImport instruction from the correct module is referenced. + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "color" + OpName %11 "v" + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 + %13 = OpCompositeConstruct %7 %12 %12 %12 %12 + OpStore %9 %13 + OpReturn + OpFunctionEnd + diff --git a/test/diff/diff_files/OpExtInst_in_src_only_autogen.cpp b/test/diff/diff_files/OpExtInst_in_src_only_autogen.cpp new file mode 100644 index 00000000..9944c2cf --- /dev/null +++ b/test/diff/diff_files/OpExtInst_in_src_only_autogen.cpp @@ -0,0 +1,242 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Tests a diff where the dst shader doesn't have OpExtImport while the +// src shader does (and uses OpExtInst). This test ensures that when matching, +// the OpExtImport instruction from the correct module is referenced. +constexpr char kSrc[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "color" + OpName %11 "v" + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + OpDecorate %14 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 + %13 = OpExtInst %6 %1 Log2 %12 + %14 = OpCompositeConstruct %7 %13 %13 %13 %13 + OpStore %9 %14 + OpReturn + OpFunctionEnd +)"; +constexpr char kDst[] = R"( OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "color" + OpName %11 "v" + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 + %13 = OpCompositeConstruct %7 %12 %12 %12 %12 + OpStore %9 %13 + OpReturn + OpFunctionEnd + +)"; + +TEST(DiffTest, OpextinstInSrcOnly) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 15 ++; Bound: 16 + ; Schema: 0 + OpCapability Shader +-%1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "color" + OpName %11 "v" + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + OpDecorate %12 RelaxedPrecision +-OpDecorate %13 RelaxedPrecision + OpDecorate %14 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 +-%13 = OpExtInst %6 %1 Log2 %12 +-%14 = OpCompositeConstruct %7 %13 %13 %13 %13 ++%14 = OpCompositeConstruct %7 %12 %12 %12 %12 + OpStore %9 %14 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, OpextinstInSrcOnlyNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + OpDecorate %14 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 + %13 = OpExtInst %6 %1 Log2 %12 + %14 = OpCompositeConstruct %7 %13 %13 %13 %13 + OpStore %9 %14 + OpReturn + OpFunctionEnd + +)"; + constexpr char kDstNoDebug[] = R"( OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 + %13 = OpCompositeConstruct %7 %12 %12 %12 %12 + OpStore %9 %13 + OpReturn + OpFunctionEnd + +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 15 ++; Bound: 16 + ; Schema: 0 + OpCapability Shader +-%1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + OpDecorate %12 RelaxedPrecision +-OpDecorate %13 RelaxedPrecision + OpDecorate %14 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 +-%13 = OpExtInst %6 %1 Log2 %12 +-%14 = OpCompositeConstruct %7 %13 %13 %13 %13 ++%14 = OpCompositeConstruct %7 %12 %12 %12 %12 + OpStore %9 %14 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/OpExtInst_in_src_only_dst.spvasm b/test/diff/diff_files/OpExtInst_in_src_only_dst.spvasm new file mode 100644 index 00000000..47233051 --- /dev/null +++ b/test/diff/diff_files/OpExtInst_in_src_only_dst.spvasm @@ -0,0 +1,30 @@ + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "color" + OpName %11 "v" + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 + %13 = OpCompositeConstruct %7 %12 %12 %12 %12 + OpStore %9 %13 + OpReturn + OpFunctionEnd + diff --git a/test/diff/diff_files/OpExtInst_in_src_only_src.spvasm b/test/diff/diff_files/OpExtInst_in_src_only_src.spvasm new file mode 100644 index 00000000..efb663a9 --- /dev/null +++ b/test/diff/diff_files/OpExtInst_in_src_only_src.spvasm @@ -0,0 +1,36 @@ +;; Tests a diff where the dst shader doesn't have OpExtImport while the +;; src shader does (and uses OpExtInst). This test ensures that when matching, +;; the OpExtImport instruction from the correct module is referenced. + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "color" + OpName %11 "v" + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + OpDecorate %14 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 + %13 = OpExtInst %6 %1 Log2 %12 + %14 = OpCompositeConstruct %7 %13 %13 %13 %13 + OpStore %9 %14 + OpReturn + OpFunctionEnd + diff --git a/test/diff/diff_files/OpTypeForwardPointer_basic_autogen.cpp b/test/diff/diff_files/OpTypeForwardPointer_basic_autogen.cpp new file mode 100644 index 00000000..af252b11 --- /dev/null +++ b/test/diff/diff_files/OpTypeForwardPointer_basic_autogen.cpp @@ -0,0 +1,136 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Basic test that OpTypeForwardPointer is matched +constexpr char kSrc[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %structptr "structptr" + OpTypeForwardPointer %structptr UniformConstant + %uint = OpTypeInt 32 0 + %structt1 = OpTypeStruct %structptr %uint + %structt2 = OpTypeStruct %uint %structptr + %structt3 = OpTypeStruct %uint %uint %structptr + %structt4 = OpTypeStruct %uint %uint %uint %structptr + %structptr = OpTypePointer UniformConstant %structt1)"; +constexpr char kDst[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %structptr "structptr" + OpName %structptr2 "structptr2" + OpTypeForwardPointer %structptr UniformConstant + OpTypeForwardPointer %structptr2 Function + %uint = OpTypeInt 32 0 + %structt1 = OpTypeStruct %structptr %uint + %structt2 = OpTypeStruct %uint %structptr + %structt3 = OpTypeStruct %uint %uint %structptr + %structt4 = OpTypeStruct %uint %uint %uint %structptr + %structptr = OpTypePointer UniformConstant %structt1 + %structptr2 = OpTypePointer Function %structt1 +)"; + +TEST(DiffTest, OptypeforwardpointerBasic) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 7 ++; Bound: 8 + ; Schema: 0 + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %1 "structptr" ++OpName %7 "structptr2" + OpTypeForwardPointer %1 UniformConstant ++OpTypeForwardPointer %7 Function + %2 = OpTypeInt 32 0 + %3 = OpTypeStruct %1 %2 + %4 = OpTypeStruct %2 %1 + %5 = OpTypeStruct %2 %2 %1 + %6 = OpTypeStruct %2 %2 %2 %1 + %1 = OpTypePointer UniformConstant %3 ++%7 = OpTypePointer Function %3 +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, OptypeforwardpointerBasicNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpTypeForwardPointer %structptr UniformConstant + %uint = OpTypeInt 32 0 + %structt1 = OpTypeStruct %structptr %uint + %structt2 = OpTypeStruct %uint %structptr + %structt3 = OpTypeStruct %uint %uint %structptr + %structt4 = OpTypeStruct %uint %uint %uint %structptr + %structptr = OpTypePointer UniformConstant %structt1 +)"; + constexpr char kDstNoDebug[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpTypeForwardPointer %structptr UniformConstant + OpTypeForwardPointer %structptr2 Function + %uint = OpTypeInt 32 0 + %structt1 = OpTypeStruct %structptr %uint + %structt2 = OpTypeStruct %uint %structptr + %structt3 = OpTypeStruct %uint %uint %structptr + %structt4 = OpTypeStruct %uint %uint %uint %structptr + %structptr = OpTypePointer UniformConstant %structt1 + %structptr2 = OpTypePointer Function %structt1 +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 7 ++; Bound: 8 + ; Schema: 0 + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpTypeForwardPointer %1 UniformConstant ++OpTypeForwardPointer %7 Function + %2 = OpTypeInt 32 0 + %3 = OpTypeStruct %1 %2 + %4 = OpTypeStruct %2 %1 + %5 = OpTypeStruct %2 %2 %1 + %6 = OpTypeStruct %2 %2 %2 %1 + %1 = OpTypePointer UniformConstant %3 ++%7 = OpTypePointer Function %3 +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/OpTypeForwardPointer_basic_dst.spvasm b/test/diff/diff_files/OpTypeForwardPointer_basic_dst.spvasm new file mode 100644 index 00000000..0c6e0cbe --- /dev/null +++ b/test/diff/diff_files/OpTypeForwardPointer_basic_dst.spvasm @@ -0,0 +1,15 @@ + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %structptr "structptr" + OpName %structptr2 "structptr2" + OpTypeForwardPointer %structptr UniformConstant + OpTypeForwardPointer %structptr2 Function + %uint = OpTypeInt 32 0 + %structt1 = OpTypeStruct %structptr %uint + %structt2 = OpTypeStruct %uint %structptr + %structt3 = OpTypeStruct %uint %uint %structptr + %structt4 = OpTypeStruct %uint %uint %uint %structptr + %structptr = OpTypePointer UniformConstant %structt1 + %structptr2 = OpTypePointer Function %structt1 diff --git a/test/diff/diff_files/OpTypeForwardPointer_basic_src.spvasm b/test/diff/diff_files/OpTypeForwardPointer_basic_src.spvasm new file mode 100644 index 00000000..408ec983 --- /dev/null +++ b/test/diff/diff_files/OpTypeForwardPointer_basic_src.spvasm @@ -0,0 +1,13 @@ +;; Basic test that OpTypeForwardPointer is matched + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %structptr "structptr" + OpTypeForwardPointer %structptr UniformConstant + %uint = OpTypeInt 32 0 + %structt1 = OpTypeStruct %structptr %uint + %structt2 = OpTypeStruct %uint %structptr + %structt3 = OpTypeStruct %uint %uint %structptr + %structt4 = OpTypeStruct %uint %uint %uint %structptr + %structptr = OpTypePointer UniformConstant %structt1 diff --git a/test/diff/diff_files/OpTypeForwardPointer_intertwined_autogen.cpp b/test/diff/diff_files/OpTypeForwardPointer_intertwined_autogen.cpp new file mode 100644 index 00000000..f2c9008e --- /dev/null +++ b/test/diff/diff_files/OpTypeForwardPointer_intertwined_autogen.cpp @@ -0,0 +1,138 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Tests that two forwarded types whose declarations are intertwined match +// correctly +constexpr char kSrc[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %Aptr "Aptr" + OpName %Bptr "Bptr" + OpTypeForwardPointer %Aptr UniformConstant + OpTypeForwardPointer %Bptr UniformConstant + %uint = OpTypeInt 32 0 + %A = OpTypeStruct %Aptr %uint %Bptr + %B = OpTypeStruct %uint %Aptr %Bptr + %Aptr = OpTypePointer UniformConstant %A + %Bptr = OpTypePointer UniformConstant %B)"; +constexpr char kDst[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %Aptr "Aptr" + OpName %Bptr "Bptr" + OpTypeForwardPointer %Bptr UniformConstant + OpTypeForwardPointer %Aptr UniformConstant + %uint = OpTypeInt 32 0 + %B = OpTypeStruct %uint %Aptr %Bptr %uint + %A = OpTypeStruct %Aptr %uint %Bptr + %Aptr = OpTypePointer UniformConstant %A + %Bptr = OpTypePointer UniformConstant %B +)"; + +TEST(DiffTest, OptypeforwardpointerIntertwined) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 6 ++; Bound: 7 + ; Schema: 0 + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %1 "Aptr" + OpName %2 "Bptr" + OpTypeForwardPointer %1 UniformConstant + OpTypeForwardPointer %2 UniformConstant + %3 = OpTypeInt 32 0 ++%6 = OpTypeStruct %3 %1 %2 %3 + %4 = OpTypeStruct %1 %3 %2 +-%5 = OpTypeStruct %3 %1 %2 + %1 = OpTypePointer UniformConstant %4 +-%2 = OpTypePointer UniformConstant %5 ++%2 = OpTypePointer UniformConstant %6 +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, OptypeforwardpointerIntertwinedNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpTypeForwardPointer %Aptr UniformConstant + OpTypeForwardPointer %Bptr UniformConstant + %uint = OpTypeInt 32 0 + %A = OpTypeStruct %Aptr %uint %Bptr + %B = OpTypeStruct %uint %Aptr %Bptr + %Aptr = OpTypePointer UniformConstant %A + %Bptr = OpTypePointer UniformConstant %B +)"; + constexpr char kDstNoDebug[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpTypeForwardPointer %Bptr UniformConstant + OpTypeForwardPointer %Aptr UniformConstant + %uint = OpTypeInt 32 0 + %B = OpTypeStruct %uint %Aptr %Bptr %uint + %A = OpTypeStruct %Aptr %uint %Bptr + %Aptr = OpTypePointer UniformConstant %A + %Bptr = OpTypePointer UniformConstant %B +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 6 ++; Bound: 10 + ; Schema: 0 + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL +-OpTypeForwardPointer %1 UniformConstant +-OpTypeForwardPointer %2 UniformConstant ++OpTypeForwardPointer %6 UniformConstant ++OpTypeForwardPointer %7 UniformConstant + %3 = OpTypeInt 32 0 +-%4 = OpTypeStruct %1 %3 %2 +-%5 = OpTypeStruct %3 %1 %2 +-%1 = OpTypePointer UniformConstant %4 +-%2 = OpTypePointer UniformConstant %5 ++%8 = OpTypeStruct %3 %7 %6 %3 ++%9 = OpTypeStruct %7 %3 %6 ++%7 = OpTypePointer UniformConstant %9 ++%6 = OpTypePointer UniformConstant %8 +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/OpTypeForwardPointer_intertwined_dst.spvasm b/test/diff/diff_files/OpTypeForwardPointer_intertwined_dst.spvasm new file mode 100644 index 00000000..bd735010 --- /dev/null +++ b/test/diff/diff_files/OpTypeForwardPointer_intertwined_dst.spvasm @@ -0,0 +1,13 @@ + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %Aptr "Aptr" + OpName %Bptr "Bptr" + OpTypeForwardPointer %Bptr UniformConstant + OpTypeForwardPointer %Aptr UniformConstant + %uint = OpTypeInt 32 0 + %B = OpTypeStruct %uint %Aptr %Bptr %uint + %A = OpTypeStruct %Aptr %uint %Bptr + %Aptr = OpTypePointer UniformConstant %A + %Bptr = OpTypePointer UniformConstant %B diff --git a/test/diff/diff_files/OpTypeForwardPointer_intertwined_src.spvasm b/test/diff/diff_files/OpTypeForwardPointer_intertwined_src.spvasm new file mode 100644 index 00000000..8fdaf28f --- /dev/null +++ b/test/diff/diff_files/OpTypeForwardPointer_intertwined_src.spvasm @@ -0,0 +1,15 @@ +;; Tests that two forwarded types whose declarations are intertwined match +;; correctly + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %Aptr "Aptr" + OpName %Bptr "Bptr" + OpTypeForwardPointer %Aptr UniformConstant + OpTypeForwardPointer %Bptr UniformConstant + %uint = OpTypeInt 32 0 + %A = OpTypeStruct %Aptr %uint %Bptr + %B = OpTypeStruct %uint %Aptr %Bptr + %Aptr = OpTypePointer UniformConstant %A + %Bptr = OpTypePointer UniformConstant %B diff --git a/test/diff/diff_files/OpTypeForwardPointer_mismatching_class_autogen.cpp b/test/diff/diff_files/OpTypeForwardPointer_mismatching_class_autogen.cpp new file mode 100644 index 00000000..0a59be32 --- /dev/null +++ b/test/diff/diff_files/OpTypeForwardPointer_mismatching_class_autogen.cpp @@ -0,0 +1,116 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Tests that two forwarded type pointers with mismatching storage classes +// aren't matched +constexpr char kSrc[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %Aptr "Aptr" + OpTypeForwardPointer %Aptr UniformConstant + %uint = OpTypeInt 32 0 + %A = OpTypeStruct %Aptr %uint + %Aptr = OpTypePointer UniformConstant %A)"; +constexpr char kDst[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %Aptr "Aptr" + OpTypeForwardPointer %Aptr Function + %uint = OpTypeInt 32 0 + %A = OpTypeStruct %Aptr %uint + %Aptr = OpTypePointer Function %A +)"; + +TEST(DiffTest, OptypeforwardpointerMismatchingClass) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 4 ++; Bound: 6 + ; Schema: 0 + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL +-OpName %1 "Aptr" ++OpName %4 "Aptr" +-OpTypeForwardPointer %1 UniformConstant ++OpTypeForwardPointer %4 Function + %2 = OpTypeInt 32 0 +-%3 = OpTypeStruct %1 %2 +-%1 = OpTypePointer UniformConstant %3 ++%5 = OpTypeStruct %4 %2 ++%4 = OpTypePointer Function %5 +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, OptypeforwardpointerMismatchingClassNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpTypeForwardPointer %Aptr UniformConstant + %uint = OpTypeInt 32 0 + %A = OpTypeStruct %Aptr %uint + %Aptr = OpTypePointer UniformConstant %A +)"; + constexpr char kDstNoDebug[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpTypeForwardPointer %Aptr Function + %uint = OpTypeInt 32 0 + %A = OpTypeStruct %Aptr %uint + %Aptr = OpTypePointer Function %A +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 4 ++; Bound: 6 + ; Schema: 0 + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL +-OpTypeForwardPointer %1 UniformConstant ++OpTypeForwardPointer %4 Function + %2 = OpTypeInt 32 0 +-%3 = OpTypeStruct %1 %2 +-%1 = OpTypePointer UniformConstant %3 ++%5 = OpTypeStruct %4 %2 ++%4 = OpTypePointer Function %5 +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/OpTypeForwardPointer_mismatching_class_dst.spvasm b/test/diff/diff_files/OpTypeForwardPointer_mismatching_class_dst.spvasm new file mode 100644 index 00000000..e874a0ca --- /dev/null +++ b/test/diff/diff_files/OpTypeForwardPointer_mismatching_class_dst.spvasm @@ -0,0 +1,9 @@ + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %Aptr "Aptr" + OpTypeForwardPointer %Aptr Function + %uint = OpTypeInt 32 0 + %A = OpTypeStruct %Aptr %uint + %Aptr = OpTypePointer Function %A diff --git a/test/diff/diff_files/OpTypeForwardPointer_mismatching_class_src.spvasm b/test/diff/diff_files/OpTypeForwardPointer_mismatching_class_src.spvasm new file mode 100644 index 00000000..8a339331 --- /dev/null +++ b/test/diff/diff_files/OpTypeForwardPointer_mismatching_class_src.spvasm @@ -0,0 +1,11 @@ +;; Tests that two forwarded type pointers with mismatching storage classes +;; aren't matched + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %Aptr "Aptr" + OpTypeForwardPointer %Aptr UniformConstant + %uint = OpTypeInt 32 0 + %A = OpTypeStruct %Aptr %uint + %Aptr = OpTypePointer UniformConstant %A diff --git a/test/diff/diff_files/OpTypeForwardPointer_mismatching_type_autogen.cpp b/test/diff/diff_files/OpTypeForwardPointer_mismatching_type_autogen.cpp new file mode 100644 index 00000000..0067cdf6 --- /dev/null +++ b/test/diff/diff_files/OpTypeForwardPointer_mismatching_type_autogen.cpp @@ -0,0 +1,111 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Tests that two forwarded type pointers with mismatching types aren't matched +constexpr char kSrc[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %Aptr "Aptr" + OpTypeForwardPointer %Aptr UniformConstant + %uint = OpTypeInt 32 0 + %A = OpTypeStruct %Aptr %uint + %Aptr = OpTypePointer UniformConstant %A)"; +constexpr char kDst[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %Aptr "Aptr" + OpTypeForwardPointer %Aptr UniformConstant + %uint = OpTypeInt 32 0 + %Aptr = OpTypePointer UniformConstant %uint +)"; + +TEST(DiffTest, OptypeforwardpointerMismatchingType) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 4 ++; Bound: 5 + ; Schema: 0 + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL +-OpName %1 "Aptr" ++OpName %4 "Aptr" +-OpTypeForwardPointer %1 UniformConstant ++OpTypeForwardPointer %4 UniformConstant + %2 = OpTypeInt 32 0 +-%3 = OpTypeStruct %1 %2 +-%1 = OpTypePointer UniformConstant %3 ++%4 = OpTypePointer UniformConstant %2 +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, OptypeforwardpointerMismatchingTypeNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpTypeForwardPointer %Aptr UniformConstant + %uint = OpTypeInt 32 0 + %A = OpTypeStruct %Aptr %uint + %Aptr = OpTypePointer UniformConstant %A +)"; + constexpr char kDstNoDebug[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpTypeForwardPointer %Aptr UniformConstant + %uint = OpTypeInt 32 0 + %Aptr = OpTypePointer UniformConstant %uint +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 4 ++; Bound: 5 + ; Schema: 0 + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL +-OpTypeForwardPointer %1 UniformConstant ++OpTypeForwardPointer %4 UniformConstant + %2 = OpTypeInt 32 0 +-%3 = OpTypeStruct %1 %2 +-%1 = OpTypePointer UniformConstant %3 ++%4 = OpTypePointer UniformConstant %2 +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/OpTypeForwardPointer_mismatching_type_dst.spvasm b/test/diff/diff_files/OpTypeForwardPointer_mismatching_type_dst.spvasm new file mode 100644 index 00000000..ee3d35c0 --- /dev/null +++ b/test/diff/diff_files/OpTypeForwardPointer_mismatching_type_dst.spvasm @@ -0,0 +1,8 @@ + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %Aptr "Aptr" + OpTypeForwardPointer %Aptr UniformConstant + %uint = OpTypeInt 32 0 + %Aptr = OpTypePointer UniformConstant %uint diff --git a/test/diff/diff_files/OpTypeForwardPointer_mismatching_type_src.spvasm b/test/diff/diff_files/OpTypeForwardPointer_mismatching_type_src.spvasm new file mode 100644 index 00000000..a4596a07 --- /dev/null +++ b/test/diff/diff_files/OpTypeForwardPointer_mismatching_type_src.spvasm @@ -0,0 +1,10 @@ +;; Tests that two forwarded type pointers with mismatching types aren't matched + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %Aptr "Aptr" + OpTypeForwardPointer %Aptr UniformConstant + %uint = OpTypeInt 32 0 + %A = OpTypeStruct %Aptr %uint + %Aptr = OpTypePointer UniformConstant %A diff --git a/test/diff/diff_files/OpTypeForwardPointer_nested_autogen.cpp b/test/diff/diff_files/OpTypeForwardPointer_nested_autogen.cpp new file mode 100644 index 00000000..d66c28ae --- /dev/null +++ b/test/diff/diff_files/OpTypeForwardPointer_nested_autogen.cpp @@ -0,0 +1,127 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Tests that two forwarded declarations match even if the type pointer is used +// in a nested struct declaration, and in multiple places +constexpr char kSrc[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %Aptr "Aptr" + OpTypeForwardPointer %Aptr UniformConstant + %uint = OpTypeInt 32 0 + %C = OpTypeStruct %Aptr %uint %Aptr + %B = OpTypeStruct %C %Aptr %uint + %A = OpTypeStruct %B %C %B + %Aptr = OpTypePointer UniformConstant %A)"; +constexpr char kDst[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %Aptr "Aptr" + OpTypeForwardPointer %Aptr UniformConstant + %uint = OpTypeInt 32 0 + %C = OpTypeStruct %Aptr %uint %Aptr + %B = OpTypeStruct %C %Aptr + %A = OpTypeStruct %B %C %B + %Aptr = OpTypePointer UniformConstant %A +)"; + +TEST(DiffTest, OptypeforwardpointerNested) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 6 ++; Bound: 8 + ; Schema: 0 + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %1 "Aptr" + OpTypeForwardPointer %1 UniformConstant + %2 = OpTypeInt 32 0 + %3 = OpTypeStruct %1 %2 %1 +-%4 = OpTypeStruct %3 %1 %2 +-%5 = OpTypeStruct %4 %3 %4 ++%6 = OpTypeStruct %3 %1 ++%7 = OpTypeStruct %6 %3 %6 +-%1 = OpTypePointer UniformConstant %5 ++%1 = OpTypePointer UniformConstant %7 +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, OptypeforwardpointerNestedNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpTypeForwardPointer %Aptr UniformConstant + %uint = OpTypeInt 32 0 + %C = OpTypeStruct %Aptr %uint %Aptr + %B = OpTypeStruct %C %Aptr %uint + %A = OpTypeStruct %B %C %B + %Aptr = OpTypePointer UniformConstant %A +)"; + constexpr char kDstNoDebug[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpTypeForwardPointer %Aptr UniformConstant + %uint = OpTypeInt 32 0 + %C = OpTypeStruct %Aptr %uint %Aptr + %B = OpTypeStruct %C %Aptr + %A = OpTypeStruct %B %C %B + %Aptr = OpTypePointer UniformConstant %A +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 6 ++; Bound: 8 + ; Schema: 0 + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpTypeForwardPointer %1 UniformConstant + %2 = OpTypeInt 32 0 + %3 = OpTypeStruct %1 %2 %1 +-%4 = OpTypeStruct %3 %1 %2 +-%5 = OpTypeStruct %4 %3 %4 ++%6 = OpTypeStruct %3 %1 ++%7 = OpTypeStruct %6 %3 %6 +-%1 = OpTypePointer UniformConstant %5 ++%1 = OpTypePointer UniformConstant %7 +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/OpTypeForwardPointer_nested_dst.spvasm b/test/diff/diff_files/OpTypeForwardPointer_nested_dst.spvasm new file mode 100644 index 00000000..e2483556 --- /dev/null +++ b/test/diff/diff_files/OpTypeForwardPointer_nested_dst.spvasm @@ -0,0 +1,11 @@ + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %Aptr "Aptr" + OpTypeForwardPointer %Aptr UniformConstant + %uint = OpTypeInt 32 0 + %C = OpTypeStruct %Aptr %uint %Aptr + %B = OpTypeStruct %C %Aptr + %A = OpTypeStruct %B %C %B + %Aptr = OpTypePointer UniformConstant %A diff --git a/test/diff/diff_files/OpTypeForwardPointer_nested_src.spvasm b/test/diff/diff_files/OpTypeForwardPointer_nested_src.spvasm new file mode 100644 index 00000000..035410e0 --- /dev/null +++ b/test/diff/diff_files/OpTypeForwardPointer_nested_src.spvasm @@ -0,0 +1,13 @@ +;; Tests that two forwarded declarations match even if the type pointer is used +;; in a nested struct declaration, and in multiple places + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %Aptr "Aptr" + OpTypeForwardPointer %Aptr UniformConstant + %uint = OpTypeInt 32 0 + %C = OpTypeStruct %Aptr %uint %Aptr + %B = OpTypeStruct %C %Aptr %uint + %A = OpTypeStruct %B %C %B + %Aptr = OpTypePointer UniformConstant %A diff --git a/test/diff/diff_files/OpTypeForwardPointer_onesided_debug_autogen.cpp b/test/diff/diff_files/OpTypeForwardPointer_onesided_debug_autogen.cpp new file mode 100644 index 00000000..df86fef2 --- /dev/null +++ b/test/diff/diff_files/OpTypeForwardPointer_onesided_debug_autogen.cpp @@ -0,0 +1,124 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Test that OpTypeForwardPointer is matched when one SPIR-V doesn't have debug +// info +constexpr char kSrc[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %structptr "structptr" + OpTypeForwardPointer %structptr UniformConstant + %uint = OpTypeInt 32 0 + %structt1 = OpTypeStruct %structptr %uint + %structt2 = OpTypeStruct %uint %structptr + %structt3 = OpTypeStruct %uint %uint %structptr + %structt4 = OpTypeStruct %uint %uint %uint %structptr + %structptr = OpTypePointer UniformConstant %structt1)"; +constexpr char kDst[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpTypeForwardPointer %structptr UniformConstant + %uint = OpTypeInt 32 0 + %structt1 = OpTypeStruct %structptr %uint + %structt2 = OpTypeStruct %uint %structptr + %structt3 = OpTypeStruct %uint %uint %structptr + %structt4 = OpTypeStruct %uint %uint %uint %structptr + %structptr = OpTypePointer UniformConstant %structt1 +)"; + +TEST(DiffTest, OptypeforwardpointerOnesidedDebug) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 + ; Bound: 7 + ; Schema: 0 + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL +-OpName %1 "structptr" + OpTypeForwardPointer %1 UniformConstant + %2 = OpTypeInt 32 0 + %3 = OpTypeStruct %1 %2 + %4 = OpTypeStruct %2 %1 + %5 = OpTypeStruct %2 %2 %1 + %6 = OpTypeStruct %2 %2 %2 %1 + %1 = OpTypePointer UniformConstant %3 +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, OptypeforwardpointerOnesidedDebugNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpTypeForwardPointer %structptr UniformConstant + %uint = OpTypeInt 32 0 + %structt1 = OpTypeStruct %structptr %uint + %structt2 = OpTypeStruct %uint %structptr + %structt3 = OpTypeStruct %uint %uint %structptr + %structt4 = OpTypeStruct %uint %uint %uint %structptr + %structptr = OpTypePointer UniformConstant %structt1 +)"; + constexpr char kDstNoDebug[] = R"( OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpTypeForwardPointer %structptr UniformConstant + %uint = OpTypeInt 32 0 + %structt1 = OpTypeStruct %structptr %uint + %structt2 = OpTypeStruct %uint %structptr + %structt3 = OpTypeStruct %uint %uint %structptr + %structt4 = OpTypeStruct %uint %uint %uint %structptr + %structptr = OpTypePointer UniformConstant %structt1 +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 + ; Bound: 7 + ; Schema: 0 + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpTypeForwardPointer %1 UniformConstant + %2 = OpTypeInt 32 0 + %3 = OpTypeStruct %1 %2 + %4 = OpTypeStruct %2 %1 + %5 = OpTypeStruct %2 %2 %1 + %6 = OpTypeStruct %2 %2 %2 %1 + %1 = OpTypePointer UniformConstant %3 +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/OpTypeForwardPointer_onesided_debug_dst.spvasm b/test/diff/diff_files/OpTypeForwardPointer_onesided_debug_dst.spvasm new file mode 100644 index 00000000..7e25710e --- /dev/null +++ b/test/diff/diff_files/OpTypeForwardPointer_onesided_debug_dst.spvasm @@ -0,0 +1,11 @@ + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpTypeForwardPointer %structptr UniformConstant + %uint = OpTypeInt 32 0 + %structt1 = OpTypeStruct %structptr %uint + %structt2 = OpTypeStruct %uint %structptr + %structt3 = OpTypeStruct %uint %uint %structptr + %structt4 = OpTypeStruct %uint %uint %uint %structptr + %structptr = OpTypePointer UniformConstant %structt1 diff --git a/test/diff/diff_files/OpTypeForwardPointer_onesided_debug_src.spvasm b/test/diff/diff_files/OpTypeForwardPointer_onesided_debug_src.spvasm new file mode 100644 index 00000000..e949b272 --- /dev/null +++ b/test/diff/diff_files/OpTypeForwardPointer_onesided_debug_src.spvasm @@ -0,0 +1,14 @@ +;; Test that OpTypeForwardPointer is matched when one SPIR-V doesn't have debug +;; info + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %structptr "structptr" + OpTypeForwardPointer %structptr UniformConstant + %uint = OpTypeInt 32 0 + %structt1 = OpTypeStruct %structptr %uint + %structt2 = OpTypeStruct %uint %structptr + %structt3 = OpTypeStruct %uint %uint %structptr + %structt4 = OpTypeStruct %uint %uint %uint %structptr + %structptr = OpTypePointer UniformConstant %structt1 diff --git a/test/diff/diff_files/README.md b/test/diff/diff_files/README.md new file mode 100644 index 00000000..5dcee25a --- /dev/null +++ b/test/diff/diff_files/README.md @@ -0,0 +1,17 @@ +# Diff tests + +This directory contains files used to ensure correctness of the `spirv-diff` implementation. The +`generate_tests.py` script takes `name_src.spvasm` and `name_dst.spvasm` (for each `name`) and +produces unit test files in the form of `name_autogen.cpp`. + +The unit test files test the diff between the src and dst inputs, as well as between debug-stripped +versions of those. Additionally, based on the `{variant}_TESTS` lists defined in +`generate_tests.py`, extra unit tests are added to exercise different options of spirv-diff. + +New tests are added simply by placing a new `name_src.spvasm` and `name_dst.spvasm` pair in this +directory and running `generate_tests.py`. Note that this script needs the path to the spirv-diff +executable that is built. + +The `generate_tests.py` script additionally expects `name_src.spvasm` to include a heading where the +purpose of the test is explained. This heading is parsed as a block of lines starting with `;;` at +the top of the file. diff --git a/test/diff/diff_files/basic_autogen.cpp b/test/diff/diff_files/basic_autogen.cpp new file mode 100644 index 00000000..f3afc701 --- /dev/null +++ b/test/diff/diff_files/basic_autogen.cpp @@ -0,0 +1,407 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Basic test for spirv-diff +constexpr char kSrc[] = R"(; SPIR-V +; Version: 1.0 +; Generator: Google ANGLE Shader Compiler; 0 +; Bound: 27 +; Schema: 0 +OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %22 "main" %4 %14 %19 + OpSource GLSL 450 + OpName %4 "_ua_position" + OpName %14 "ANGLEXfbPosition" + OpName %17 "gl_PerVertex" + OpMemberName %17 0 "gl_Position" +OpMemberName %17 1 "gl_PointSize" +OpMemberName %17 2 "gl_ClipDistance" + OpMemberName %17 3 "gl_CullDistance" + OpName %19 "" + OpName %22 "main" + OpDecorate %4 Location 0 + OpDecorate %14 Location 0 + OpMemberDecorate %17 1 RelaxedPrecision + OpMemberDecorate %17 0 BuiltIn Position + OpMemberDecorate %17 1 BuiltIn PointSize +OpMemberDecorate %17 2 BuiltIn ClipDistance +OpMemberDecorate %17 3 BuiltIn CullDistance +OpDecorate %17 Block +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeInt 32 0 +%6 = OpTypeInt 32 1 +%15 = OpConstant %5 8 +%16 = OpTypeArray %1 %15 +%17 = OpTypeStruct %2 %1 %16 %16 +%20 = OpTypeVoid +%25 = OpConstant %6 0 +%3 = OpTypePointer Input %2 +%13 = OpTypePointer Output %2 +%18 = OpTypePointer Output %17 +%21 = OpTypeFunction %20 +%4 = OpVariable %3 Input +%14 = OpVariable %13 Output +%19 = OpVariable %18 Output +%22 = OpFunction %20 None %21 +%23 = OpLabel +%24 = OpLoad %2 %4 +%26 = OpAccessChain %13 %19 %25 +OpStore %26 %24 +OpReturn +OpFunctionEnd)"; +constexpr char kDst[] = R"(; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 28 +; Schema: 0 +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %4 "main" %13 %17 %27 +OpSource GLSL 450 +OpName %4 "main" +OpName %11 "gl_PerVertex" +OpMemberName %11 0 "gl_Position" +OpMemberName %11 1 "gl_PointSize" +OpMemberName %11 2 "gl_ClipDistance" +OpMemberName %11 3 "gl_CullDistance" +OpName %13 "" +OpName %17 "_ua_position" +OpName %27 "ANGLEXfbPosition" +OpMemberDecorate %11 0 BuiltIn Position +OpMemberDecorate %11 1 BuiltIn PointSize +OpMemberDecorate %11 2 BuiltIn ClipDistance +OpMemberDecorate %11 3 BuiltIn CullDistance +OpDecorate %11 Block +OpDecorate %17 Location 0 +OpDecorate %27 Location 0 +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%6 = OpTypeFloat 32 +%7 = OpTypeVector %6 4 +%8 = OpTypeInt 32 0 +%9 = OpConstant %8 1 +%10 = OpTypeArray %6 %9 +%11 = OpTypeStruct %7 %6 %10 %10 +%12 = OpTypePointer Output %11 +%13 = OpVariable %12 Output +%14 = OpTypeInt 32 1 +%15 = OpConstant %14 0 +%16 = OpTypePointer Input %7 +%17 = OpVariable %16 Input +%19 = OpTypePointer Output %7 +%27 = OpVariable %19 Output +%4 = OpFunction %2 None %3 +%5 = OpLabel +%18 = OpLoad %7 %17 +%20 = OpAccessChain %19 %13 %15 +OpStore %20 %18 +OpReturn +OpFunctionEnd +)"; + +TEST(DiffTest, Basic) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 27 ++; Bound: 36 + ; Schema: 0 + OpCapability Shader ++%27 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 +-OpEntryPoint Vertex %22 "main" %4 %14 %19 ++OpEntryPoint Vertex %22 "main" %19 %4 %14 + OpSource GLSL 450 + OpName %4 "_ua_position" + OpName %14 "ANGLEXfbPosition" + OpName %17 "gl_PerVertex" + OpMemberName %17 0 "gl_Position" + OpMemberName %17 1 "gl_PointSize" + OpMemberName %17 2 "gl_ClipDistance" + OpMemberName %17 3 "gl_CullDistance" + OpName %19 "" + OpName %22 "main" + OpDecorate %4 Location 0 + OpDecorate %14 Location 0 +-OpMemberDecorate %17 1 RelaxedPrecision + OpMemberDecorate %17 0 BuiltIn Position + OpMemberDecorate %17 1 BuiltIn PointSize + OpMemberDecorate %17 2 BuiltIn ClipDistance + OpMemberDecorate %17 3 BuiltIn CullDistance + OpDecorate %17 Block + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 + %5 = OpTypeInt 32 0 + %6 = OpTypeInt 32 1 +-%15 = OpConstant %5 8 +-%16 = OpTypeArray %1 %15 +-%17 = OpTypeStruct %2 %1 %16 %16 ++%17 = OpTypeStruct %2 %1 %29 %29 ++%28 = OpConstant %5 1 ++%29 = OpTypeArray %1 %28 + %20 = OpTypeVoid + %25 = OpConstant %6 0 + %3 = OpTypePointer Input %2 + %13 = OpTypePointer Output %2 + %18 = OpTypePointer Output %17 + %21 = OpTypeFunction %20 + %4 = OpVariable %3 Input + %14 = OpVariable %13 Output + %19 = OpVariable %18 Output + %22 = OpFunction %20 None %21 + %23 = OpLabel + %24 = OpLoad %2 %4 + %26 = OpAccessChain %13 %19 %25 + OpStore %26 %24 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, BasicNoDebug) { + constexpr char kSrcNoDebug[] = R"(; SPIR-V +; Version: 1.0 +; Generator: Google ANGLE Shader Compiler; 0 +; Bound: 27 +; Schema: 0 +OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %22 "main" %4 %14 %19 + OpSource GLSL 450 + OpDecorate %4 Location 0 + OpDecorate %14 Location 0 + OpMemberDecorate %17 1 RelaxedPrecision + OpMemberDecorate %17 0 BuiltIn Position + OpMemberDecorate %17 1 BuiltIn PointSize +OpMemberDecorate %17 2 BuiltIn ClipDistance +OpMemberDecorate %17 3 BuiltIn CullDistance +OpDecorate %17 Block +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeInt 32 0 +%6 = OpTypeInt 32 1 +%15 = OpConstant %5 8 +%16 = OpTypeArray %1 %15 +%17 = OpTypeStruct %2 %1 %16 %16 +%20 = OpTypeVoid +%25 = OpConstant %6 0 +%3 = OpTypePointer Input %2 +%13 = OpTypePointer Output %2 +%18 = OpTypePointer Output %17 +%21 = OpTypeFunction %20 +%4 = OpVariable %3 Input +%14 = OpVariable %13 Output +%19 = OpVariable %18 Output +%22 = OpFunction %20 None %21 +%23 = OpLabel +%24 = OpLoad %2 %4 +%26 = OpAccessChain %13 %19 %25 +OpStore %26 %24 +OpReturn +OpFunctionEnd +)"; + constexpr char kDstNoDebug[] = R"(; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 28 +; Schema: 0 +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %4 "main" %13 %17 %27 +OpSource GLSL 450 +OpMemberDecorate %11 0 BuiltIn Position +OpMemberDecorate %11 1 BuiltIn PointSize +OpMemberDecorate %11 2 BuiltIn ClipDistance +OpMemberDecorate %11 3 BuiltIn CullDistance +OpDecorate %11 Block +OpDecorate %17 Location 0 +OpDecorate %27 Location 0 +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%6 = OpTypeFloat 32 +%7 = OpTypeVector %6 4 +%8 = OpTypeInt 32 0 +%9 = OpConstant %8 1 +%10 = OpTypeArray %6 %9 +%11 = OpTypeStruct %7 %6 %10 %10 +%12 = OpTypePointer Output %11 +%13 = OpVariable %12 Output +%14 = OpTypeInt 32 1 +%15 = OpConstant %14 0 +%16 = OpTypePointer Input %7 +%17 = OpVariable %16 Input +%19 = OpTypePointer Output %7 +%27 = OpVariable %19 Output +%4 = OpFunction %2 None %3 +%5 = OpLabel +%18 = OpLoad %7 %17 +%20 = OpAccessChain %19 %13 %15 +OpStore %20 %18 +OpReturn +OpFunctionEnd +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 27 ++; Bound: 36 + ; Schema: 0 + OpCapability Shader ++%27 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 +-OpEntryPoint Vertex %22 "main" %4 %14 %19 ++OpEntryPoint Vertex %22 "main" %19 %4 %14 + OpSource GLSL 450 + OpDecorate %4 Location 0 + OpDecorate %14 Location 0 +-OpMemberDecorate %17 1 RelaxedPrecision + OpMemberDecorate %17 0 BuiltIn Position + OpMemberDecorate %17 1 BuiltIn PointSize + OpMemberDecorate %17 2 BuiltIn ClipDistance + OpMemberDecorate %17 3 BuiltIn CullDistance + OpDecorate %17 Block + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 + %5 = OpTypeInt 32 0 + %6 = OpTypeInt 32 1 +-%15 = OpConstant %5 8 +-%16 = OpTypeArray %1 %15 +-%17 = OpTypeStruct %2 %1 %16 %16 ++%17 = OpTypeStruct %2 %1 %29 %29 ++%28 = OpConstant %5 1 ++%29 = OpTypeArray %1 %28 + %20 = OpTypeVoid + %25 = OpConstant %6 0 + %3 = OpTypePointer Input %2 + %13 = OpTypePointer Output %2 + %18 = OpTypePointer Output %17 + %21 = OpTypeFunction %20 + %4 = OpVariable %3 Input + %14 = OpVariable %13 Output + %19 = OpVariable %18 Output + %22 = OpFunction %20 None %21 + %23 = OpLabel + %24 = OpLoad %2 %4 + %26 = OpAccessChain %13 %19 %25 + OpStore %26 %24 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +TEST(DiffTest, BasicDumpIds) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 27 ++; Bound: 36 + ; Schema: 0 + OpCapability Shader ++%27 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 +-OpEntryPoint Vertex %22 "main" %4 %14 %19 ++OpEntryPoint Vertex %22 "main" %19 %4 %14 + OpSource GLSL 450 + OpName %4 "_ua_position" + OpName %14 "ANGLEXfbPosition" + OpName %17 "gl_PerVertex" + OpMemberName %17 0 "gl_Position" + OpMemberName %17 1 "gl_PointSize" + OpMemberName %17 2 "gl_ClipDistance" + OpMemberName %17 3 "gl_CullDistance" + OpName %19 "" + OpName %22 "main" + OpDecorate %4 Location 0 + OpDecorate %14 Location 0 +-OpMemberDecorate %17 1 RelaxedPrecision + OpMemberDecorate %17 0 BuiltIn Position + OpMemberDecorate %17 1 BuiltIn PointSize + OpMemberDecorate %17 2 BuiltIn ClipDistance + OpMemberDecorate %17 3 BuiltIn CullDistance + OpDecorate %17 Block + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 + %5 = OpTypeInt 32 0 + %6 = OpTypeInt 32 1 +-%15 = OpConstant %5 8 +-%16 = OpTypeArray %1 %15 +-%17 = OpTypeStruct %2 %1 %16 %16 ++%17 = OpTypeStruct %2 %1 %29 %29 ++%28 = OpConstant %5 1 ++%29 = OpTypeArray %1 %28 + %20 = OpTypeVoid + %25 = OpConstant %6 0 + %3 = OpTypePointer Input %2 + %13 = OpTypePointer Output %2 + %18 = OpTypePointer Output %17 + %21 = OpTypeFunction %20 + %4 = OpVariable %3 Input + %14 = OpVariable %13 Output + %19 = OpVariable %18 Output + %22 = OpFunction %20 None %21 + %23 = OpLabel + %24 = OpLoad %2 %4 + %26 = OpAccessChain %13 %19 %25 + OpStore %26 %24 + OpReturn + OpFunctionEnd + Src -> Dst + 1 -> 6 [TypeFloat] + 2 -> 7 [TypeVector] + 3 -> 16 [TypePointer] + 4 -> 17 [Variable] + 5 -> 8 [TypeInt] + 6 -> 14 [TypeInt] + 13 -> 19 [TypePointer] + 14 -> 27 [Variable] + 15 -> 34 [Constant] + 16 -> 35 [TypeArray] + 17 -> 11 [TypeStruct] + 18 -> 12 [TypePointer] + 19 -> 13 [Variable] + 20 -> 2 [TypeVoid] + 21 -> 3 [TypeFunction] + 22 -> 4 [Function] + 23 -> 5 [Label] + 24 -> 18 [Load] + 25 -> 15 [Constant] + 26 -> 20 [AccessChain] +)"; + Options options; + options.dump_id_map = true; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/basic_dst.spvasm b/test/diff/diff_files/basic_dst.spvasm new file mode 100644 index 00000000..79cb8887 --- /dev/null +++ b/test/diff/diff_files/basic_dst.spvasm @@ -0,0 +1,49 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 28 +; Schema: 0 +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %4 "main" %13 %17 %27 +OpSource GLSL 450 +OpName %4 "main" +OpName %11 "gl_PerVertex" +OpMemberName %11 0 "gl_Position" +OpMemberName %11 1 "gl_PointSize" +OpMemberName %11 2 "gl_ClipDistance" +OpMemberName %11 3 "gl_CullDistance" +OpName %13 "" +OpName %17 "_ua_position" +OpName %27 "ANGLEXfbPosition" +OpMemberDecorate %11 0 BuiltIn Position +OpMemberDecorate %11 1 BuiltIn PointSize +OpMemberDecorate %11 2 BuiltIn ClipDistance +OpMemberDecorate %11 3 BuiltIn CullDistance +OpDecorate %11 Block +OpDecorate %17 Location 0 +OpDecorate %27 Location 0 +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%6 = OpTypeFloat 32 +%7 = OpTypeVector %6 4 +%8 = OpTypeInt 32 0 +%9 = OpConstant %8 1 +%10 = OpTypeArray %6 %9 +%11 = OpTypeStruct %7 %6 %10 %10 +%12 = OpTypePointer Output %11 +%13 = OpVariable %12 Output +%14 = OpTypeInt 32 1 +%15 = OpConstant %14 0 +%16 = OpTypePointer Input %7 +%17 = OpVariable %16 Input +%19 = OpTypePointer Output %7 +%27 = OpVariable %19 Output +%4 = OpFunction %2 None %3 +%5 = OpLabel +%18 = OpLoad %7 %17 +%20 = OpAccessChain %19 %13 %15 +OpStore %20 %18 +OpReturn +OpFunctionEnd diff --git a/test/diff/diff_files/basic_src.spvasm b/test/diff/diff_files/basic_src.spvasm new file mode 100644 index 00000000..c55ec7af --- /dev/null +++ b/test/diff/diff_files/basic_src.spvasm @@ -0,0 +1,50 @@ +;; Basic test for spirv-diff +; SPIR-V +; Version: 1.0 +; Generator: Google ANGLE Shader Compiler; 0 +; Bound: 27 +; Schema: 0 +OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %22 "main" %4 %14 %19 + OpSource GLSL 450 + OpName %4 "_ua_position" + OpName %14 "ANGLEXfbPosition" + OpName %17 "gl_PerVertex" + OpMemberName %17 0 "gl_Position" +OpMemberName %17 1 "gl_PointSize" +OpMemberName %17 2 "gl_ClipDistance" + OpMemberName %17 3 "gl_CullDistance" + OpName %19 "" + OpName %22 "main" + OpDecorate %4 Location 0 + OpDecorate %14 Location 0 + OpMemberDecorate %17 1 RelaxedPrecision + OpMemberDecorate %17 0 BuiltIn Position + OpMemberDecorate %17 1 BuiltIn PointSize +OpMemberDecorate %17 2 BuiltIn ClipDistance +OpMemberDecorate %17 3 BuiltIn CullDistance +OpDecorate %17 Block +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeInt 32 0 +%6 = OpTypeInt 32 1 +%15 = OpConstant %5 8 +%16 = OpTypeArray %1 %15 +%17 = OpTypeStruct %2 %1 %16 %16 +%20 = OpTypeVoid +%25 = OpConstant %6 0 +%3 = OpTypePointer Input %2 +%13 = OpTypePointer Output %2 +%18 = OpTypePointer Output %17 +%21 = OpTypeFunction %20 +%4 = OpVariable %3 Input +%14 = OpVariable %13 Output +%19 = OpVariable %18 Output +%22 = OpFunction %20 None %21 +%23 = OpLabel +%24 = OpLoad %2 %4 +%26 = OpAccessChain %13 %19 %25 +OpStore %26 %24 +OpReturn +OpFunctionEnd diff --git a/test/diff/diff_files/constant_array_size_autogen.cpp b/test/diff/diff_files/constant_array_size_autogen.cpp new file mode 100644 index 00000000..16975ff4 --- /dev/null +++ b/test/diff/diff_files/constant_array_size_autogen.cpp @@ -0,0 +1,306 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Tests that identical integer constants are matched when used as array size, +// regardless of int or uint. +constexpr char kSrc[] = R"(; SPIR-V +; Version: 1.0 +; Generator: Google ANGLE Shader Compiler; 0 +; Bound: 27 +; Schema: 0 +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %22 "main" %4 %19 +OpSource GLSL 450 +OpName %4 "_ua_position" +OpName %17 "gl_PerVertex" +OpMemberName %17 0 "gl_Position" +OpMemberName %17 1 "gl_PointSize" +OpMemberName %17 2 "gl_ClipDistance" +OpMemberName %17 3 "gl_CullDistance" +OpName %19 "" +OpName %22 "main" +OpDecorate %4 Location 0 +OpMemberDecorate %17 1 RelaxedPrecision +OpMemberDecorate %17 0 BuiltIn Position +OpMemberDecorate %17 1 BuiltIn PointSize +OpMemberDecorate %17 2 BuiltIn ClipDistance +OpMemberDecorate %17 3 BuiltIn CullDistance +OpDecorate %17 Block +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeInt 32 0 +%8 = OpTypeVector %5 4 +%15 = OpConstant %5 8 +%16 = OpTypeArray %1 %15 +%17 = OpTypeStruct %2 %1 %16 %16 +%20 = OpTypeVoid +%25 = OpConstant %5 0 +%3 = OpTypePointer Input %2 +%13 = OpTypePointer Output %2 +%18 = OpTypePointer Output %17 +%21 = OpTypeFunction %20 +%4 = OpVariable %3 Input +%19 = OpVariable %18 Output +%22 = OpFunction %20 None %21 +%23 = OpLabel +%24 = OpLoad %2 %4 +%26 = OpAccessChain %13 %19 %25 +OpStore %26 %24 +OpReturn +OpFunctionEnd)"; +constexpr char kDst[] = R"(; SPIR-V +; Version: 1.0 +; Generator: Google ANGLE Shader Compiler; 0 +; Bound: 27 +; Schema: 0 +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %22 "main" %4 %19 +OpSource GLSL 450 +OpName %4 "_ua_position" +OpName %17 "gl_PerVertex" +OpMemberName %17 0 "gl_Position" +OpMemberName %17 1 "gl_PointSize" +OpMemberName %17 2 "gl_ClipDistance" +OpMemberName %17 3 "gl_CullDistance" +OpName %19 "" +OpName %22 "main" +OpDecorate %4 Location 0 +OpMemberDecorate %17 1 RelaxedPrecision +OpMemberDecorate %17 0 BuiltIn Position +OpMemberDecorate %17 1 BuiltIn PointSize +OpMemberDecorate %17 2 BuiltIn ClipDistance +OpMemberDecorate %17 3 BuiltIn CullDistance +OpDecorate %17 Block +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeInt 32 0 +%6 = OpTypeInt 32 1 +%8 = OpTypeVector %5 4 +%15 = OpConstant %6 8 +%16 = OpTypeArray %1 %15 +%17 = OpTypeStruct %2 %1 %16 %16 +%20 = OpTypeVoid +%25 = OpConstant %5 0 +%3 = OpTypePointer Input %2 +%13 = OpTypePointer Output %2 +%18 = OpTypePointer Output %17 +%21 = OpTypeFunction %20 +%4 = OpVariable %3 Input +%19 = OpVariable %18 Output +%22 = OpFunction %20 None %21 +%23 = OpLabel +%24 = OpLoad %2 %4 +%26 = OpAccessChain %13 %19 %25 +OpStore %26 %24 +OpReturn +OpFunctionEnd +)"; + +TEST(DiffTest, ConstantArraySize) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 27 ++; Bound: 34 + ; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %22 "main" %4 %19 + OpSource GLSL 450 + OpName %4 "_ua_position" + OpName %17 "gl_PerVertex" + OpMemberName %17 0 "gl_Position" + OpMemberName %17 1 "gl_PointSize" + OpMemberName %17 2 "gl_ClipDistance" + OpMemberName %17 3 "gl_CullDistance" + OpName %19 "" + OpName %22 "main" + OpDecorate %4 Location 0 + OpMemberDecorate %17 1 RelaxedPrecision + OpMemberDecorate %17 0 BuiltIn Position + OpMemberDecorate %17 1 BuiltIn PointSize + OpMemberDecorate %17 2 BuiltIn ClipDistance + OpMemberDecorate %17 3 BuiltIn CullDistance + OpDecorate %17 Block + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 + %5 = OpTypeInt 32 0 ++%27 = OpTypeInt 32 1 + %8 = OpTypeVector %5 4 +-%15 = OpConstant %5 8 ++%15 = OpConstant %27 8 + %16 = OpTypeArray %1 %15 + %17 = OpTypeStruct %2 %1 %16 %16 + %20 = OpTypeVoid + %25 = OpConstant %5 0 + %3 = OpTypePointer Input %2 + %13 = OpTypePointer Output %2 + %18 = OpTypePointer Output %17 + %21 = OpTypeFunction %20 + %4 = OpVariable %3 Input + %19 = OpVariable %18 Output + %22 = OpFunction %20 None %21 + %23 = OpLabel + %24 = OpLoad %2 %4 + %26 = OpAccessChain %13 %19 %25 + OpStore %26 %24 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, ConstantArraySizeNoDebug) { + constexpr char kSrcNoDebug[] = R"(; SPIR-V +; Version: 1.0 +; Generator: Google ANGLE Shader Compiler; 0 +; Bound: 27 +; Schema: 0 +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %22 "main" %4 %19 +OpSource GLSL 450 +OpDecorate %4 Location 0 +OpMemberDecorate %17 1 RelaxedPrecision +OpMemberDecorate %17 0 BuiltIn Position +OpMemberDecorate %17 1 BuiltIn PointSize +OpMemberDecorate %17 2 BuiltIn ClipDistance +OpMemberDecorate %17 3 BuiltIn CullDistance +OpDecorate %17 Block +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeInt 32 0 +%8 = OpTypeVector %5 4 +%15 = OpConstant %5 8 +%16 = OpTypeArray %1 %15 +%17 = OpTypeStruct %2 %1 %16 %16 +%20 = OpTypeVoid +%25 = OpConstant %5 0 +%3 = OpTypePointer Input %2 +%13 = OpTypePointer Output %2 +%18 = OpTypePointer Output %17 +%21 = OpTypeFunction %20 +%4 = OpVariable %3 Input +%19 = OpVariable %18 Output +%22 = OpFunction %20 None %21 +%23 = OpLabel +%24 = OpLoad %2 %4 +%26 = OpAccessChain %13 %19 %25 +OpStore %26 %24 +OpReturn +OpFunctionEnd +)"; + constexpr char kDstNoDebug[] = R"(; SPIR-V +; Version: 1.0 +; Generator: Google ANGLE Shader Compiler; 0 +; Bound: 27 +; Schema: 0 +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %22 "main" %4 %19 +OpSource GLSL 450 +OpDecorate %4 Location 0 +OpMemberDecorate %17 1 RelaxedPrecision +OpMemberDecorate %17 0 BuiltIn Position +OpMemberDecorate %17 1 BuiltIn PointSize +OpMemberDecorate %17 2 BuiltIn ClipDistance +OpMemberDecorate %17 3 BuiltIn CullDistance +OpDecorate %17 Block +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeInt 32 0 +%6 = OpTypeInt 32 1 +%8 = OpTypeVector %5 4 +%15 = OpConstant %6 8 +%16 = OpTypeArray %1 %15 +%17 = OpTypeStruct %2 %1 %16 %16 +%20 = OpTypeVoid +%25 = OpConstant %5 0 +%3 = OpTypePointer Input %2 +%13 = OpTypePointer Output %2 +%18 = OpTypePointer Output %17 +%21 = OpTypeFunction %20 +%4 = OpVariable %3 Input +%19 = OpVariable %18 Output +%22 = OpFunction %20 None %21 +%23 = OpLabel +%24 = OpLoad %2 %4 +%26 = OpAccessChain %13 %19 %25 +OpStore %26 %24 +OpReturn +OpFunctionEnd +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 27 ++; Bound: 34 + ; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %22 "main" %4 %19 + OpSource GLSL 450 + OpDecorate %4 Location 0 + OpMemberDecorate %17 1 RelaxedPrecision + OpMemberDecorate %17 0 BuiltIn Position + OpMemberDecorate %17 1 BuiltIn PointSize + OpMemberDecorate %17 2 BuiltIn ClipDistance + OpMemberDecorate %17 3 BuiltIn CullDistance + OpDecorate %17 Block + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 + %5 = OpTypeInt 32 0 ++%27 = OpTypeInt 32 1 + %8 = OpTypeVector %5 4 +-%15 = OpConstant %5 8 ++%15 = OpConstant %27 8 + %16 = OpTypeArray %1 %15 + %17 = OpTypeStruct %2 %1 %16 %16 + %20 = OpTypeVoid + %25 = OpConstant %5 0 + %3 = OpTypePointer Input %2 + %13 = OpTypePointer Output %2 + %18 = OpTypePointer Output %17 + %21 = OpTypeFunction %20 + %4 = OpVariable %3 Input + %19 = OpVariable %18 Output + %22 = OpFunction %20 None %21 + %23 = OpLabel + %24 = OpLoad %2 %4 + %26 = OpAccessChain %13 %19 %25 + OpStore %26 %24 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/constant_array_size_dst.spvasm b/test/diff/diff_files/constant_array_size_dst.spvasm new file mode 100644 index 00000000..a432b612 --- /dev/null +++ b/test/diff/diff_files/constant_array_size_dst.spvasm @@ -0,0 +1,47 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google ANGLE Shader Compiler; 0 +; Bound: 27 +; Schema: 0 +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %22 "main" %4 %19 +OpSource GLSL 450 +OpName %4 "_ua_position" +OpName %17 "gl_PerVertex" +OpMemberName %17 0 "gl_Position" +OpMemberName %17 1 "gl_PointSize" +OpMemberName %17 2 "gl_ClipDistance" +OpMemberName %17 3 "gl_CullDistance" +OpName %19 "" +OpName %22 "main" +OpDecorate %4 Location 0 +OpMemberDecorate %17 1 RelaxedPrecision +OpMemberDecorate %17 0 BuiltIn Position +OpMemberDecorate %17 1 BuiltIn PointSize +OpMemberDecorate %17 2 BuiltIn ClipDistance +OpMemberDecorate %17 3 BuiltIn CullDistance +OpDecorate %17 Block +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeInt 32 0 +%6 = OpTypeInt 32 1 +%8 = OpTypeVector %5 4 +%15 = OpConstant %6 8 +%16 = OpTypeArray %1 %15 +%17 = OpTypeStruct %2 %1 %16 %16 +%20 = OpTypeVoid +%25 = OpConstant %5 0 +%3 = OpTypePointer Input %2 +%13 = OpTypePointer Output %2 +%18 = OpTypePointer Output %17 +%21 = OpTypeFunction %20 +%4 = OpVariable %3 Input +%19 = OpVariable %18 Output +%22 = OpFunction %20 None %21 +%23 = OpLabel +%24 = OpLoad %2 %4 +%26 = OpAccessChain %13 %19 %25 +OpStore %26 %24 +OpReturn +OpFunctionEnd diff --git a/test/diff/diff_files/constant_array_size_src.spvasm b/test/diff/diff_files/constant_array_size_src.spvasm new file mode 100644 index 00000000..4e1ba646 --- /dev/null +++ b/test/diff/diff_files/constant_array_size_src.spvasm @@ -0,0 +1,48 @@ +;; Tests that identical integer constants are matched when used as array size, +;; regardless of int or uint. +; SPIR-V +; Version: 1.0 +; Generator: Google ANGLE Shader Compiler; 0 +; Bound: 27 +; Schema: 0 +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %22 "main" %4 %19 +OpSource GLSL 450 +OpName %4 "_ua_position" +OpName %17 "gl_PerVertex" +OpMemberName %17 0 "gl_Position" +OpMemberName %17 1 "gl_PointSize" +OpMemberName %17 2 "gl_ClipDistance" +OpMemberName %17 3 "gl_CullDistance" +OpName %19 "" +OpName %22 "main" +OpDecorate %4 Location 0 +OpMemberDecorate %17 1 RelaxedPrecision +OpMemberDecorate %17 0 BuiltIn Position +OpMemberDecorate %17 1 BuiltIn PointSize +OpMemberDecorate %17 2 BuiltIn ClipDistance +OpMemberDecorate %17 3 BuiltIn CullDistance +OpDecorate %17 Block +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeInt 32 0 +%8 = OpTypeVector %5 4 +%15 = OpConstant %5 8 +%16 = OpTypeArray %1 %15 +%17 = OpTypeStruct %2 %1 %16 %16 +%20 = OpTypeVoid +%25 = OpConstant %5 0 +%3 = OpTypePointer Input %2 +%13 = OpTypePointer Output %2 +%18 = OpTypePointer Output %17 +%21 = OpTypeFunction %20 +%4 = OpVariable %3 Input +%19 = OpVariable %18 Output +%22 = OpFunction %20 None %21 +%23 = OpLabel +%24 = OpLoad %2 %4 +%26 = OpAccessChain %13 %19 %25 +OpStore %26 %24 +OpReturn +OpFunctionEnd diff --git a/test/diff/diff_files/diff_test_files_autogen.cmake b/test/diff/diff_files/diff_test_files_autogen.cmake new file mode 100644 index 00000000..e6d36452 --- /dev/null +++ b/test/diff/diff_files/diff_test_files_autogen.cmake @@ -0,0 +1,45 @@ +# GENERATED FILE - DO NOT EDIT. +# Generated by generate_tests.py +# +# Copyright (c) 2022 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. + +list(APPEND DIFF_TEST_FILES +"diff_files/OpExtInst_in_dst_only_autogen.cpp" +"diff_files/OpExtInst_in_src_only_autogen.cpp" +"diff_files/OpTypeForwardPointer_basic_autogen.cpp" +"diff_files/OpTypeForwardPointer_intertwined_autogen.cpp" +"diff_files/OpTypeForwardPointer_mismatching_class_autogen.cpp" +"diff_files/OpTypeForwardPointer_mismatching_type_autogen.cpp" +"diff_files/OpTypeForwardPointer_nested_autogen.cpp" +"diff_files/OpTypeForwardPointer_onesided_debug_autogen.cpp" +"diff_files/basic_autogen.cpp" +"diff_files/constant_array_size_autogen.cpp" +"diff_files/different_decorations_fragment_autogen.cpp" +"diff_files/different_decorations_vertex_autogen.cpp" +"diff_files/different_function_parameter_count_autogen.cpp" +"diff_files/extra_if_block_autogen.cpp" +"diff_files/index_signedness_autogen.cpp" +"diff_files/int_vs_uint_constants_autogen.cpp" +"diff_files/large_functions_large_diffs_autogen.cpp" +"diff_files/large_functions_small_diffs_autogen.cpp" +"diff_files/multiple_different_entry_points_autogen.cpp" +"diff_files/multiple_same_entry_points_autogen.cpp" +"diff_files/reordered_if_blocks_autogen.cpp" +"diff_files/reordered_switch_blocks_autogen.cpp" +"diff_files/small_functions_small_diffs_autogen.cpp" +"diff_files/spec_constant_array_size_autogen.cpp" +"diff_files/spec_constant_composite_autogen.cpp" +"diff_files/unrelated_shaders_autogen.cpp" +) diff --git a/test/diff/diff_files/different_decorations_fragment_autogen.cpp b/test/diff/diff_files/different_decorations_fragment_autogen.cpp new file mode 100644 index 00000000..0d34654f --- /dev/null +++ b/test/diff/diff_files/different_decorations_fragment_autogen.cpp @@ -0,0 +1,1626 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Test where variable set/binding/location decorations are different between +// src and dst fragment shaders. +constexpr char kSrc[] = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %63 "main" %4 %22 +OpExecutionMode %63 OriginUpperLeft +OpSource GLSL 450 +OpName %4 "_ue" +OpName %8 "_uf" +OpName %11 "_ug" +OpName %12 "_uA" +OpMemberName %12 0 "_ux" +OpName %14 "_uc" +OpName %15 "_uB" +OpMemberName %15 0 "_ux" +OpName %20 "_ud" +OpName %22 "_ucol" +OpName %26 "ANGLEDepthRangeParams" +OpMemberName %26 0 "near" +OpMemberName %26 1 "far" +OpMemberName %26 2 "diff" +OpMemberName %26 3 "reserved" +OpName %27 "ANGLEUniformBlock" +OpMemberName %27 0 "viewport" +OpMemberName %27 1 "clipDistancesEnabled" +OpMemberName %27 2 "xfbActiveUnpaused" +OpMemberName %27 3 "xfbVerticesPerInstance" +OpMemberName %27 4 "numSamples" +OpMemberName %27 5 "xfbBufferOffsets" +OpMemberName %27 6 "acbBufferOffsets" +OpMemberName %27 7 "depthRange" +OpName %29 "ANGLEUniforms" +OpName %33 "_uc" +OpName %32 "_uh" +OpName %49 "_ux" +OpName %50 "_uy" +OpName %48 "_ui" +OpName %63 "main" +OpName %65 "param" +OpName %68 "param" +OpName %73 "param" +OpDecorate %4 Location 0 +OpDecorate %8 RelaxedPrecision +OpDecorate %8 DescriptorSet 0 +OpDecorate %8 Binding 0 +OpDecorate %11 DescriptorSet 0 +OpDecorate %11 Binding 1 +OpMemberDecorate %12 0 Offset 0 +OpMemberDecorate %12 0 RelaxedPrecision +OpDecorate %12 Block +OpDecorate %14 DescriptorSet 0 +OpDecorate %14 Binding 2 +OpMemberDecorate %15 0 Offset 0 +OpMemberDecorate %15 0 RelaxedPrecision +OpDecorate %15 BufferBlock +OpDecorate %20 DescriptorSet 0 +OpDecorate %20 Binding 3 +OpDecorate %22 RelaxedPrecision +OpDecorate %22 Location 0 +OpMemberDecorate %26 0 Offset 0 +OpMemberDecorate %26 1 Offset 4 +OpMemberDecorate %26 2 Offset 8 +OpMemberDecorate %26 3 Offset 12 +OpMemberDecorate %27 0 Offset 0 +OpMemberDecorate %27 1 Offset 16 +OpMemberDecorate %27 2 Offset 20 +OpMemberDecorate %27 3 Offset 24 +OpMemberDecorate %27 4 Offset 28 +OpMemberDecorate %27 5 Offset 32 +OpMemberDecorate %27 6 Offset 48 +OpMemberDecorate %27 7 Offset 64 +OpMemberDecorate %27 2 RelaxedPrecision +OpMemberDecorate %27 4 RelaxedPrecision +OpDecorate %27 Block +OpDecorate %29 DescriptorSet 0 +OpDecorate %29 Binding 4 +OpDecorate %32 RelaxedPrecision +OpDecorate %33 RelaxedPrecision +OpDecorate %36 RelaxedPrecision +OpDecorate %37 RelaxedPrecision +OpDecorate %38 RelaxedPrecision +OpDecorate %39 RelaxedPrecision +OpDecorate %41 RelaxedPrecision +OpDecorate %42 RelaxedPrecision +OpDecorate %43 RelaxedPrecision +OpDecorate %48 RelaxedPrecision +OpDecorate %49 RelaxedPrecision +OpDecorate %50 RelaxedPrecision +OpDecorate %52 RelaxedPrecision +OpDecorate %53 RelaxedPrecision +OpDecorate %54 RelaxedPrecision +OpDecorate %55 RelaxedPrecision +OpDecorate %56 RelaxedPrecision +OpDecorate %57 RelaxedPrecision +OpDecorate %58 RelaxedPrecision +OpDecorate %59 RelaxedPrecision +OpDecorate %60 RelaxedPrecision +OpDecorate %67 RelaxedPrecision +OpDecorate %68 RelaxedPrecision +OpDecorate %72 RelaxedPrecision +OpDecorate %73 RelaxedPrecision +OpDecorate %75 RelaxedPrecision +OpDecorate %76 RelaxedPrecision +OpDecorate %77 RelaxedPrecision +OpDecorate %80 RelaxedPrecision +OpDecorate %81 RelaxedPrecision +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeImage %1 2D 0 0 0 1 Unknown +%6 = OpTypeSampledImage %5 +%9 = OpTypeImage %1 2D 0 0 0 2 Rgba8 +%12 = OpTypeStruct %2 +%15 = OpTypeStruct %2 +%16 = OpTypeInt 32 0 +%17 = OpConstant %16 2 +%18 = OpTypeArray %15 %17 +%23 = OpTypeInt 32 1 +%24 = OpTypeVector %23 4 +%25 = OpTypeVector %16 4 +%26 = OpTypeStruct %1 %1 %1 %1 +%27 = OpTypeStruct %2 %16 %16 %23 %23 %24 %25 %26 +%35 = OpTypeVector %1 2 +%40 = OpTypeVector %23 2 +%61 = OpTypeVoid +%69 = OpConstant %16 0 +%78 = OpConstant %16 1 +%3 = OpTypePointer Input %2 +%7 = OpTypePointer UniformConstant %6 +%10 = OpTypePointer UniformConstant %9 +%13 = OpTypePointer Uniform %12 +%19 = OpTypePointer Uniform %18 +%21 = OpTypePointer Output %2 +%28 = OpTypePointer Uniform %27 +%30 = OpTypePointer Function %2 +%70 = OpTypePointer Uniform %2 +%31 = OpTypeFunction %2 %30 +%47 = OpTypeFunction %2 %30 %30 +%62 = OpTypeFunction %61 +%4 = OpVariable %3 Input +%8 = OpVariable %7 UniformConstant +%11 = OpVariable %10 UniformConstant +%14 = OpVariable %13 Uniform +%20 = OpVariable %19 Uniform +%22 = OpVariable %21 Output +%29 = OpVariable %28 Uniform +%32 = OpFunction %2 None %31 +%33 = OpFunctionParameter %30 +%34 = OpLabel +%36 = OpLoad %6 %8 +%37 = OpLoad %2 %33 +%38 = OpVectorShuffle %35 %37 %37 0 1 +%39 = OpImageSampleImplicitLod %2 %36 %38 +%41 = OpLoad %2 %33 +%42 = OpVectorShuffle %35 %41 %41 2 3 +%43 = OpConvertFToS %40 %42 +%44 = OpLoad %9 %11 +%45 = OpImageRead %2 %44 %43 +%46 = OpFAdd %2 %39 %45 +OpReturnValue %46 +OpFunctionEnd +%48 = OpFunction %2 None %47 +%49 = OpFunctionParameter %30 +%50 = OpFunctionParameter %30 +%51 = OpLabel +%52 = OpLoad %2 %49 +%53 = OpVectorShuffle %35 %52 %52 0 1 +%54 = OpLoad %2 %50 +%55 = OpVectorShuffle %35 %54 %54 2 3 +%56 = OpCompositeExtract %1 %53 0 +%57 = OpCompositeExtract %1 %53 1 +%58 = OpCompositeExtract %1 %55 0 +%59 = OpCompositeExtract %1 %55 1 +%60 = OpCompositeConstruct %2 %56 %57 %58 %59 +OpReturnValue %60 +OpFunctionEnd +%63 = OpFunction %61 None %62 +%64 = OpLabel +%65 = OpVariable %30 Function +%68 = OpVariable %30 Function +%73 = OpVariable %30 Function +%66 = OpLoad %2 %4 +OpStore %65 %66 +%67 = OpFunctionCall %2 %32 %65 +%71 = OpAccessChain %70 %14 %69 +%72 = OpLoad %2 %71 +OpStore %68 %72 +%74 = OpAccessChain %70 %20 %69 %69 +%75 = OpLoad %2 %74 +OpStore %73 %75 +%76 = OpFunctionCall %2 %48 %68 %73 +%77 = OpFAdd %2 %67 %76 +%79 = OpAccessChain %70 %20 %78 %69 +%80 = OpLoad %2 %79 +%81 = OpFAdd %2 %77 %80 +OpStore %22 %81 +OpReturn +OpFunctionEnd +)"; +constexpr char kDst[] = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %63 "main" %4 %22 +OpExecutionMode %63 OriginUpperLeft +OpSource GLSL 450 +OpName %4 "_ue" +OpName %8 "_uf" +OpName %11 "_ug" +OpName %12 "_uA" +OpMemberName %12 0 "_ux" +OpName %14 "_uc" +OpName %15 "_uB" +OpMemberName %15 0 "_ux" +OpName %20 "_ud" +OpName %22 "_ucol" +OpName %26 "ANGLEDepthRangeParams" +OpMemberName %26 0 "near" +OpMemberName %26 1 "far" +OpMemberName %26 2 "diff" +OpMemberName %26 3 "reserved" +OpName %27 "ANGLEUniformBlock" +OpMemberName %27 0 "viewport" +OpMemberName %27 1 "clipDistancesEnabled" +OpMemberName %27 2 "xfbActiveUnpaused" +OpMemberName %27 3 "xfbVerticesPerInstance" +OpMemberName %27 4 "numSamples" +OpMemberName %27 5 "xfbBufferOffsets" +OpMemberName %27 6 "acbBufferOffsets" +OpMemberName %27 7 "depthRange" +OpName %29 "ANGLEUniforms" +OpName %33 "_uc" +OpName %32 "_uh" +OpName %49 "_ux" +OpName %50 "_uy" +OpName %48 "_ui" +OpName %63 "main" +OpName %65 "param" +OpName %68 "param" +OpName %73 "param" +OpDecorate %4 Location 1 +OpDecorate %8 RelaxedPrecision +OpDecorate %8 DescriptorSet 2 +OpDecorate %8 Binding 0 +OpDecorate %11 DescriptorSet 3 +OpDecorate %11 Binding 0 +OpMemberDecorate %12 0 Offset 0 +OpMemberDecorate %12 0 RelaxedPrecision +OpDecorate %12 Block +OpDecorate %14 DescriptorSet 3 +OpDecorate %14 Binding 1 +OpMemberDecorate %15 0 Offset 0 +OpMemberDecorate %15 0 RelaxedPrecision +OpDecorate %15 BufferBlock +OpDecorate %20 DescriptorSet 3 +OpDecorate %20 Binding 2 +OpDecorate %22 RelaxedPrecision +OpDecorate %22 Location 1 +OpMemberDecorate %26 0 Offset 0 +OpMemberDecorate %26 1 Offset 4 +OpMemberDecorate %26 2 Offset 8 +OpMemberDecorate %26 3 Offset 12 +OpMemberDecorate %27 0 Offset 0 +OpMemberDecorate %27 1 Offset 16 +OpMemberDecorate %27 2 Offset 20 +OpMemberDecorate %27 3 Offset 24 +OpMemberDecorate %27 4 Offset 28 +OpMemberDecorate %27 5 Offset 32 +OpMemberDecorate %27 6 Offset 48 +OpMemberDecorate %27 7 Offset 64 +OpMemberDecorate %27 2 RelaxedPrecision +OpMemberDecorate %27 4 RelaxedPrecision +OpDecorate %27 Block +OpDecorate %29 DescriptorSet 0 +OpDecorate %29 Binding 0 +OpDecorate %32 RelaxedPrecision +OpDecorate %33 RelaxedPrecision +OpDecorate %36 RelaxedPrecision +OpDecorate %37 RelaxedPrecision +OpDecorate %38 RelaxedPrecision +OpDecorate %39 RelaxedPrecision +OpDecorate %41 RelaxedPrecision +OpDecorate %42 RelaxedPrecision +OpDecorate %43 RelaxedPrecision +OpDecorate %48 RelaxedPrecision +OpDecorate %49 RelaxedPrecision +OpDecorate %50 RelaxedPrecision +OpDecorate %52 RelaxedPrecision +OpDecorate %53 RelaxedPrecision +OpDecorate %54 RelaxedPrecision +OpDecorate %55 RelaxedPrecision +OpDecorate %56 RelaxedPrecision +OpDecorate %57 RelaxedPrecision +OpDecorate %58 RelaxedPrecision +OpDecorate %59 RelaxedPrecision +OpDecorate %60 RelaxedPrecision +OpDecorate %67 RelaxedPrecision +OpDecorate %68 RelaxedPrecision +OpDecorate %72 RelaxedPrecision +OpDecorate %73 RelaxedPrecision +OpDecorate %75 RelaxedPrecision +OpDecorate %76 RelaxedPrecision +OpDecorate %77 RelaxedPrecision +OpDecorate %80 RelaxedPrecision +OpDecorate %81 RelaxedPrecision +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeImage %1 2D 0 0 0 1 Unknown +%6 = OpTypeSampledImage %5 +%9 = OpTypeImage %1 2D 0 0 0 2 Rgba8 +%12 = OpTypeStruct %2 +%15 = OpTypeStruct %2 +%16 = OpTypeInt 32 0 +%17 = OpConstant %16 2 +%18 = OpTypeArray %15 %17 +%23 = OpTypeInt 32 1 +%24 = OpTypeVector %23 4 +%25 = OpTypeVector %16 4 +%26 = OpTypeStruct %1 %1 %1 %1 +%27 = OpTypeStruct %2 %16 %16 %23 %23 %24 %25 %26 +%35 = OpTypeVector %1 2 +%40 = OpTypeVector %23 2 +%61 = OpTypeVoid +%69 = OpConstant %16 0 +%78 = OpConstant %16 1 +%82 = OpTypePointer Private %2 +%3 = OpTypePointer Input %2 +%7 = OpTypePointer UniformConstant %6 +%10 = OpTypePointer UniformConstant %9 +%13 = OpTypePointer Uniform %12 +%19 = OpTypePointer Uniform %18 +%83 = OpTypePointer Private %2 +%21 = OpTypePointer Output %2 +%28 = OpTypePointer Uniform %27 +%30 = OpTypePointer Function %2 +%70 = OpTypePointer Uniform %2 +%31 = OpTypeFunction %2 %30 +%47 = OpTypeFunction %2 %30 %30 +%62 = OpTypeFunction %61 +%4 = OpVariable %3 Input +%8 = OpVariable %7 UniformConstant +%11 = OpVariable %10 UniformConstant +%14 = OpVariable %13 Uniform +%20 = OpVariable %19 Uniform +%22 = OpVariable %21 Output +%29 = OpVariable %28 Uniform +%84 = OpConstant %23 0 +%85 = OpConstant %1 0.5 +%32 = OpFunction %2 None %31 +%33 = OpFunctionParameter %30 +%34 = OpLabel +%36 = OpLoad %6 %8 +%37 = OpLoad %2 %33 +%38 = OpVectorShuffle %35 %37 %37 0 1 +%39 = OpImageSampleImplicitLod %2 %36 %38 +%41 = OpLoad %2 %33 +%42 = OpVectorShuffle %35 %41 %41 2 3 +%43 = OpConvertFToS %40 %42 +%44 = OpLoad %9 %11 +%45 = OpImageRead %2 %44 %43 +%46 = OpFAdd %2 %39 %45 +OpReturnValue %46 +OpFunctionEnd +%48 = OpFunction %2 None %47 +%49 = OpFunctionParameter %30 +%50 = OpFunctionParameter %30 +%51 = OpLabel +%52 = OpLoad %2 %49 +%53 = OpVectorShuffle %35 %52 %52 0 1 +%54 = OpLoad %2 %50 +%55 = OpVectorShuffle %35 %54 %54 2 3 +%56 = OpCompositeExtract %1 %53 0 +%57 = OpCompositeExtract %1 %53 1 +%58 = OpCompositeExtract %1 %55 0 +%59 = OpCompositeExtract %1 %55 1 +%60 = OpCompositeConstruct %2 %56 %57 %58 %59 +OpReturnValue %60 +OpFunctionEnd +%63 = OpFunction %61 None %62 +%64 = OpLabel +%65 = OpVariable %30 Function +%68 = OpVariable %30 Function +%73 = OpVariable %30 Function +%66 = OpLoad %2 %4 +OpStore %65 %66 +%67 = OpFunctionCall %2 %32 %65 +%71 = OpAccessChain %70 %14 %69 +%72 = OpLoad %2 %71 +OpStore %68 %72 +%74 = OpAccessChain %70 %20 %69 %69 +%75 = OpLoad %2 %74 +OpStore %73 %75 +%76 = OpFunctionCall %2 %48 %68 %73 +%77 = OpFAdd %2 %67 %76 +%79 = OpAccessChain %70 %20 %78 %69 +%80 = OpLoad %2 %79 +%81 = OpFAdd %2 %77 %80 +OpStore %22 %81 +OpReturn +OpFunctionEnd +)"; + +TEST(DiffTest, DifferentDecorationsFragment) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 82 ++; Bound: 86 + ; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %63 "main" %4 %22 + OpExecutionMode %63 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "_ue" + OpName %8 "_uf" + OpName %11 "_ug" + OpName %12 "_uA" + OpMemberName %12 0 "_ux" + OpName %14 "_uc" + OpName %15 "_uB" + OpMemberName %15 0 "_ux" + OpName %20 "_ud" + OpName %22 "_ucol" + OpName %26 "ANGLEDepthRangeParams" + OpMemberName %26 0 "near" + OpMemberName %26 1 "far" + OpMemberName %26 2 "diff" + OpMemberName %26 3 "reserved" + OpName %27 "ANGLEUniformBlock" + OpMemberName %27 0 "viewport" + OpMemberName %27 1 "clipDistancesEnabled" + OpMemberName %27 2 "xfbActiveUnpaused" + OpMemberName %27 3 "xfbVerticesPerInstance" + OpMemberName %27 4 "numSamples" + OpMemberName %27 5 "xfbBufferOffsets" + OpMemberName %27 6 "acbBufferOffsets" + OpMemberName %27 7 "depthRange" + OpName %29 "ANGLEUniforms" + OpName %33 "_uc" + OpName %32 "_uh" + OpName %49 "_ux" + OpName %50 "_uy" + OpName %48 "_ui" + OpName %63 "main" + OpName %65 "param" + OpName %68 "param" + OpName %73 "param" +-OpDecorate %4 Location 0 ++OpDecorate %4 Location 1 + OpDecorate %8 RelaxedPrecision +-OpDecorate %8 DescriptorSet 0 ++OpDecorate %8 DescriptorSet 2 + OpDecorate %8 Binding 0 +-OpDecorate %11 DescriptorSet 0 ++OpDecorate %11 DescriptorSet 3 +-OpDecorate %11 Binding 1 ++OpDecorate %11 Binding 0 + OpMemberDecorate %12 0 Offset 0 + OpMemberDecorate %12 0 RelaxedPrecision + OpDecorate %12 Block +-OpDecorate %14 DescriptorSet 0 ++OpDecorate %14 DescriptorSet 3 +-OpDecorate %14 Binding 2 ++OpDecorate %14 Binding 1 + OpMemberDecorate %15 0 Offset 0 + OpMemberDecorate %15 0 RelaxedPrecision + OpDecorate %15 BufferBlock +-OpDecorate %20 DescriptorSet 0 ++OpDecorate %20 DescriptorSet 3 +-OpDecorate %20 Binding 3 ++OpDecorate %20 Binding 2 + OpDecorate %22 RelaxedPrecision +-OpDecorate %22 Location 0 ++OpDecorate %22 Location 1 + OpMemberDecorate %26 0 Offset 0 + OpMemberDecorate %26 1 Offset 4 + OpMemberDecorate %26 2 Offset 8 + OpMemberDecorate %26 3 Offset 12 + OpMemberDecorate %27 0 Offset 0 + OpMemberDecorate %27 1 Offset 16 + OpMemberDecorate %27 2 Offset 20 + OpMemberDecorate %27 3 Offset 24 + OpMemberDecorate %27 4 Offset 28 + OpMemberDecorate %27 5 Offset 32 + OpMemberDecorate %27 6 Offset 48 + OpMemberDecorate %27 7 Offset 64 + OpMemberDecorate %27 2 RelaxedPrecision + OpMemberDecorate %27 4 RelaxedPrecision + OpDecorate %27 Block + OpDecorate %29 DescriptorSet 0 +-OpDecorate %29 Binding 4 ++OpDecorate %29 Binding 0 + OpDecorate %32 RelaxedPrecision + OpDecorate %33 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %38 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %43 RelaxedPrecision + OpDecorate %48 RelaxedPrecision + OpDecorate %49 RelaxedPrecision + OpDecorate %50 RelaxedPrecision + OpDecorate %52 RelaxedPrecision + OpDecorate %53 RelaxedPrecision + OpDecorate %54 RelaxedPrecision + OpDecorate %55 RelaxedPrecision + OpDecorate %56 RelaxedPrecision + OpDecorate %57 RelaxedPrecision + OpDecorate %58 RelaxedPrecision + OpDecorate %59 RelaxedPrecision + OpDecorate %60 RelaxedPrecision + OpDecorate %67 RelaxedPrecision + OpDecorate %68 RelaxedPrecision + OpDecorate %72 RelaxedPrecision + OpDecorate %73 RelaxedPrecision + OpDecorate %75 RelaxedPrecision + OpDecorate %76 RelaxedPrecision + OpDecorate %77 RelaxedPrecision + OpDecorate %80 RelaxedPrecision + OpDecorate %81 RelaxedPrecision + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 + %5 = OpTypeImage %1 2D 0 0 0 1 Unknown + %6 = OpTypeSampledImage %5 + %9 = OpTypeImage %1 2D 0 0 0 2 Rgba8 + %12 = OpTypeStruct %2 + %15 = OpTypeStruct %2 + %16 = OpTypeInt 32 0 + %17 = OpConstant %16 2 + %18 = OpTypeArray %15 %17 + %23 = OpTypeInt 32 1 + %24 = OpTypeVector %23 4 + %25 = OpTypeVector %16 4 + %26 = OpTypeStruct %1 %1 %1 %1 + %27 = OpTypeStruct %2 %16 %16 %23 %23 %24 %25 %26 + %35 = OpTypeVector %1 2 + %40 = OpTypeVector %23 2 + %61 = OpTypeVoid + %69 = OpConstant %16 0 + %78 = OpConstant %16 1 ++%82 = OpTypePointer Private %2 + %3 = OpTypePointer Input %2 + %7 = OpTypePointer UniformConstant %6 + %10 = OpTypePointer UniformConstant %9 + %13 = OpTypePointer Uniform %12 + %19 = OpTypePointer Uniform %18 ++%83 = OpTypePointer Private %2 + %21 = OpTypePointer Output %2 + %28 = OpTypePointer Uniform %27 + %30 = OpTypePointer Function %2 + %70 = OpTypePointer Uniform %2 + %31 = OpTypeFunction %2 %30 + %47 = OpTypeFunction %2 %30 %30 + %62 = OpTypeFunction %61 + %4 = OpVariable %3 Input + %8 = OpVariable %7 UniformConstant + %11 = OpVariable %10 UniformConstant + %14 = OpVariable %13 Uniform + %20 = OpVariable %19 Uniform + %22 = OpVariable %21 Output + %29 = OpVariable %28 Uniform ++%84 = OpConstant %23 0 ++%85 = OpConstant %1 0.5 + %32 = OpFunction %2 None %31 + %33 = OpFunctionParameter %30 + %34 = OpLabel + %36 = OpLoad %6 %8 + %37 = OpLoad %2 %33 + %38 = OpVectorShuffle %35 %37 %37 0 1 + %39 = OpImageSampleImplicitLod %2 %36 %38 + %41 = OpLoad %2 %33 + %42 = OpVectorShuffle %35 %41 %41 2 3 + %43 = OpConvertFToS %40 %42 + %44 = OpLoad %9 %11 + %45 = OpImageRead %2 %44 %43 + %46 = OpFAdd %2 %39 %45 + OpReturnValue %46 + OpFunctionEnd + %48 = OpFunction %2 None %47 + %49 = OpFunctionParameter %30 + %50 = OpFunctionParameter %30 + %51 = OpLabel + %52 = OpLoad %2 %49 + %53 = OpVectorShuffle %35 %52 %52 0 1 + %54 = OpLoad %2 %50 + %55 = OpVectorShuffle %35 %54 %54 2 3 + %56 = OpCompositeExtract %1 %53 0 + %57 = OpCompositeExtract %1 %53 1 + %58 = OpCompositeExtract %1 %55 0 + %59 = OpCompositeExtract %1 %55 1 + %60 = OpCompositeConstruct %2 %56 %57 %58 %59 + OpReturnValue %60 + OpFunctionEnd + %63 = OpFunction %61 None %62 + %64 = OpLabel + %65 = OpVariable %30 Function + %68 = OpVariable %30 Function + %73 = OpVariable %30 Function + %66 = OpLoad %2 %4 + OpStore %65 %66 + %67 = OpFunctionCall %2 %32 %65 + %71 = OpAccessChain %70 %14 %69 + %72 = OpLoad %2 %71 + OpStore %68 %72 + %74 = OpAccessChain %70 %20 %69 %69 + %75 = OpLoad %2 %74 + OpStore %73 %75 + %76 = OpFunctionCall %2 %48 %68 %73 + %77 = OpFAdd %2 %67 %76 + %79 = OpAccessChain %70 %20 %78 %69 + %80 = OpLoad %2 %79 + %81 = OpFAdd %2 %77 %80 + OpStore %22 %81 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, DifferentDecorationsFragmentNoDebug) { + constexpr char kSrcNoDebug[] = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %63 "main" %4 %22 +OpExecutionMode %63 OriginUpperLeft +OpSource GLSL 450 +OpDecorate %4 Location 0 +OpDecorate %8 RelaxedPrecision +OpDecorate %8 DescriptorSet 0 +OpDecorate %8 Binding 0 +OpDecorate %11 DescriptorSet 0 +OpDecorate %11 Binding 1 +OpMemberDecorate %12 0 Offset 0 +OpMemberDecorate %12 0 RelaxedPrecision +OpDecorate %12 Block +OpDecorate %14 DescriptorSet 0 +OpDecorate %14 Binding 2 +OpMemberDecorate %15 0 Offset 0 +OpMemberDecorate %15 0 RelaxedPrecision +OpDecorate %15 BufferBlock +OpDecorate %20 DescriptorSet 0 +OpDecorate %20 Binding 3 +OpDecorate %22 RelaxedPrecision +OpDecorate %22 Location 0 +OpMemberDecorate %26 0 Offset 0 +OpMemberDecorate %26 1 Offset 4 +OpMemberDecorate %26 2 Offset 8 +OpMemberDecorate %26 3 Offset 12 +OpMemberDecorate %27 0 Offset 0 +OpMemberDecorate %27 1 Offset 16 +OpMemberDecorate %27 2 Offset 20 +OpMemberDecorate %27 3 Offset 24 +OpMemberDecorate %27 4 Offset 28 +OpMemberDecorate %27 5 Offset 32 +OpMemberDecorate %27 6 Offset 48 +OpMemberDecorate %27 7 Offset 64 +OpMemberDecorate %27 2 RelaxedPrecision +OpMemberDecorate %27 4 RelaxedPrecision +OpDecorate %27 Block +OpDecorate %29 DescriptorSet 0 +OpDecorate %29 Binding 4 +OpDecorate %32 RelaxedPrecision +OpDecorate %33 RelaxedPrecision +OpDecorate %36 RelaxedPrecision +OpDecorate %37 RelaxedPrecision +OpDecorate %38 RelaxedPrecision +OpDecorate %39 RelaxedPrecision +OpDecorate %41 RelaxedPrecision +OpDecorate %42 RelaxedPrecision +OpDecorate %43 RelaxedPrecision +OpDecorate %48 RelaxedPrecision +OpDecorate %49 RelaxedPrecision +OpDecorate %50 RelaxedPrecision +OpDecorate %52 RelaxedPrecision +OpDecorate %53 RelaxedPrecision +OpDecorate %54 RelaxedPrecision +OpDecorate %55 RelaxedPrecision +OpDecorate %56 RelaxedPrecision +OpDecorate %57 RelaxedPrecision +OpDecorate %58 RelaxedPrecision +OpDecorate %59 RelaxedPrecision +OpDecorate %60 RelaxedPrecision +OpDecorate %67 RelaxedPrecision +OpDecorate %68 RelaxedPrecision +OpDecorate %72 RelaxedPrecision +OpDecorate %73 RelaxedPrecision +OpDecorate %75 RelaxedPrecision +OpDecorate %76 RelaxedPrecision +OpDecorate %77 RelaxedPrecision +OpDecorate %80 RelaxedPrecision +OpDecorate %81 RelaxedPrecision +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeImage %1 2D 0 0 0 1 Unknown +%6 = OpTypeSampledImage %5 +%9 = OpTypeImage %1 2D 0 0 0 2 Rgba8 +%12 = OpTypeStruct %2 +%15 = OpTypeStruct %2 +%16 = OpTypeInt 32 0 +%17 = OpConstant %16 2 +%18 = OpTypeArray %15 %17 +%23 = OpTypeInt 32 1 +%24 = OpTypeVector %23 4 +%25 = OpTypeVector %16 4 +%26 = OpTypeStruct %1 %1 %1 %1 +%27 = OpTypeStruct %2 %16 %16 %23 %23 %24 %25 %26 +%35 = OpTypeVector %1 2 +%40 = OpTypeVector %23 2 +%61 = OpTypeVoid +%69 = OpConstant %16 0 +%78 = OpConstant %16 1 +%3 = OpTypePointer Input %2 +%7 = OpTypePointer UniformConstant %6 +%10 = OpTypePointer UniformConstant %9 +%13 = OpTypePointer Uniform %12 +%19 = OpTypePointer Uniform %18 +%21 = OpTypePointer Output %2 +%28 = OpTypePointer Uniform %27 +%30 = OpTypePointer Function %2 +%70 = OpTypePointer Uniform %2 +%31 = OpTypeFunction %2 %30 +%47 = OpTypeFunction %2 %30 %30 +%62 = OpTypeFunction %61 +%4 = OpVariable %3 Input +%8 = OpVariable %7 UniformConstant +%11 = OpVariable %10 UniformConstant +%14 = OpVariable %13 Uniform +%20 = OpVariable %19 Uniform +%22 = OpVariable %21 Output +%29 = OpVariable %28 Uniform +%32 = OpFunction %2 None %31 +%33 = OpFunctionParameter %30 +%34 = OpLabel +%36 = OpLoad %6 %8 +%37 = OpLoad %2 %33 +%38 = OpVectorShuffle %35 %37 %37 0 1 +%39 = OpImageSampleImplicitLod %2 %36 %38 +%41 = OpLoad %2 %33 +%42 = OpVectorShuffle %35 %41 %41 2 3 +%43 = OpConvertFToS %40 %42 +%44 = OpLoad %9 %11 +%45 = OpImageRead %2 %44 %43 +%46 = OpFAdd %2 %39 %45 +OpReturnValue %46 +OpFunctionEnd +%48 = OpFunction %2 None %47 +%49 = OpFunctionParameter %30 +%50 = OpFunctionParameter %30 +%51 = OpLabel +%52 = OpLoad %2 %49 +%53 = OpVectorShuffle %35 %52 %52 0 1 +%54 = OpLoad %2 %50 +%55 = OpVectorShuffle %35 %54 %54 2 3 +%56 = OpCompositeExtract %1 %53 0 +%57 = OpCompositeExtract %1 %53 1 +%58 = OpCompositeExtract %1 %55 0 +%59 = OpCompositeExtract %1 %55 1 +%60 = OpCompositeConstruct %2 %56 %57 %58 %59 +OpReturnValue %60 +OpFunctionEnd +%63 = OpFunction %61 None %62 +%64 = OpLabel +%65 = OpVariable %30 Function +%68 = OpVariable %30 Function +%73 = OpVariable %30 Function +%66 = OpLoad %2 %4 +OpStore %65 %66 +%67 = OpFunctionCall %2 %32 %65 +%71 = OpAccessChain %70 %14 %69 +%72 = OpLoad %2 %71 +OpStore %68 %72 +%74 = OpAccessChain %70 %20 %69 %69 +%75 = OpLoad %2 %74 +OpStore %73 %75 +%76 = OpFunctionCall %2 %48 %68 %73 +%77 = OpFAdd %2 %67 %76 +%79 = OpAccessChain %70 %20 %78 %69 +%80 = OpLoad %2 %79 +%81 = OpFAdd %2 %77 %80 +OpStore %22 %81 +OpReturn +OpFunctionEnd + +)"; + constexpr char kDstNoDebug[] = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %63 "main" %4 %22 +OpExecutionMode %63 OriginUpperLeft +OpSource GLSL 450 +OpDecorate %4 Location 1 +OpDecorate %8 RelaxedPrecision +OpDecorate %8 DescriptorSet 2 +OpDecorate %8 Binding 0 +OpDecorate %11 DescriptorSet 3 +OpDecorate %11 Binding 0 +OpMemberDecorate %12 0 Offset 0 +OpMemberDecorate %12 0 RelaxedPrecision +OpDecorate %12 Block +OpDecorate %14 DescriptorSet 3 +OpDecorate %14 Binding 1 +OpMemberDecorate %15 0 Offset 0 +OpMemberDecorate %15 0 RelaxedPrecision +OpDecorate %15 BufferBlock +OpDecorate %20 DescriptorSet 3 +OpDecorate %20 Binding 2 +OpDecorate %22 RelaxedPrecision +OpDecorate %22 Location 1 +OpMemberDecorate %26 0 Offset 0 +OpMemberDecorate %26 1 Offset 4 +OpMemberDecorate %26 2 Offset 8 +OpMemberDecorate %26 3 Offset 12 +OpMemberDecorate %27 0 Offset 0 +OpMemberDecorate %27 1 Offset 16 +OpMemberDecorate %27 2 Offset 20 +OpMemberDecorate %27 3 Offset 24 +OpMemberDecorate %27 4 Offset 28 +OpMemberDecorate %27 5 Offset 32 +OpMemberDecorate %27 6 Offset 48 +OpMemberDecorate %27 7 Offset 64 +OpMemberDecorate %27 2 RelaxedPrecision +OpMemberDecorate %27 4 RelaxedPrecision +OpDecorate %27 Block +OpDecorate %29 DescriptorSet 0 +OpDecorate %29 Binding 0 +OpDecorate %32 RelaxedPrecision +OpDecorate %33 RelaxedPrecision +OpDecorate %36 RelaxedPrecision +OpDecorate %37 RelaxedPrecision +OpDecorate %38 RelaxedPrecision +OpDecorate %39 RelaxedPrecision +OpDecorate %41 RelaxedPrecision +OpDecorate %42 RelaxedPrecision +OpDecorate %43 RelaxedPrecision +OpDecorate %48 RelaxedPrecision +OpDecorate %49 RelaxedPrecision +OpDecorate %50 RelaxedPrecision +OpDecorate %52 RelaxedPrecision +OpDecorate %53 RelaxedPrecision +OpDecorate %54 RelaxedPrecision +OpDecorate %55 RelaxedPrecision +OpDecorate %56 RelaxedPrecision +OpDecorate %57 RelaxedPrecision +OpDecorate %58 RelaxedPrecision +OpDecorate %59 RelaxedPrecision +OpDecorate %60 RelaxedPrecision +OpDecorate %67 RelaxedPrecision +OpDecorate %68 RelaxedPrecision +OpDecorate %72 RelaxedPrecision +OpDecorate %73 RelaxedPrecision +OpDecorate %75 RelaxedPrecision +OpDecorate %76 RelaxedPrecision +OpDecorate %77 RelaxedPrecision +OpDecorate %80 RelaxedPrecision +OpDecorate %81 RelaxedPrecision +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeImage %1 2D 0 0 0 1 Unknown +%6 = OpTypeSampledImage %5 +%9 = OpTypeImage %1 2D 0 0 0 2 Rgba8 +%12 = OpTypeStruct %2 +%15 = OpTypeStruct %2 +%16 = OpTypeInt 32 0 +%17 = OpConstant %16 2 +%18 = OpTypeArray %15 %17 +%23 = OpTypeInt 32 1 +%24 = OpTypeVector %23 4 +%25 = OpTypeVector %16 4 +%26 = OpTypeStruct %1 %1 %1 %1 +%27 = OpTypeStruct %2 %16 %16 %23 %23 %24 %25 %26 +%35 = OpTypeVector %1 2 +%40 = OpTypeVector %23 2 +%61 = OpTypeVoid +%69 = OpConstant %16 0 +%78 = OpConstant %16 1 +%82 = OpTypePointer Private %2 +%3 = OpTypePointer Input %2 +%7 = OpTypePointer UniformConstant %6 +%10 = OpTypePointer UniformConstant %9 +%13 = OpTypePointer Uniform %12 +%19 = OpTypePointer Uniform %18 +%83 = OpTypePointer Private %2 +%21 = OpTypePointer Output %2 +%28 = OpTypePointer Uniform %27 +%30 = OpTypePointer Function %2 +%70 = OpTypePointer Uniform %2 +%31 = OpTypeFunction %2 %30 +%47 = OpTypeFunction %2 %30 %30 +%62 = OpTypeFunction %61 +%4 = OpVariable %3 Input +%8 = OpVariable %7 UniformConstant +%11 = OpVariable %10 UniformConstant +%14 = OpVariable %13 Uniform +%20 = OpVariable %19 Uniform +%22 = OpVariable %21 Output +%29 = OpVariable %28 Uniform +%84 = OpConstant %23 0 +%85 = OpConstant %1 0.5 +%32 = OpFunction %2 None %31 +%33 = OpFunctionParameter %30 +%34 = OpLabel +%36 = OpLoad %6 %8 +%37 = OpLoad %2 %33 +%38 = OpVectorShuffle %35 %37 %37 0 1 +%39 = OpImageSampleImplicitLod %2 %36 %38 +%41 = OpLoad %2 %33 +%42 = OpVectorShuffle %35 %41 %41 2 3 +%43 = OpConvertFToS %40 %42 +%44 = OpLoad %9 %11 +%45 = OpImageRead %2 %44 %43 +%46 = OpFAdd %2 %39 %45 +OpReturnValue %46 +OpFunctionEnd +%48 = OpFunction %2 None %47 +%49 = OpFunctionParameter %30 +%50 = OpFunctionParameter %30 +%51 = OpLabel +%52 = OpLoad %2 %49 +%53 = OpVectorShuffle %35 %52 %52 0 1 +%54 = OpLoad %2 %50 +%55 = OpVectorShuffle %35 %54 %54 2 3 +%56 = OpCompositeExtract %1 %53 0 +%57 = OpCompositeExtract %1 %53 1 +%58 = OpCompositeExtract %1 %55 0 +%59 = OpCompositeExtract %1 %55 1 +%60 = OpCompositeConstruct %2 %56 %57 %58 %59 +OpReturnValue %60 +OpFunctionEnd +%63 = OpFunction %61 None %62 +%64 = OpLabel +%65 = OpVariable %30 Function +%68 = OpVariable %30 Function +%73 = OpVariable %30 Function +%66 = OpLoad %2 %4 +OpStore %65 %66 +%67 = OpFunctionCall %2 %32 %65 +%71 = OpAccessChain %70 %14 %69 +%72 = OpLoad %2 %71 +OpStore %68 %72 +%74 = OpAccessChain %70 %20 %69 %69 +%75 = OpLoad %2 %74 +OpStore %73 %75 +%76 = OpFunctionCall %2 %48 %68 %73 +%77 = OpFAdd %2 %67 %76 +%79 = OpAccessChain %70 %20 %78 %69 +%80 = OpLoad %2 %79 +%81 = OpFAdd %2 %77 %80 +OpStore %22 %81 +OpReturn +OpFunctionEnd +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 82 ++; Bound: 92 + ; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %63 "main" %4 %22 + OpExecutionMode %63 OriginUpperLeft + OpSource GLSL 450 +-OpDecorate %4 Location 0 ++OpDecorate %4 Location 1 + OpDecorate %8 RelaxedPrecision +-OpDecorate %8 DescriptorSet 0 ++OpDecorate %8 DescriptorSet 2 + OpDecorate %8 Binding 0 +-OpDecorate %11 DescriptorSet 0 ++OpDecorate %11 DescriptorSet 3 +-OpDecorate %11 Binding 1 ++OpDecorate %11 Binding 0 + OpMemberDecorate %12 0 Offset 0 + OpMemberDecorate %12 0 RelaxedPrecision + OpDecorate %12 Block ++OpDecorate %82 DescriptorSet 3 ++OpDecorate %82 Binding 1 +-OpDecorate %14 DescriptorSet 0 ++OpDecorate %14 DescriptorSet 3 + OpDecorate %14 Binding 2 + OpMemberDecorate %15 0 Offset 0 + OpMemberDecorate %15 0 RelaxedPrecision + OpDecorate %15 BufferBlock +-OpDecorate %20 DescriptorSet 0 +-OpDecorate %20 Binding 3 + OpDecorate %22 RelaxedPrecision +-OpDecorate %22 Location 0 ++OpDecorate %22 Location 1 + OpMemberDecorate %26 0 Offset 0 + OpMemberDecorate %26 1 Offset 4 + OpMemberDecorate %26 2 Offset 8 + OpMemberDecorate %26 3 Offset 12 + OpMemberDecorate %27 0 Offset 0 + OpMemberDecorate %27 1 Offset 16 + OpMemberDecorate %27 2 Offset 20 + OpMemberDecorate %27 3 Offset 24 + OpMemberDecorate %27 4 Offset 28 + OpMemberDecorate %27 5 Offset 32 + OpMemberDecorate %27 6 Offset 48 + OpMemberDecorate %27 7 Offset 64 + OpMemberDecorate %27 2 RelaxedPrecision + OpMemberDecorate %27 4 RelaxedPrecision + OpDecorate %27 Block +-OpDecorate %29 DescriptorSet 0 +-OpDecorate %29 Binding 4 ++OpDecorate %83 DescriptorSet 0 ++OpDecorate %83 Binding 0 + OpDecorate %32 RelaxedPrecision +-OpDecorate %33 RelaxedPrecision ++OpDecorate %84 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %38 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %43 RelaxedPrecision + OpDecorate %48 RelaxedPrecision +-OpDecorate %49 RelaxedPrecision +-OpDecorate %50 RelaxedPrecision ++OpDecorate %85 RelaxedPrecision ++OpDecorate %86 RelaxedPrecision + OpDecorate %52 RelaxedPrecision + OpDecorate %53 RelaxedPrecision + OpDecorate %54 RelaxedPrecision + OpDecorate %55 RelaxedPrecision + OpDecorate %56 RelaxedPrecision + OpDecorate %57 RelaxedPrecision + OpDecorate %58 RelaxedPrecision + OpDecorate %59 RelaxedPrecision + OpDecorate %60 RelaxedPrecision + OpDecorate %67 RelaxedPrecision + OpDecorate %68 RelaxedPrecision + OpDecorate %72 RelaxedPrecision + OpDecorate %73 RelaxedPrecision + OpDecorate %75 RelaxedPrecision + OpDecorate %76 RelaxedPrecision + OpDecorate %77 RelaxedPrecision + OpDecorate %80 RelaxedPrecision + OpDecorate %81 RelaxedPrecision + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 + %5 = OpTypeImage %1 2D 0 0 0 1 Unknown + %6 = OpTypeSampledImage %5 + %9 = OpTypeImage %1 2D 0 0 0 2 Rgba8 + %12 = OpTypeStruct %2 + %15 = OpTypeStruct %2 + %16 = OpTypeInt 32 0 + %17 = OpConstant %16 2 + %18 = OpTypeArray %15 %17 + %23 = OpTypeInt 32 1 + %24 = OpTypeVector %23 4 + %25 = OpTypeVector %16 4 + %26 = OpTypeStruct %1 %1 %1 %1 + %27 = OpTypeStruct %2 %16 %16 %23 %23 %24 %25 %26 + %35 = OpTypeVector %1 2 + %40 = OpTypeVector %23 2 + %61 = OpTypeVoid + %69 = OpConstant %16 0 + %78 = OpConstant %16 1 ++%88 = OpTypePointer Private %2 + %3 = OpTypePointer Input %2 + %7 = OpTypePointer UniformConstant %6 + %10 = OpTypePointer UniformConstant %9 + %13 = OpTypePointer Uniform %12 + %19 = OpTypePointer Uniform %18 ++%89 = OpTypePointer Private %2 + %21 = OpTypePointer Output %2 + %28 = OpTypePointer Uniform %27 + %30 = OpTypePointer Function %2 + %70 = OpTypePointer Uniform %2 + %31 = OpTypeFunction %2 %30 + %47 = OpTypeFunction %2 %30 %30 + %62 = OpTypeFunction %61 + %4 = OpVariable %3 Input + %8 = OpVariable %7 UniformConstant + %11 = OpVariable %10 UniformConstant ++%82 = OpVariable %13 Uniform +-%14 = OpVariable %13 Uniform ++%14 = OpVariable %19 Uniform +-%20 = OpVariable %19 Uniform + %22 = OpVariable %21 Output +-%29 = OpVariable %28 Uniform ++%83 = OpVariable %28 Uniform ++%90 = OpConstant %23 0 ++%91 = OpConstant %1 0.5 + %32 = OpFunction %2 None %31 +-%33 = OpFunctionParameter %30 ++%84 = OpFunctionParameter %30 + %34 = OpLabel + %36 = OpLoad %6 %8 +-%37 = OpLoad %2 %33 ++%37 = OpLoad %2 %84 + %38 = OpVectorShuffle %35 %37 %37 0 1 + %39 = OpImageSampleImplicitLod %2 %36 %38 +-%41 = OpLoad %2 %33 ++%41 = OpLoad %2 %84 + %42 = OpVectorShuffle %35 %41 %41 2 3 + %43 = OpConvertFToS %40 %42 + %44 = OpLoad %9 %11 + %45 = OpImageRead %2 %44 %43 + %46 = OpFAdd %2 %39 %45 + OpReturnValue %46 + OpFunctionEnd + %48 = OpFunction %2 None %47 +-%49 = OpFunctionParameter %30 +-%50 = OpFunctionParameter %30 ++%85 = OpFunctionParameter %30 ++%86 = OpFunctionParameter %30 + %51 = OpLabel +-%52 = OpLoad %2 %49 ++%52 = OpLoad %2 %85 + %53 = OpVectorShuffle %35 %52 %52 0 1 +-%54 = OpLoad %2 %50 ++%54 = OpLoad %2 %86 + %55 = OpVectorShuffle %35 %54 %54 2 3 + %56 = OpCompositeExtract %1 %53 0 + %57 = OpCompositeExtract %1 %53 1 + %58 = OpCompositeExtract %1 %55 0 + %59 = OpCompositeExtract %1 %55 1 + %60 = OpCompositeConstruct %2 %56 %57 %58 %59 + OpReturnValue %60 + OpFunctionEnd + %63 = OpFunction %61 None %62 + %64 = OpLabel + %65 = OpVariable %30 Function + %68 = OpVariable %30 Function + %73 = OpVariable %30 Function + %66 = OpLoad %2 %4 + OpStore %65 %66 + %67 = OpFunctionCall %2 %32 %65 +-%71 = OpAccessChain %70 %14 %69 ++%87 = OpAccessChain %70 %82 %69 +-%72 = OpLoad %2 %71 ++%72 = OpLoad %2 %87 + OpStore %68 %72 +-%74 = OpAccessChain %70 %20 %69 %69 ++%74 = OpAccessChain %70 %14 %69 %69 + %75 = OpLoad %2 %74 + OpStore %73 %75 + %76 = OpFunctionCall %2 %48 %68 %73 + %77 = OpFAdd %2 %67 %76 +-%79 = OpAccessChain %70 %20 %78 %69 ++%79 = OpAccessChain %70 %14 %78 %69 + %80 = OpLoad %2 %79 + %81 = OpFAdd %2 %77 %80 + OpStore %22 %81 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +TEST(DiffTest, DifferentDecorationsFragmentIgnoreLocation) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 82 ++; Bound: 86 + ; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %63 "main" %4 %22 + OpExecutionMode %63 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "_ue" + OpName %8 "_uf" + OpName %11 "_ug" + OpName %12 "_uA" + OpMemberName %12 0 "_ux" + OpName %14 "_uc" + OpName %15 "_uB" + OpMemberName %15 0 "_ux" + OpName %20 "_ud" + OpName %22 "_ucol" + OpName %26 "ANGLEDepthRangeParams" + OpMemberName %26 0 "near" + OpMemberName %26 1 "far" + OpMemberName %26 2 "diff" + OpMemberName %26 3 "reserved" + OpName %27 "ANGLEUniformBlock" + OpMemberName %27 0 "viewport" + OpMemberName %27 1 "clipDistancesEnabled" + OpMemberName %27 2 "xfbActiveUnpaused" + OpMemberName %27 3 "xfbVerticesPerInstance" + OpMemberName %27 4 "numSamples" + OpMemberName %27 5 "xfbBufferOffsets" + OpMemberName %27 6 "acbBufferOffsets" + OpMemberName %27 7 "depthRange" + OpName %29 "ANGLEUniforms" + OpName %33 "_uc" + OpName %32 "_uh" + OpName %49 "_ux" + OpName %50 "_uy" + OpName %48 "_ui" + OpName %63 "main" + OpName %65 "param" + OpName %68 "param" + OpName %73 "param" +-OpDecorate %4 Location 0 ++OpDecorate %4 Location 1 + OpDecorate %8 RelaxedPrecision +-OpDecorate %8 DescriptorSet 0 ++OpDecorate %8 DescriptorSet 2 + OpDecorate %8 Binding 0 +-OpDecorate %11 DescriptorSet 0 ++OpDecorate %11 DescriptorSet 3 +-OpDecorate %11 Binding 1 ++OpDecorate %11 Binding 0 + OpMemberDecorate %12 0 Offset 0 + OpMemberDecorate %12 0 RelaxedPrecision + OpDecorate %12 Block +-OpDecorate %14 DescriptorSet 0 ++OpDecorate %14 DescriptorSet 3 +-OpDecorate %14 Binding 2 ++OpDecorate %14 Binding 1 + OpMemberDecorate %15 0 Offset 0 + OpMemberDecorate %15 0 RelaxedPrecision + OpDecorate %15 BufferBlock +-OpDecorate %20 DescriptorSet 0 ++OpDecorate %20 DescriptorSet 3 +-OpDecorate %20 Binding 3 ++OpDecorate %20 Binding 2 + OpDecorate %22 RelaxedPrecision +-OpDecorate %22 Location 0 ++OpDecorate %22 Location 1 + OpMemberDecorate %26 0 Offset 0 + OpMemberDecorate %26 1 Offset 4 + OpMemberDecorate %26 2 Offset 8 + OpMemberDecorate %26 3 Offset 12 + OpMemberDecorate %27 0 Offset 0 + OpMemberDecorate %27 1 Offset 16 + OpMemberDecorate %27 2 Offset 20 + OpMemberDecorate %27 3 Offset 24 + OpMemberDecorate %27 4 Offset 28 + OpMemberDecorate %27 5 Offset 32 + OpMemberDecorate %27 6 Offset 48 + OpMemberDecorate %27 7 Offset 64 + OpMemberDecorate %27 2 RelaxedPrecision + OpMemberDecorate %27 4 RelaxedPrecision + OpDecorate %27 Block + OpDecorate %29 DescriptorSet 0 +-OpDecorate %29 Binding 4 ++OpDecorate %29 Binding 0 + OpDecorate %32 RelaxedPrecision + OpDecorate %33 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %38 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %43 RelaxedPrecision + OpDecorate %48 RelaxedPrecision + OpDecorate %49 RelaxedPrecision + OpDecorate %50 RelaxedPrecision + OpDecorate %52 RelaxedPrecision + OpDecorate %53 RelaxedPrecision + OpDecorate %54 RelaxedPrecision + OpDecorate %55 RelaxedPrecision + OpDecorate %56 RelaxedPrecision + OpDecorate %57 RelaxedPrecision + OpDecorate %58 RelaxedPrecision + OpDecorate %59 RelaxedPrecision + OpDecorate %60 RelaxedPrecision + OpDecorate %67 RelaxedPrecision + OpDecorate %68 RelaxedPrecision + OpDecorate %72 RelaxedPrecision + OpDecorate %73 RelaxedPrecision + OpDecorate %75 RelaxedPrecision + OpDecorate %76 RelaxedPrecision + OpDecorate %77 RelaxedPrecision + OpDecorate %80 RelaxedPrecision + OpDecorate %81 RelaxedPrecision + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 + %5 = OpTypeImage %1 2D 0 0 0 1 Unknown + %6 = OpTypeSampledImage %5 + %9 = OpTypeImage %1 2D 0 0 0 2 Rgba8 + %12 = OpTypeStruct %2 + %15 = OpTypeStruct %2 + %16 = OpTypeInt 32 0 + %17 = OpConstant %16 2 + %18 = OpTypeArray %15 %17 + %23 = OpTypeInt 32 1 + %24 = OpTypeVector %23 4 + %25 = OpTypeVector %16 4 + %26 = OpTypeStruct %1 %1 %1 %1 + %27 = OpTypeStruct %2 %16 %16 %23 %23 %24 %25 %26 + %35 = OpTypeVector %1 2 + %40 = OpTypeVector %23 2 + %61 = OpTypeVoid + %69 = OpConstant %16 0 + %78 = OpConstant %16 1 ++%82 = OpTypePointer Private %2 + %3 = OpTypePointer Input %2 + %7 = OpTypePointer UniformConstant %6 + %10 = OpTypePointer UniformConstant %9 + %13 = OpTypePointer Uniform %12 + %19 = OpTypePointer Uniform %18 ++%83 = OpTypePointer Private %2 + %21 = OpTypePointer Output %2 + %28 = OpTypePointer Uniform %27 + %30 = OpTypePointer Function %2 + %70 = OpTypePointer Uniform %2 + %31 = OpTypeFunction %2 %30 + %47 = OpTypeFunction %2 %30 %30 + %62 = OpTypeFunction %61 + %4 = OpVariable %3 Input + %8 = OpVariable %7 UniformConstant + %11 = OpVariable %10 UniformConstant + %14 = OpVariable %13 Uniform + %20 = OpVariable %19 Uniform + %22 = OpVariable %21 Output + %29 = OpVariable %28 Uniform ++%84 = OpConstant %23 0 ++%85 = OpConstant %1 0.5 + %32 = OpFunction %2 None %31 + %33 = OpFunctionParameter %30 + %34 = OpLabel + %36 = OpLoad %6 %8 + %37 = OpLoad %2 %33 + %38 = OpVectorShuffle %35 %37 %37 0 1 + %39 = OpImageSampleImplicitLod %2 %36 %38 + %41 = OpLoad %2 %33 + %42 = OpVectorShuffle %35 %41 %41 2 3 + %43 = OpConvertFToS %40 %42 + %44 = OpLoad %9 %11 + %45 = OpImageRead %2 %44 %43 + %46 = OpFAdd %2 %39 %45 + OpReturnValue %46 + OpFunctionEnd + %48 = OpFunction %2 None %47 + %49 = OpFunctionParameter %30 + %50 = OpFunctionParameter %30 + %51 = OpLabel + %52 = OpLoad %2 %49 + %53 = OpVectorShuffle %35 %52 %52 0 1 + %54 = OpLoad %2 %50 + %55 = OpVectorShuffle %35 %54 %54 2 3 + %56 = OpCompositeExtract %1 %53 0 + %57 = OpCompositeExtract %1 %53 1 + %58 = OpCompositeExtract %1 %55 0 + %59 = OpCompositeExtract %1 %55 1 + %60 = OpCompositeConstruct %2 %56 %57 %58 %59 + OpReturnValue %60 + OpFunctionEnd + %63 = OpFunction %61 None %62 + %64 = OpLabel + %65 = OpVariable %30 Function + %68 = OpVariable %30 Function + %73 = OpVariable %30 Function + %66 = OpLoad %2 %4 + OpStore %65 %66 + %67 = OpFunctionCall %2 %32 %65 + %71 = OpAccessChain %70 %14 %69 + %72 = OpLoad %2 %71 + OpStore %68 %72 + %74 = OpAccessChain %70 %20 %69 %69 + %75 = OpLoad %2 %74 + OpStore %73 %75 + %76 = OpFunctionCall %2 %48 %68 %73 + %77 = OpFAdd %2 %67 %76 + %79 = OpAccessChain %70 %20 %78 %69 + %80 = OpLoad %2 %79 + %81 = OpFAdd %2 %77 %80 + OpStore %22 %81 + OpReturn + OpFunctionEnd +)"; + Options options; + options.ignore_location = true; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, DifferentDecorationsFragmentIgnoreSetBindingLocation) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 82 ++; Bound: 86 + ; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %63 "main" %4 %22 + OpExecutionMode %63 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "_ue" + OpName %8 "_uf" + OpName %11 "_ug" + OpName %12 "_uA" + OpMemberName %12 0 "_ux" + OpName %14 "_uc" + OpName %15 "_uB" + OpMemberName %15 0 "_ux" + OpName %20 "_ud" + OpName %22 "_ucol" + OpName %26 "ANGLEDepthRangeParams" + OpMemberName %26 0 "near" + OpMemberName %26 1 "far" + OpMemberName %26 2 "diff" + OpMemberName %26 3 "reserved" + OpName %27 "ANGLEUniformBlock" + OpMemberName %27 0 "viewport" + OpMemberName %27 1 "clipDistancesEnabled" + OpMemberName %27 2 "xfbActiveUnpaused" + OpMemberName %27 3 "xfbVerticesPerInstance" + OpMemberName %27 4 "numSamples" + OpMemberName %27 5 "xfbBufferOffsets" + OpMemberName %27 6 "acbBufferOffsets" + OpMemberName %27 7 "depthRange" + OpName %29 "ANGLEUniforms" + OpName %33 "_uc" + OpName %32 "_uh" + OpName %49 "_ux" + OpName %50 "_uy" + OpName %48 "_ui" + OpName %63 "main" + OpName %65 "param" + OpName %68 "param" + OpName %73 "param" +-OpDecorate %4 Location 0 ++OpDecorate %4 Location 1 + OpDecorate %8 RelaxedPrecision +-OpDecorate %8 DescriptorSet 0 ++OpDecorate %8 DescriptorSet 2 + OpDecorate %8 Binding 0 +-OpDecorate %11 DescriptorSet 0 ++OpDecorate %11 DescriptorSet 3 +-OpDecorate %11 Binding 1 ++OpDecorate %11 Binding 0 + OpMemberDecorate %12 0 Offset 0 + OpMemberDecorate %12 0 RelaxedPrecision + OpDecorate %12 Block +-OpDecorate %14 DescriptorSet 0 ++OpDecorate %14 DescriptorSet 3 +-OpDecorate %14 Binding 2 ++OpDecorate %14 Binding 1 + OpMemberDecorate %15 0 Offset 0 + OpMemberDecorate %15 0 RelaxedPrecision + OpDecorate %15 BufferBlock +-OpDecorate %20 DescriptorSet 0 ++OpDecorate %20 DescriptorSet 3 +-OpDecorate %20 Binding 3 ++OpDecorate %20 Binding 2 + OpDecorate %22 RelaxedPrecision +-OpDecorate %22 Location 0 ++OpDecorate %22 Location 1 + OpMemberDecorate %26 0 Offset 0 + OpMemberDecorate %26 1 Offset 4 + OpMemberDecorate %26 2 Offset 8 + OpMemberDecorate %26 3 Offset 12 + OpMemberDecorate %27 0 Offset 0 + OpMemberDecorate %27 1 Offset 16 + OpMemberDecorate %27 2 Offset 20 + OpMemberDecorate %27 3 Offset 24 + OpMemberDecorate %27 4 Offset 28 + OpMemberDecorate %27 5 Offset 32 + OpMemberDecorate %27 6 Offset 48 + OpMemberDecorate %27 7 Offset 64 + OpMemberDecorate %27 2 RelaxedPrecision + OpMemberDecorate %27 4 RelaxedPrecision + OpDecorate %27 Block + OpDecorate %29 DescriptorSet 0 +-OpDecorate %29 Binding 4 ++OpDecorate %29 Binding 0 + OpDecorate %32 RelaxedPrecision + OpDecorate %33 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %38 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %43 RelaxedPrecision + OpDecorate %48 RelaxedPrecision + OpDecorate %49 RelaxedPrecision + OpDecorate %50 RelaxedPrecision + OpDecorate %52 RelaxedPrecision + OpDecorate %53 RelaxedPrecision + OpDecorate %54 RelaxedPrecision + OpDecorate %55 RelaxedPrecision + OpDecorate %56 RelaxedPrecision + OpDecorate %57 RelaxedPrecision + OpDecorate %58 RelaxedPrecision + OpDecorate %59 RelaxedPrecision + OpDecorate %60 RelaxedPrecision + OpDecorate %67 RelaxedPrecision + OpDecorate %68 RelaxedPrecision + OpDecorate %72 RelaxedPrecision + OpDecorate %73 RelaxedPrecision + OpDecorate %75 RelaxedPrecision + OpDecorate %76 RelaxedPrecision + OpDecorate %77 RelaxedPrecision + OpDecorate %80 RelaxedPrecision + OpDecorate %81 RelaxedPrecision + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 + %5 = OpTypeImage %1 2D 0 0 0 1 Unknown + %6 = OpTypeSampledImage %5 + %9 = OpTypeImage %1 2D 0 0 0 2 Rgba8 + %12 = OpTypeStruct %2 + %15 = OpTypeStruct %2 + %16 = OpTypeInt 32 0 + %17 = OpConstant %16 2 + %18 = OpTypeArray %15 %17 + %23 = OpTypeInt 32 1 + %24 = OpTypeVector %23 4 + %25 = OpTypeVector %16 4 + %26 = OpTypeStruct %1 %1 %1 %1 + %27 = OpTypeStruct %2 %16 %16 %23 %23 %24 %25 %26 + %35 = OpTypeVector %1 2 + %40 = OpTypeVector %23 2 + %61 = OpTypeVoid + %69 = OpConstant %16 0 + %78 = OpConstant %16 1 ++%82 = OpTypePointer Private %2 + %3 = OpTypePointer Input %2 + %7 = OpTypePointer UniformConstant %6 + %10 = OpTypePointer UniformConstant %9 + %13 = OpTypePointer Uniform %12 + %19 = OpTypePointer Uniform %18 ++%83 = OpTypePointer Private %2 + %21 = OpTypePointer Output %2 + %28 = OpTypePointer Uniform %27 + %30 = OpTypePointer Function %2 + %70 = OpTypePointer Uniform %2 + %31 = OpTypeFunction %2 %30 + %47 = OpTypeFunction %2 %30 %30 + %62 = OpTypeFunction %61 + %4 = OpVariable %3 Input + %8 = OpVariable %7 UniformConstant + %11 = OpVariable %10 UniformConstant + %14 = OpVariable %13 Uniform + %20 = OpVariable %19 Uniform + %22 = OpVariable %21 Output + %29 = OpVariable %28 Uniform ++%84 = OpConstant %23 0 ++%85 = OpConstant %1 0.5 + %32 = OpFunction %2 None %31 + %33 = OpFunctionParameter %30 + %34 = OpLabel + %36 = OpLoad %6 %8 + %37 = OpLoad %2 %33 + %38 = OpVectorShuffle %35 %37 %37 0 1 + %39 = OpImageSampleImplicitLod %2 %36 %38 + %41 = OpLoad %2 %33 + %42 = OpVectorShuffle %35 %41 %41 2 3 + %43 = OpConvertFToS %40 %42 + %44 = OpLoad %9 %11 + %45 = OpImageRead %2 %44 %43 + %46 = OpFAdd %2 %39 %45 + OpReturnValue %46 + OpFunctionEnd + %48 = OpFunction %2 None %47 + %49 = OpFunctionParameter %30 + %50 = OpFunctionParameter %30 + %51 = OpLabel + %52 = OpLoad %2 %49 + %53 = OpVectorShuffle %35 %52 %52 0 1 + %54 = OpLoad %2 %50 + %55 = OpVectorShuffle %35 %54 %54 2 3 + %56 = OpCompositeExtract %1 %53 0 + %57 = OpCompositeExtract %1 %53 1 + %58 = OpCompositeExtract %1 %55 0 + %59 = OpCompositeExtract %1 %55 1 + %60 = OpCompositeConstruct %2 %56 %57 %58 %59 + OpReturnValue %60 + OpFunctionEnd + %63 = OpFunction %61 None %62 + %64 = OpLabel + %65 = OpVariable %30 Function + %68 = OpVariable %30 Function + %73 = OpVariable %30 Function + %66 = OpLoad %2 %4 + OpStore %65 %66 + %67 = OpFunctionCall %2 %32 %65 + %71 = OpAccessChain %70 %14 %69 + %72 = OpLoad %2 %71 + OpStore %68 %72 + %74 = OpAccessChain %70 %20 %69 %69 + %75 = OpLoad %2 %74 + OpStore %73 %75 + %76 = OpFunctionCall %2 %48 %68 %73 + %77 = OpFAdd %2 %67 %76 + %79 = OpAccessChain %70 %20 %78 %69 + %80 = OpLoad %2 %79 + %81 = OpFAdd %2 %77 %80 + OpStore %22 %81 + OpReturn + OpFunctionEnd +)"; + Options options; + options.ignore_set_binding = true; + options.ignore_location = true; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/different_decorations_fragment_dst.spvasm b/test/diff/diff_files/different_decorations_fragment_dst.spvasm new file mode 100644 index 00000000..b5d1c9f7 --- /dev/null +++ b/test/diff/diff_files/different_decorations_fragment_dst.spvasm @@ -0,0 +1,199 @@ +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %63 "main" %4 %22 +OpExecutionMode %63 OriginUpperLeft +OpSource GLSL 450 +OpName %4 "_ue" +OpName %8 "_uf" +OpName %11 "_ug" +OpName %12 "_uA" +OpMemberName %12 0 "_ux" +OpName %14 "_uc" +OpName %15 "_uB" +OpMemberName %15 0 "_ux" +OpName %20 "_ud" +OpName %22 "_ucol" +OpName %26 "ANGLEDepthRangeParams" +OpMemberName %26 0 "near" +OpMemberName %26 1 "far" +OpMemberName %26 2 "diff" +OpMemberName %26 3 "reserved" +OpName %27 "ANGLEUniformBlock" +OpMemberName %27 0 "viewport" +OpMemberName %27 1 "clipDistancesEnabled" +OpMemberName %27 2 "xfbActiveUnpaused" +OpMemberName %27 3 "xfbVerticesPerInstance" +OpMemberName %27 4 "numSamples" +OpMemberName %27 5 "xfbBufferOffsets" +OpMemberName %27 6 "acbBufferOffsets" +OpMemberName %27 7 "depthRange" +OpName %29 "ANGLEUniforms" +OpName %33 "_uc" +OpName %32 "_uh" +OpName %49 "_ux" +OpName %50 "_uy" +OpName %48 "_ui" +OpName %63 "main" +OpName %65 "param" +OpName %68 "param" +OpName %73 "param" +OpDecorate %4 Location 1 +OpDecorate %8 RelaxedPrecision +OpDecorate %8 DescriptorSet 2 +OpDecorate %8 Binding 0 +OpDecorate %11 DescriptorSet 3 +OpDecorate %11 Binding 0 +OpMemberDecorate %12 0 Offset 0 +OpMemberDecorate %12 0 RelaxedPrecision +OpDecorate %12 Block +OpDecorate %14 DescriptorSet 3 +OpDecorate %14 Binding 1 +OpMemberDecorate %15 0 Offset 0 +OpMemberDecorate %15 0 RelaxedPrecision +OpDecorate %15 BufferBlock +OpDecorate %20 DescriptorSet 3 +OpDecorate %20 Binding 2 +OpDecorate %22 RelaxedPrecision +OpDecorate %22 Location 1 +OpMemberDecorate %26 0 Offset 0 +OpMemberDecorate %26 1 Offset 4 +OpMemberDecorate %26 2 Offset 8 +OpMemberDecorate %26 3 Offset 12 +OpMemberDecorate %27 0 Offset 0 +OpMemberDecorate %27 1 Offset 16 +OpMemberDecorate %27 2 Offset 20 +OpMemberDecorate %27 3 Offset 24 +OpMemberDecorate %27 4 Offset 28 +OpMemberDecorate %27 5 Offset 32 +OpMemberDecorate %27 6 Offset 48 +OpMemberDecorate %27 7 Offset 64 +OpMemberDecorate %27 2 RelaxedPrecision +OpMemberDecorate %27 4 RelaxedPrecision +OpDecorate %27 Block +OpDecorate %29 DescriptorSet 0 +OpDecorate %29 Binding 0 +OpDecorate %32 RelaxedPrecision +OpDecorate %33 RelaxedPrecision +OpDecorate %36 RelaxedPrecision +OpDecorate %37 RelaxedPrecision +OpDecorate %38 RelaxedPrecision +OpDecorate %39 RelaxedPrecision +OpDecorate %41 RelaxedPrecision +OpDecorate %42 RelaxedPrecision +OpDecorate %43 RelaxedPrecision +OpDecorate %48 RelaxedPrecision +OpDecorate %49 RelaxedPrecision +OpDecorate %50 RelaxedPrecision +OpDecorate %52 RelaxedPrecision +OpDecorate %53 RelaxedPrecision +OpDecorate %54 RelaxedPrecision +OpDecorate %55 RelaxedPrecision +OpDecorate %56 RelaxedPrecision +OpDecorate %57 RelaxedPrecision +OpDecorate %58 RelaxedPrecision +OpDecorate %59 RelaxedPrecision +OpDecorate %60 RelaxedPrecision +OpDecorate %67 RelaxedPrecision +OpDecorate %68 RelaxedPrecision +OpDecorate %72 RelaxedPrecision +OpDecorate %73 RelaxedPrecision +OpDecorate %75 RelaxedPrecision +OpDecorate %76 RelaxedPrecision +OpDecorate %77 RelaxedPrecision +OpDecorate %80 RelaxedPrecision +OpDecorate %81 RelaxedPrecision +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeImage %1 2D 0 0 0 1 Unknown +%6 = OpTypeSampledImage %5 +%9 = OpTypeImage %1 2D 0 0 0 2 Rgba8 +%12 = OpTypeStruct %2 +%15 = OpTypeStruct %2 +%16 = OpTypeInt 32 0 +%17 = OpConstant %16 2 +%18 = OpTypeArray %15 %17 +%23 = OpTypeInt 32 1 +%24 = OpTypeVector %23 4 +%25 = OpTypeVector %16 4 +%26 = OpTypeStruct %1 %1 %1 %1 +%27 = OpTypeStruct %2 %16 %16 %23 %23 %24 %25 %26 +%35 = OpTypeVector %1 2 +%40 = OpTypeVector %23 2 +%61 = OpTypeVoid +%69 = OpConstant %16 0 +%78 = OpConstant %16 1 +%82 = OpTypePointer Private %2 +%3 = OpTypePointer Input %2 +%7 = OpTypePointer UniformConstant %6 +%10 = OpTypePointer UniformConstant %9 +%13 = OpTypePointer Uniform %12 +%19 = OpTypePointer Uniform %18 +%83 = OpTypePointer Private %2 +%21 = OpTypePointer Output %2 +%28 = OpTypePointer Uniform %27 +%30 = OpTypePointer Function %2 +%70 = OpTypePointer Uniform %2 +%31 = OpTypeFunction %2 %30 +%47 = OpTypeFunction %2 %30 %30 +%62 = OpTypeFunction %61 +%4 = OpVariable %3 Input +%8 = OpVariable %7 UniformConstant +%11 = OpVariable %10 UniformConstant +%14 = OpVariable %13 Uniform +%20 = OpVariable %19 Uniform +%22 = OpVariable %21 Output +%29 = OpVariable %28 Uniform +%84 = OpConstant %23 0 +%85 = OpConstant %1 0.5 +%32 = OpFunction %2 None %31 +%33 = OpFunctionParameter %30 +%34 = OpLabel +%36 = OpLoad %6 %8 +%37 = OpLoad %2 %33 +%38 = OpVectorShuffle %35 %37 %37 0 1 +%39 = OpImageSampleImplicitLod %2 %36 %38 +%41 = OpLoad %2 %33 +%42 = OpVectorShuffle %35 %41 %41 2 3 +%43 = OpConvertFToS %40 %42 +%44 = OpLoad %9 %11 +%45 = OpImageRead %2 %44 %43 +%46 = OpFAdd %2 %39 %45 +OpReturnValue %46 +OpFunctionEnd +%48 = OpFunction %2 None %47 +%49 = OpFunctionParameter %30 +%50 = OpFunctionParameter %30 +%51 = OpLabel +%52 = OpLoad %2 %49 +%53 = OpVectorShuffle %35 %52 %52 0 1 +%54 = OpLoad %2 %50 +%55 = OpVectorShuffle %35 %54 %54 2 3 +%56 = OpCompositeExtract %1 %53 0 +%57 = OpCompositeExtract %1 %53 1 +%58 = OpCompositeExtract %1 %55 0 +%59 = OpCompositeExtract %1 %55 1 +%60 = OpCompositeConstruct %2 %56 %57 %58 %59 +OpReturnValue %60 +OpFunctionEnd +%63 = OpFunction %61 None %62 +%64 = OpLabel +%65 = OpVariable %30 Function +%68 = OpVariable %30 Function +%73 = OpVariable %30 Function +%66 = OpLoad %2 %4 +OpStore %65 %66 +%67 = OpFunctionCall %2 %32 %65 +%71 = OpAccessChain %70 %14 %69 +%72 = OpLoad %2 %71 +OpStore %68 %72 +%74 = OpAccessChain %70 %20 %69 %69 +%75 = OpLoad %2 %74 +OpStore %73 %75 +%76 = OpFunctionCall %2 %48 %68 %73 +%77 = OpFAdd %2 %67 %76 +%79 = OpAccessChain %70 %20 %78 %69 +%80 = OpLoad %2 %79 +%81 = OpFAdd %2 %77 %80 +OpStore %22 %81 +OpReturn +OpFunctionEnd diff --git a/test/diff/diff_files/different_decorations_fragment_src.spvasm b/test/diff/diff_files/different_decorations_fragment_src.spvasm new file mode 100644 index 00000000..2c8cd644 --- /dev/null +++ b/test/diff/diff_files/different_decorations_fragment_src.spvasm @@ -0,0 +1,198 @@ +;; Test where variable set/binding/location decorations are different between +;; src and dst fragment shaders. +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %63 "main" %4 %22 +OpExecutionMode %63 OriginUpperLeft +OpSource GLSL 450 +OpName %4 "_ue" +OpName %8 "_uf" +OpName %11 "_ug" +OpName %12 "_uA" +OpMemberName %12 0 "_ux" +OpName %14 "_uc" +OpName %15 "_uB" +OpMemberName %15 0 "_ux" +OpName %20 "_ud" +OpName %22 "_ucol" +OpName %26 "ANGLEDepthRangeParams" +OpMemberName %26 0 "near" +OpMemberName %26 1 "far" +OpMemberName %26 2 "diff" +OpMemberName %26 3 "reserved" +OpName %27 "ANGLEUniformBlock" +OpMemberName %27 0 "viewport" +OpMemberName %27 1 "clipDistancesEnabled" +OpMemberName %27 2 "xfbActiveUnpaused" +OpMemberName %27 3 "xfbVerticesPerInstance" +OpMemberName %27 4 "numSamples" +OpMemberName %27 5 "xfbBufferOffsets" +OpMemberName %27 6 "acbBufferOffsets" +OpMemberName %27 7 "depthRange" +OpName %29 "ANGLEUniforms" +OpName %33 "_uc" +OpName %32 "_uh" +OpName %49 "_ux" +OpName %50 "_uy" +OpName %48 "_ui" +OpName %63 "main" +OpName %65 "param" +OpName %68 "param" +OpName %73 "param" +OpDecorate %4 Location 0 +OpDecorate %8 RelaxedPrecision +OpDecorate %8 DescriptorSet 0 +OpDecorate %8 Binding 0 +OpDecorate %11 DescriptorSet 0 +OpDecorate %11 Binding 1 +OpMemberDecorate %12 0 Offset 0 +OpMemberDecorate %12 0 RelaxedPrecision +OpDecorate %12 Block +OpDecorate %14 DescriptorSet 0 +OpDecorate %14 Binding 2 +OpMemberDecorate %15 0 Offset 0 +OpMemberDecorate %15 0 RelaxedPrecision +OpDecorate %15 BufferBlock +OpDecorate %20 DescriptorSet 0 +OpDecorate %20 Binding 3 +OpDecorate %22 RelaxedPrecision +OpDecorate %22 Location 0 +OpMemberDecorate %26 0 Offset 0 +OpMemberDecorate %26 1 Offset 4 +OpMemberDecorate %26 2 Offset 8 +OpMemberDecorate %26 3 Offset 12 +OpMemberDecorate %27 0 Offset 0 +OpMemberDecorate %27 1 Offset 16 +OpMemberDecorate %27 2 Offset 20 +OpMemberDecorate %27 3 Offset 24 +OpMemberDecorate %27 4 Offset 28 +OpMemberDecorate %27 5 Offset 32 +OpMemberDecorate %27 6 Offset 48 +OpMemberDecorate %27 7 Offset 64 +OpMemberDecorate %27 2 RelaxedPrecision +OpMemberDecorate %27 4 RelaxedPrecision +OpDecorate %27 Block +OpDecorate %29 DescriptorSet 0 +OpDecorate %29 Binding 4 +OpDecorate %32 RelaxedPrecision +OpDecorate %33 RelaxedPrecision +OpDecorate %36 RelaxedPrecision +OpDecorate %37 RelaxedPrecision +OpDecorate %38 RelaxedPrecision +OpDecorate %39 RelaxedPrecision +OpDecorate %41 RelaxedPrecision +OpDecorate %42 RelaxedPrecision +OpDecorate %43 RelaxedPrecision +OpDecorate %48 RelaxedPrecision +OpDecorate %49 RelaxedPrecision +OpDecorate %50 RelaxedPrecision +OpDecorate %52 RelaxedPrecision +OpDecorate %53 RelaxedPrecision +OpDecorate %54 RelaxedPrecision +OpDecorate %55 RelaxedPrecision +OpDecorate %56 RelaxedPrecision +OpDecorate %57 RelaxedPrecision +OpDecorate %58 RelaxedPrecision +OpDecorate %59 RelaxedPrecision +OpDecorate %60 RelaxedPrecision +OpDecorate %67 RelaxedPrecision +OpDecorate %68 RelaxedPrecision +OpDecorate %72 RelaxedPrecision +OpDecorate %73 RelaxedPrecision +OpDecorate %75 RelaxedPrecision +OpDecorate %76 RelaxedPrecision +OpDecorate %77 RelaxedPrecision +OpDecorate %80 RelaxedPrecision +OpDecorate %81 RelaxedPrecision +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeImage %1 2D 0 0 0 1 Unknown +%6 = OpTypeSampledImage %5 +%9 = OpTypeImage %1 2D 0 0 0 2 Rgba8 +%12 = OpTypeStruct %2 +%15 = OpTypeStruct %2 +%16 = OpTypeInt 32 0 +%17 = OpConstant %16 2 +%18 = OpTypeArray %15 %17 +%23 = OpTypeInt 32 1 +%24 = OpTypeVector %23 4 +%25 = OpTypeVector %16 4 +%26 = OpTypeStruct %1 %1 %1 %1 +%27 = OpTypeStruct %2 %16 %16 %23 %23 %24 %25 %26 +%35 = OpTypeVector %1 2 +%40 = OpTypeVector %23 2 +%61 = OpTypeVoid +%69 = OpConstant %16 0 +%78 = OpConstant %16 1 +%3 = OpTypePointer Input %2 +%7 = OpTypePointer UniformConstant %6 +%10 = OpTypePointer UniformConstant %9 +%13 = OpTypePointer Uniform %12 +%19 = OpTypePointer Uniform %18 +%21 = OpTypePointer Output %2 +%28 = OpTypePointer Uniform %27 +%30 = OpTypePointer Function %2 +%70 = OpTypePointer Uniform %2 +%31 = OpTypeFunction %2 %30 +%47 = OpTypeFunction %2 %30 %30 +%62 = OpTypeFunction %61 +%4 = OpVariable %3 Input +%8 = OpVariable %7 UniformConstant +%11 = OpVariable %10 UniformConstant +%14 = OpVariable %13 Uniform +%20 = OpVariable %19 Uniform +%22 = OpVariable %21 Output +%29 = OpVariable %28 Uniform +%32 = OpFunction %2 None %31 +%33 = OpFunctionParameter %30 +%34 = OpLabel +%36 = OpLoad %6 %8 +%37 = OpLoad %2 %33 +%38 = OpVectorShuffle %35 %37 %37 0 1 +%39 = OpImageSampleImplicitLod %2 %36 %38 +%41 = OpLoad %2 %33 +%42 = OpVectorShuffle %35 %41 %41 2 3 +%43 = OpConvertFToS %40 %42 +%44 = OpLoad %9 %11 +%45 = OpImageRead %2 %44 %43 +%46 = OpFAdd %2 %39 %45 +OpReturnValue %46 +OpFunctionEnd +%48 = OpFunction %2 None %47 +%49 = OpFunctionParameter %30 +%50 = OpFunctionParameter %30 +%51 = OpLabel +%52 = OpLoad %2 %49 +%53 = OpVectorShuffle %35 %52 %52 0 1 +%54 = OpLoad %2 %50 +%55 = OpVectorShuffle %35 %54 %54 2 3 +%56 = OpCompositeExtract %1 %53 0 +%57 = OpCompositeExtract %1 %53 1 +%58 = OpCompositeExtract %1 %55 0 +%59 = OpCompositeExtract %1 %55 1 +%60 = OpCompositeConstruct %2 %56 %57 %58 %59 +OpReturnValue %60 +OpFunctionEnd +%63 = OpFunction %61 None %62 +%64 = OpLabel +%65 = OpVariable %30 Function +%68 = OpVariable %30 Function +%73 = OpVariable %30 Function +%66 = OpLoad %2 %4 +OpStore %65 %66 +%67 = OpFunctionCall %2 %32 %65 +%71 = OpAccessChain %70 %14 %69 +%72 = OpLoad %2 %71 +OpStore %68 %72 +%74 = OpAccessChain %70 %20 %69 %69 +%75 = OpLoad %2 %74 +OpStore %73 %75 +%76 = OpFunctionCall %2 %48 %68 %73 +%77 = OpFAdd %2 %67 %76 +%79 = OpAccessChain %70 %20 %78 %69 +%80 = OpLoad %2 %79 +%81 = OpFAdd %2 %77 %80 +OpStore %22 %81 +OpReturn +OpFunctionEnd + diff --git a/test/diff/diff_files/different_decorations_vertex_autogen.cpp b/test/diff/diff_files/different_decorations_vertex_autogen.cpp new file mode 100644 index 00000000..f65ee5a1 --- /dev/null +++ b/test/diff/diff_files/different_decorations_vertex_autogen.cpp @@ -0,0 +1,1322 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Test where variable set/binding/location decorations are different between +// src and dst vertex shaders. +constexpr char kSrc[] = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %40 "main" %4 %5 %6 %8 %20 %25 +OpSource GLSL 450 +OpName %4 "_ub" +OpName %5 "_uc" +OpName %6 "_ud" +OpName %8 "_ue" +OpName %9 "defaultUniformsVS" +OpMemberName %9 0 "_ua" +OpName %11 "" +OpName %16 "ANGLEDepthRangeParams" +OpMemberName %16 0 "near" +OpMemberName %16 1 "far" +OpMemberName %16 2 "diff" +OpMemberName %16 3 "reserved" +OpName %17 "ANGLEUniformBlock" +OpMemberName %17 0 "viewport" +OpMemberName %17 1 "clipDistancesEnabled" +OpMemberName %17 2 "xfbActiveUnpaused" +OpMemberName %17 3 "xfbVerticesPerInstance" +OpMemberName %17 4 "numSamples" +OpMemberName %17 5 "xfbBufferOffsets" +OpMemberName %17 6 "acbBufferOffsets" +OpMemberName %17 7 "depthRange" +OpName %19 "ANGLEUniforms" +OpName %20 "ANGLEXfbPosition" +OpName %23 "gl_PerVertex" +OpMemberName %23 0 "gl_Position" +OpMemberName %23 1 "gl_PointSize" +OpMemberName %23 2 "gl_ClipDistance" +OpMemberName %23 3 "gl_CullDistance" +OpName %25 "" +OpName %29 "_ua" +OpName %28 "_uf" +OpName %33 "_uf" +OpName %32 "_ug" +OpName %40 "main" +OpName %42 "param" +OpName %50 "param" +OpName %53 "param" +OpDecorate %4 Location 0 +OpDecorate %5 Location 1 +OpDecorate %6 Location 2 +OpDecorate %8 Location 0 +OpMemberDecorate %9 0 Offset 0 +OpDecorate %9 Block +OpDecorate %11 DescriptorSet 0 +OpDecorate %11 Binding 0 +OpMemberDecorate %16 0 Offset 0 +OpMemberDecorate %16 1 Offset 4 +OpMemberDecorate %16 2 Offset 8 +OpMemberDecorate %16 3 Offset 12 +OpMemberDecorate %17 0 Offset 0 +OpMemberDecorate %17 1 Offset 16 +OpMemberDecorate %17 2 Offset 20 +OpMemberDecorate %17 3 Offset 24 +OpMemberDecorate %17 4 Offset 28 +OpMemberDecorate %17 5 Offset 32 +OpMemberDecorate %17 6 Offset 48 +OpMemberDecorate %17 7 Offset 64 +OpMemberDecorate %17 2 RelaxedPrecision +OpMemberDecorate %17 4 RelaxedPrecision +OpDecorate %17 Block +OpDecorate %19 DescriptorSet 0 +OpDecorate %19 Binding 1 +OpDecorate %20 Location 1 +OpMemberDecorate %23 0 BuiltIn Position +OpMemberDecorate %23 1 BuiltIn PointSize +OpMemberDecorate %23 2 BuiltIn ClipDistance +OpMemberDecorate %23 3 BuiltIn CullDistance +OpDecorate %23 Block +OpDecorate %28 RelaxedPrecision +OpDecorate %29 RelaxedPrecision +OpDecorate %31 RelaxedPrecision +OpDecorate %32 RelaxedPrecision +OpDecorate %33 RelaxedPrecision +OpDecorate %35 RelaxedPrecision +OpDecorate %36 RelaxedPrecision +OpDecorate %37 RelaxedPrecision +OpDecorate %44 RelaxedPrecision +OpDecorate %52 RelaxedPrecision +OpDecorate %55 RelaxedPrecision +OpDecorate %56 RelaxedPrecision +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%9 = OpTypeStruct %2 +%12 = OpTypeInt 32 0 +%13 = OpTypeInt 32 1 +%14 = OpTypeVector %13 4 +%15 = OpTypeVector %12 4 +%16 = OpTypeStruct %1 %1 %1 %1 +%17 = OpTypeStruct %2 %12 %12 %13 %13 %14 %15 %16 +%21 = OpConstant %12 8 +%22 = OpTypeArray %1 %21 +%23 = OpTypeStruct %2 %1 %22 %22 +%38 = OpTypeVoid +%45 = OpConstant %12 0 +%3 = OpTypePointer Input %2 +%7 = OpTypePointer Output %2 +%10 = OpTypePointer Uniform %9 +%18 = OpTypePointer Uniform %17 +%24 = OpTypePointer Output %23 +%26 = OpTypePointer Function %2 +%46 = OpTypePointer Uniform %2 +%27 = OpTypeFunction %2 %26 +%39 = OpTypeFunction %38 +%4 = OpVariable %3 Input +%5 = OpVariable %3 Input +%6 = OpVariable %3 Input +%8 = OpVariable %7 Output +%11 = OpVariable %10 Uniform +%19 = OpVariable %18 Uniform +%20 = OpVariable %7 Output +%25 = OpVariable %24 Output +%28 = OpFunction %2 None %27 +%29 = OpFunctionParameter %26 +%30 = OpLabel +%31 = OpLoad %2 %29 +OpReturnValue %31 +OpFunctionEnd +%32 = OpFunction %2 None %27 +%33 = OpFunctionParameter %26 +%34 = OpLabel +%35 = OpLoad %2 %33 +%36 = OpLoad %2 %33 +%37 = OpFAdd %2 %35 %36 +OpReturnValue %37 +OpFunctionEnd +%40 = OpFunction %38 None %39 +%41 = OpLabel +%42 = OpVariable %26 Function +%50 = OpVariable %26 Function +%53 = OpVariable %26 Function +%43 = OpLoad %2 %4 +OpStore %42 %43 +%44 = OpFunctionCall %2 %28 %42 +%47 = OpAccessChain %46 %11 %45 +%48 = OpLoad %2 %47 +%49 = OpFAdd %2 %44 %48 +OpStore %8 %49 +%51 = OpLoad %2 %5 +OpStore %50 %51 +%52 = OpFunctionCall %2 %32 %50 +%54 = OpLoad %2 %6 +OpStore %53 %54 +%55 = OpFunctionCall %2 %28 %53 +%56 = OpFAdd %2 %52 %55 +%57 = OpAccessChain %7 %25 %45 +OpStore %57 %56 +OpReturn +OpFunctionEnd +)"; +constexpr char kDst[] = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %40 "main" %4 %5 %6 %8 %25 +OpSource GLSL 450 +OpName %4 "_ub" +OpName %5 "_uc" +OpName %6 "_ud" +OpName %8 "_ue" +OpName %9 "defaultUniformsVS" +OpMemberName %9 0 "_ua" +OpName %11 "" +OpName %16 "ANGLEDepthRangeParams" +OpMemberName %16 0 "near" +OpMemberName %16 1 "far" +OpMemberName %16 2 "diff" +OpMemberName %16 3 "reserved" +OpName %17 "ANGLEUniformBlock" +OpMemberName %17 0 "viewport" +OpMemberName %17 1 "clipDistancesEnabled" +OpMemberName %17 2 "xfbActiveUnpaused" +OpMemberName %17 3 "xfbVerticesPerInstance" +OpMemberName %17 4 "numSamples" +OpMemberName %17 5 "xfbBufferOffsets" +OpMemberName %17 6 "acbBufferOffsets" +OpMemberName %17 7 "depthRange" +OpName %19 "ANGLEUniforms" +OpName %23 "gl_PerVertex" +OpMemberName %23 0 "gl_Position" +OpName %25 "" +OpName %29 "_ua" +OpName %28 "_uf" +OpName %33 "_uf" +OpName %32 "_ug" +OpName %40 "main" +OpName %42 "param" +OpName %50 "param" +OpName %53 "param" +OpDecorate %4 Location 1 +OpDecorate %5 Location 2 +OpDecorate %6 Location 0 +OpDecorate %8 Location 1 +OpMemberDecorate %9 0 Offset 0 +OpDecorate %9 Block +OpDecorate %11 DescriptorSet 0 +OpDecorate %11 Binding 1 +OpMemberDecorate %16 0 Offset 0 +OpMemberDecorate %16 1 Offset 4 +OpMemberDecorate %16 2 Offset 8 +OpMemberDecorate %16 3 Offset 12 +OpMemberDecorate %17 0 Offset 0 +OpMemberDecorate %17 1 Offset 16 +OpMemberDecorate %17 2 Offset 20 +OpMemberDecorate %17 3 Offset 24 +OpMemberDecorate %17 4 Offset 28 +OpMemberDecorate %17 5 Offset 32 +OpMemberDecorate %17 6 Offset 48 +OpMemberDecorate %17 7 Offset 64 +OpMemberDecorate %17 2 RelaxedPrecision +OpMemberDecorate %17 4 RelaxedPrecision +OpDecorate %17 Block +OpDecorate %19 DescriptorSet 2 +OpDecorate %19 Binding 0 +OpMemberDecorate %23 0 BuiltIn Position +OpDecorate %23 Block +OpDecorate %28 RelaxedPrecision +OpDecorate %29 RelaxedPrecision +OpDecorate %31 RelaxedPrecision +OpDecorate %32 RelaxedPrecision +OpDecorate %33 RelaxedPrecision +OpDecorate %35 RelaxedPrecision +OpDecorate %36 RelaxedPrecision +OpDecorate %37 RelaxedPrecision +OpDecorate %44 RelaxedPrecision +OpDecorate %52 RelaxedPrecision +OpDecorate %55 RelaxedPrecision +OpDecorate %56 RelaxedPrecision +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%9 = OpTypeStruct %2 +%12 = OpTypeInt 32 0 +%13 = OpTypeInt 32 1 +%14 = OpTypeVector %13 4 +%15 = OpTypeVector %12 4 +%16 = OpTypeStruct %1 %1 %1 %1 +%17 = OpTypeStruct %2 %12 %12 %13 %13 %14 %15 %16 +%21 = OpConstant %12 8 +%22 = OpTypeArray %1 %21 +%23 = OpTypeStruct %2 +%38 = OpTypeVoid +%45 = OpConstant %12 0 +%58 = OpTypePointer Private %2 +%3 = OpTypePointer Input %2 +%59 = OpTypePointer Private %2 +%7 = OpTypePointer Output %2 +%10 = OpTypePointer Uniform %9 +%18 = OpTypePointer Uniform %17 +%24 = OpTypePointer Output %23 +%26 = OpTypePointer Function %2 +%46 = OpTypePointer Uniform %2 +%27 = OpTypeFunction %2 %26 +%39 = OpTypeFunction %38 +%4 = OpVariable %3 Input +%5 = OpVariable %3 Input +%6 = OpVariable %3 Input +%8 = OpVariable %7 Output +%11 = OpVariable %10 Uniform +%19 = OpVariable %18 Uniform +%20 = OpVariable %59 Private +%25 = OpVariable %24 Output +%60 = OpConstant %13 0 +%61 = OpConstant %1 0.5 +%28 = OpFunction %2 None %27 +%29 = OpFunctionParameter %26 +%30 = OpLabel +%31 = OpLoad %2 %29 +OpReturnValue %31 +OpFunctionEnd +%32 = OpFunction %2 None %27 +%33 = OpFunctionParameter %26 +%34 = OpLabel +%35 = OpLoad %2 %33 +%36 = OpLoad %2 %33 +%37 = OpFAdd %2 %35 %36 +OpReturnValue %37 +OpFunctionEnd +%40 = OpFunction %38 None %39 +%41 = OpLabel +%42 = OpVariable %26 Function +%50 = OpVariable %26 Function +%53 = OpVariable %26 Function +%43 = OpLoad %2 %4 +OpStore %42 %43 +%44 = OpFunctionCall %2 %28 %42 +%47 = OpAccessChain %46 %11 %45 +%48 = OpLoad %2 %47 +%49 = OpFAdd %2 %44 %48 +OpStore %8 %49 +%51 = OpLoad %2 %5 +OpStore %50 %51 +%52 = OpFunctionCall %2 %32 %50 +%54 = OpLoad %2 %6 +OpStore %53 %54 +%55 = OpFunctionCall %2 %28 %53 +%56 = OpFAdd %2 %52 %55 +%57 = OpAccessChain %7 %25 %45 +OpStore %57 %56 +%62 = OpAccessChain %7 %25 %60 +%63 = OpLoad %2 %62 +%64 = OpCompositeExtract %1 %63 0 +%65 = OpCompositeExtract %1 %63 1 +%66 = OpCompositeExtract %1 %63 2 +%67 = OpCompositeExtract %1 %63 3 +%69 = OpFNegate %1 %64 +%70 = OpFAdd %1 %66 %67 +%71 = OpFMul %1 %70 %61 +%68 = OpCompositeConstruct %2 %65 %69 %71 %67 +OpStore %62 %68 +OpReturn +OpFunctionEnd +)"; + +TEST(DiffTest, DifferentDecorationsVertex) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 58 ++; Bound: 73 + ; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 +-OpEntryPoint Vertex %40 "main" %4 %5 %6 %8 %20 %25 ++OpEntryPoint Vertex %40 "main" %4 %5 %6 %8 %25 + OpSource GLSL 450 + OpName %4 "_ub" + OpName %5 "_uc" + OpName %6 "_ud" + OpName %8 "_ue" + OpName %9 "defaultUniformsVS" + OpMemberName %9 0 "_ua" + OpName %11 "" + OpName %16 "ANGLEDepthRangeParams" + OpMemberName %16 0 "near" + OpMemberName %16 1 "far" + OpMemberName %16 2 "diff" + OpMemberName %16 3 "reserved" + OpName %17 "ANGLEUniformBlock" + OpMemberName %17 0 "viewport" + OpMemberName %17 1 "clipDistancesEnabled" + OpMemberName %17 2 "xfbActiveUnpaused" + OpMemberName %17 3 "xfbVerticesPerInstance" + OpMemberName %17 4 "numSamples" + OpMemberName %17 5 "xfbBufferOffsets" + OpMemberName %17 6 "acbBufferOffsets" + OpMemberName %17 7 "depthRange" + OpName %19 "ANGLEUniforms" +-OpName %20 "ANGLEXfbPosition" + OpName %23 "gl_PerVertex" + OpMemberName %23 0 "gl_Position" +-OpMemberName %23 1 "gl_PointSize" +-OpMemberName %23 2 "gl_ClipDistance" +-OpMemberName %23 3 "gl_CullDistance" + OpName %25 "" + OpName %29 "_ua" + OpName %28 "_uf" + OpName %33 "_uf" + OpName %32 "_ug" + OpName %40 "main" + OpName %42 "param" + OpName %50 "param" + OpName %53 "param" +-OpDecorate %4 Location 0 ++OpDecorate %4 Location 1 +-OpDecorate %5 Location 1 ++OpDecorate %5 Location 2 +-OpDecorate %6 Location 2 ++OpDecorate %6 Location 0 +-OpDecorate %8 Location 0 ++OpDecorate %8 Location 1 + OpMemberDecorate %9 0 Offset 0 + OpDecorate %9 Block + OpDecorate %11 DescriptorSet 0 +-OpDecorate %11 Binding 0 ++OpDecorate %11 Binding 1 + OpMemberDecorate %16 0 Offset 0 + OpMemberDecorate %16 1 Offset 4 + OpMemberDecorate %16 2 Offset 8 + OpMemberDecorate %16 3 Offset 12 + OpMemberDecorate %17 0 Offset 0 + OpMemberDecorate %17 1 Offset 16 + OpMemberDecorate %17 2 Offset 20 + OpMemberDecorate %17 3 Offset 24 + OpMemberDecorate %17 4 Offset 28 + OpMemberDecorate %17 5 Offset 32 + OpMemberDecorate %17 6 Offset 48 + OpMemberDecorate %17 7 Offset 64 + OpMemberDecorate %17 2 RelaxedPrecision + OpMemberDecorate %17 4 RelaxedPrecision + OpDecorate %17 Block +-OpDecorate %19 DescriptorSet 0 ++OpDecorate %19 DescriptorSet 2 +-OpDecorate %19 Binding 1 ++OpDecorate %19 Binding 0 +-OpDecorate %20 Location 1 + OpMemberDecorate %23 0 BuiltIn Position +-OpMemberDecorate %23 1 BuiltIn PointSize +-OpMemberDecorate %23 2 BuiltIn ClipDistance +-OpMemberDecorate %23 3 BuiltIn CullDistance + OpDecorate %23 Block + OpDecorate %28 RelaxedPrecision + OpDecorate %29 RelaxedPrecision + OpDecorate %31 RelaxedPrecision + OpDecorate %32 RelaxedPrecision + OpDecorate %33 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %44 RelaxedPrecision + OpDecorate %52 RelaxedPrecision + OpDecorate %55 RelaxedPrecision + OpDecorate %56 RelaxedPrecision + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 + %9 = OpTypeStruct %2 + %12 = OpTypeInt 32 0 + %13 = OpTypeInt 32 1 + %14 = OpTypeVector %13 4 + %15 = OpTypeVector %12 4 + %16 = OpTypeStruct %1 %1 %1 %1 + %17 = OpTypeStruct %2 %12 %12 %13 %13 %14 %15 %16 + %21 = OpConstant %12 8 + %22 = OpTypeArray %1 %21 +-%23 = OpTypeStruct %2 %1 %22 %22 ++%23 = OpTypeStruct %2 + %38 = OpTypeVoid + %45 = OpConstant %12 0 ++%59 = OpTypePointer Private %2 + %3 = OpTypePointer Input %2 ++%60 = OpTypePointer Private %2 + %7 = OpTypePointer Output %2 + %10 = OpTypePointer Uniform %9 + %18 = OpTypePointer Uniform %17 + %24 = OpTypePointer Output %23 + %26 = OpTypePointer Function %2 + %46 = OpTypePointer Uniform %2 + %27 = OpTypeFunction %2 %26 + %39 = OpTypeFunction %38 + %4 = OpVariable %3 Input + %5 = OpVariable %3 Input + %6 = OpVariable %3 Input + %8 = OpVariable %7 Output + %11 = OpVariable %10 Uniform + %19 = OpVariable %18 Uniform +-%20 = OpVariable %7 Output ++%58 = OpVariable %60 Private + %25 = OpVariable %24 Output ++%61 = OpConstant %13 0 ++%62 = OpConstant %1 0.5 + %28 = OpFunction %2 None %27 + %29 = OpFunctionParameter %26 + %30 = OpLabel + %31 = OpLoad %2 %29 + OpReturnValue %31 + OpFunctionEnd + %32 = OpFunction %2 None %27 + %33 = OpFunctionParameter %26 + %34 = OpLabel + %35 = OpLoad %2 %33 + %36 = OpLoad %2 %33 + %37 = OpFAdd %2 %35 %36 + OpReturnValue %37 + OpFunctionEnd + %40 = OpFunction %38 None %39 + %41 = OpLabel + %42 = OpVariable %26 Function + %50 = OpVariable %26 Function + %53 = OpVariable %26 Function + %43 = OpLoad %2 %4 + OpStore %42 %43 + %44 = OpFunctionCall %2 %28 %42 + %47 = OpAccessChain %46 %11 %45 + %48 = OpLoad %2 %47 + %49 = OpFAdd %2 %44 %48 + OpStore %8 %49 + %51 = OpLoad %2 %5 + OpStore %50 %51 + %52 = OpFunctionCall %2 %32 %50 + %54 = OpLoad %2 %6 + OpStore %53 %54 + %55 = OpFunctionCall %2 %28 %53 + %56 = OpFAdd %2 %52 %55 + %57 = OpAccessChain %7 %25 %45 + OpStore %57 %56 ++%63 = OpAccessChain %7 %25 %61 ++%64 = OpLoad %2 %63 ++%65 = OpCompositeExtract %1 %64 0 ++%66 = OpCompositeExtract %1 %64 1 ++%67 = OpCompositeExtract %1 %64 2 ++%68 = OpCompositeExtract %1 %64 3 ++%70 = OpFNegate %1 %65 ++%71 = OpFAdd %1 %67 %68 ++%72 = OpFMul %1 %71 %62 ++%69 = OpCompositeConstruct %2 %66 %70 %72 %68 ++OpStore %63 %69 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, DifferentDecorationsVertexNoDebug) { + constexpr char kSrcNoDebug[] = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %40 "main" %4 %5 %6 %8 %20 %25 +OpSource GLSL 450 +OpDecorate %4 Location 0 +OpDecorate %5 Location 1 +OpDecorate %6 Location 2 +OpDecorate %8 Location 0 +OpMemberDecorate %9 0 Offset 0 +OpDecorate %9 Block +OpDecorate %11 DescriptorSet 0 +OpDecorate %11 Binding 0 +OpMemberDecorate %16 0 Offset 0 +OpMemberDecorate %16 1 Offset 4 +OpMemberDecorate %16 2 Offset 8 +OpMemberDecorate %16 3 Offset 12 +OpMemberDecorate %17 0 Offset 0 +OpMemberDecorate %17 1 Offset 16 +OpMemberDecorate %17 2 Offset 20 +OpMemberDecorate %17 3 Offset 24 +OpMemberDecorate %17 4 Offset 28 +OpMemberDecorate %17 5 Offset 32 +OpMemberDecorate %17 6 Offset 48 +OpMemberDecorate %17 7 Offset 64 +OpMemberDecorate %17 2 RelaxedPrecision +OpMemberDecorate %17 4 RelaxedPrecision +OpDecorate %17 Block +OpDecorate %19 DescriptorSet 0 +OpDecorate %19 Binding 1 +OpDecorate %20 Location 1 +OpMemberDecorate %23 0 BuiltIn Position +OpMemberDecorate %23 1 BuiltIn PointSize +OpMemberDecorate %23 2 BuiltIn ClipDistance +OpMemberDecorate %23 3 BuiltIn CullDistance +OpDecorate %23 Block +OpDecorate %28 RelaxedPrecision +OpDecorate %29 RelaxedPrecision +OpDecorate %31 RelaxedPrecision +OpDecorate %32 RelaxedPrecision +OpDecorate %33 RelaxedPrecision +OpDecorate %35 RelaxedPrecision +OpDecorate %36 RelaxedPrecision +OpDecorate %37 RelaxedPrecision +OpDecorate %44 RelaxedPrecision +OpDecorate %52 RelaxedPrecision +OpDecorate %55 RelaxedPrecision +OpDecorate %56 RelaxedPrecision +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%9 = OpTypeStruct %2 +%12 = OpTypeInt 32 0 +%13 = OpTypeInt 32 1 +%14 = OpTypeVector %13 4 +%15 = OpTypeVector %12 4 +%16 = OpTypeStruct %1 %1 %1 %1 +%17 = OpTypeStruct %2 %12 %12 %13 %13 %14 %15 %16 +%21 = OpConstant %12 8 +%22 = OpTypeArray %1 %21 +%23 = OpTypeStruct %2 %1 %22 %22 +%38 = OpTypeVoid +%45 = OpConstant %12 0 +%3 = OpTypePointer Input %2 +%7 = OpTypePointer Output %2 +%10 = OpTypePointer Uniform %9 +%18 = OpTypePointer Uniform %17 +%24 = OpTypePointer Output %23 +%26 = OpTypePointer Function %2 +%46 = OpTypePointer Uniform %2 +%27 = OpTypeFunction %2 %26 +%39 = OpTypeFunction %38 +%4 = OpVariable %3 Input +%5 = OpVariable %3 Input +%6 = OpVariable %3 Input +%8 = OpVariable %7 Output +%11 = OpVariable %10 Uniform +%19 = OpVariable %18 Uniform +%20 = OpVariable %7 Output +%25 = OpVariable %24 Output +%28 = OpFunction %2 None %27 +%29 = OpFunctionParameter %26 +%30 = OpLabel +%31 = OpLoad %2 %29 +OpReturnValue %31 +OpFunctionEnd +%32 = OpFunction %2 None %27 +%33 = OpFunctionParameter %26 +%34 = OpLabel +%35 = OpLoad %2 %33 +%36 = OpLoad %2 %33 +%37 = OpFAdd %2 %35 %36 +OpReturnValue %37 +OpFunctionEnd +%40 = OpFunction %38 None %39 +%41 = OpLabel +%42 = OpVariable %26 Function +%50 = OpVariable %26 Function +%53 = OpVariable %26 Function +%43 = OpLoad %2 %4 +OpStore %42 %43 +%44 = OpFunctionCall %2 %28 %42 +%47 = OpAccessChain %46 %11 %45 +%48 = OpLoad %2 %47 +%49 = OpFAdd %2 %44 %48 +OpStore %8 %49 +%51 = OpLoad %2 %5 +OpStore %50 %51 +%52 = OpFunctionCall %2 %32 %50 +%54 = OpLoad %2 %6 +OpStore %53 %54 +%55 = OpFunctionCall %2 %28 %53 +%56 = OpFAdd %2 %52 %55 +%57 = OpAccessChain %7 %25 %45 +OpStore %57 %56 +OpReturn +OpFunctionEnd + +)"; + constexpr char kDstNoDebug[] = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %40 "main" %4 %5 %6 %8 %25 +OpSource GLSL 450 +OpDecorate %4 Location 1 +OpDecorate %5 Location 2 +OpDecorate %6 Location 0 +OpDecorate %8 Location 1 +OpMemberDecorate %9 0 Offset 0 +OpDecorate %9 Block +OpDecorate %11 DescriptorSet 0 +OpDecorate %11 Binding 1 +OpMemberDecorate %16 0 Offset 0 +OpMemberDecorate %16 1 Offset 4 +OpMemberDecorate %16 2 Offset 8 +OpMemberDecorate %16 3 Offset 12 +OpMemberDecorate %17 0 Offset 0 +OpMemberDecorate %17 1 Offset 16 +OpMemberDecorate %17 2 Offset 20 +OpMemberDecorate %17 3 Offset 24 +OpMemberDecorate %17 4 Offset 28 +OpMemberDecorate %17 5 Offset 32 +OpMemberDecorate %17 6 Offset 48 +OpMemberDecorate %17 7 Offset 64 +OpMemberDecorate %17 2 RelaxedPrecision +OpMemberDecorate %17 4 RelaxedPrecision +OpDecorate %17 Block +OpDecorate %19 DescriptorSet 2 +OpDecorate %19 Binding 0 +OpMemberDecorate %23 0 BuiltIn Position +OpDecorate %23 Block +OpDecorate %28 RelaxedPrecision +OpDecorate %29 RelaxedPrecision +OpDecorate %31 RelaxedPrecision +OpDecorate %32 RelaxedPrecision +OpDecorate %33 RelaxedPrecision +OpDecorate %35 RelaxedPrecision +OpDecorate %36 RelaxedPrecision +OpDecorate %37 RelaxedPrecision +OpDecorate %44 RelaxedPrecision +OpDecorate %52 RelaxedPrecision +OpDecorate %55 RelaxedPrecision +OpDecorate %56 RelaxedPrecision +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%9 = OpTypeStruct %2 +%12 = OpTypeInt 32 0 +%13 = OpTypeInt 32 1 +%14 = OpTypeVector %13 4 +%15 = OpTypeVector %12 4 +%16 = OpTypeStruct %1 %1 %1 %1 +%17 = OpTypeStruct %2 %12 %12 %13 %13 %14 %15 %16 +%21 = OpConstant %12 8 +%22 = OpTypeArray %1 %21 +%23 = OpTypeStruct %2 +%38 = OpTypeVoid +%45 = OpConstant %12 0 +%58 = OpTypePointer Private %2 +%3 = OpTypePointer Input %2 +%59 = OpTypePointer Private %2 +%7 = OpTypePointer Output %2 +%10 = OpTypePointer Uniform %9 +%18 = OpTypePointer Uniform %17 +%24 = OpTypePointer Output %23 +%26 = OpTypePointer Function %2 +%46 = OpTypePointer Uniform %2 +%27 = OpTypeFunction %2 %26 +%39 = OpTypeFunction %38 +%4 = OpVariable %3 Input +%5 = OpVariable %3 Input +%6 = OpVariable %3 Input +%8 = OpVariable %7 Output +%11 = OpVariable %10 Uniform +%19 = OpVariable %18 Uniform +%20 = OpVariable %59 Private +%25 = OpVariable %24 Output +%60 = OpConstant %13 0 +%61 = OpConstant %1 0.5 +%28 = OpFunction %2 None %27 +%29 = OpFunctionParameter %26 +%30 = OpLabel +%31 = OpLoad %2 %29 +OpReturnValue %31 +OpFunctionEnd +%32 = OpFunction %2 None %27 +%33 = OpFunctionParameter %26 +%34 = OpLabel +%35 = OpLoad %2 %33 +%36 = OpLoad %2 %33 +%37 = OpFAdd %2 %35 %36 +OpReturnValue %37 +OpFunctionEnd +%40 = OpFunction %38 None %39 +%41 = OpLabel +%42 = OpVariable %26 Function +%50 = OpVariable %26 Function +%53 = OpVariable %26 Function +%43 = OpLoad %2 %4 +OpStore %42 %43 +%44 = OpFunctionCall %2 %28 %42 +%47 = OpAccessChain %46 %11 %45 +%48 = OpLoad %2 %47 +%49 = OpFAdd %2 %44 %48 +OpStore %8 %49 +%51 = OpLoad %2 %5 +OpStore %50 %51 +%52 = OpFunctionCall %2 %32 %50 +%54 = OpLoad %2 %6 +OpStore %53 %54 +%55 = OpFunctionCall %2 %28 %53 +%56 = OpFAdd %2 %52 %55 +%57 = OpAccessChain %7 %25 %45 +OpStore %57 %56 +%62 = OpAccessChain %7 %25 %60 +%63 = OpLoad %2 %62 +%64 = OpCompositeExtract %1 %63 0 +%65 = OpCompositeExtract %1 %63 1 +%66 = OpCompositeExtract %1 %63 2 +%67 = OpCompositeExtract %1 %63 3 +%69 = OpFNegate %1 %64 +%70 = OpFAdd %1 %66 %67 +%71 = OpFMul %1 %70 %61 +%68 = OpCompositeConstruct %2 %65 %69 %71 %67 +OpStore %62 %68 +OpReturn +OpFunctionEnd +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 58 ++; Bound: 79 + ; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 +-OpEntryPoint Vertex %40 "main" %4 %5 %6 %8 %20 %25 ++OpEntryPoint Vertex %40 "main" %5 %6 %4 %20 %25 + OpSource GLSL 450 + OpDecorate %4 Location 0 + OpDecorate %5 Location 1 + OpDecorate %6 Location 2 +-OpDecorate %8 Location 0 + OpMemberDecorate %9 0 Offset 0 + OpDecorate %9 Block +-OpDecorate %11 DescriptorSet 0 ++OpDecorate %11 DescriptorSet 2 + OpDecorate %11 Binding 0 + OpMemberDecorate %16 0 Offset 0 + OpMemberDecorate %16 1 Offset 4 + OpMemberDecorate %16 2 Offset 8 + OpMemberDecorate %16 3 Offset 12 + OpMemberDecorate %17 0 Offset 0 + OpMemberDecorate %17 1 Offset 16 + OpMemberDecorate %17 2 Offset 20 + OpMemberDecorate %17 3 Offset 24 + OpMemberDecorate %17 4 Offset 28 + OpMemberDecorate %17 5 Offset 32 + OpMemberDecorate %17 6 Offset 48 + OpMemberDecorate %17 7 Offset 64 + OpMemberDecorate %17 2 RelaxedPrecision + OpMemberDecorate %17 4 RelaxedPrecision + OpDecorate %17 Block + OpDecorate %19 DescriptorSet 0 + OpDecorate %19 Binding 1 + OpDecorate %20 Location 1 + OpMemberDecorate %23 0 BuiltIn Position +-OpMemberDecorate %23 1 BuiltIn PointSize +-OpMemberDecorate %23 2 BuiltIn ClipDistance +-OpMemberDecorate %23 3 BuiltIn CullDistance + OpDecorate %23 Block + OpDecorate %28 RelaxedPrecision +-OpDecorate %29 RelaxedPrecision ++OpDecorate %59 RelaxedPrecision + OpDecorate %31 RelaxedPrecision + OpDecorate %32 RelaxedPrecision +-OpDecorate %33 RelaxedPrecision ++OpDecorate %60 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %44 RelaxedPrecision + OpDecorate %52 RelaxedPrecision + OpDecorate %55 RelaxedPrecision + OpDecorate %56 RelaxedPrecision + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 + %9 = OpTypeStruct %2 + %12 = OpTypeInt 32 0 + %13 = OpTypeInt 32 1 + %14 = OpTypeVector %13 4 + %15 = OpTypeVector %12 4 + %16 = OpTypeStruct %1 %1 %1 %1 + %17 = OpTypeStruct %2 %12 %12 %13 %13 %14 %15 %16 + %21 = OpConstant %12 8 + %22 = OpTypeArray %1 %21 +-%23 = OpTypeStruct %2 %1 %22 %22 ++%23 = OpTypeStruct %2 + %38 = OpTypeVoid + %45 = OpConstant %12 0 ++%65 = OpTypePointer Private %2 + %3 = OpTypePointer Input %2 ++%66 = OpTypePointer Private %2 + %7 = OpTypePointer Output %2 + %10 = OpTypePointer Uniform %9 + %18 = OpTypePointer Uniform %17 + %24 = OpTypePointer Output %23 + %26 = OpTypePointer Function %2 + %46 = OpTypePointer Uniform %2 + %27 = OpTypeFunction %2 %26 + %39 = OpTypeFunction %38 + %4 = OpVariable %3 Input + %5 = OpVariable %3 Input + %6 = OpVariable %3 Input +-%8 = OpVariable %7 Output +-%11 = OpVariable %10 Uniform ++%11 = OpVariable %18 Uniform +-%19 = OpVariable %18 Uniform ++%19 = OpVariable %10 Uniform + %20 = OpVariable %7 Output ++%58 = OpVariable %66 Private + %25 = OpVariable %24 Output ++%67 = OpConstant %13 0 ++%68 = OpConstant %1 0.5 + %28 = OpFunction %2 None %27 +-%29 = OpFunctionParameter %26 ++%59 = OpFunctionParameter %26 + %30 = OpLabel +-%31 = OpLoad %2 %29 ++%31 = OpLoad %2 %59 + OpReturnValue %31 + OpFunctionEnd + %32 = OpFunction %2 None %27 +-%33 = OpFunctionParameter %26 ++%60 = OpFunctionParameter %26 + %34 = OpLabel +-%35 = OpLoad %2 %33 ++%35 = OpLoad %2 %60 +-%36 = OpLoad %2 %33 ++%36 = OpLoad %2 %60 + %37 = OpFAdd %2 %35 %36 + OpReturnValue %37 + OpFunctionEnd + %40 = OpFunction %38 None %39 + %41 = OpLabel + %42 = OpVariable %26 Function + %50 = OpVariable %26 Function + %53 = OpVariable %26 Function +-%43 = OpLoad %2 %4 ++%61 = OpLoad %2 %5 +-OpStore %42 %43 ++OpStore %42 %61 + %44 = OpFunctionCall %2 %28 %42 +-%47 = OpAccessChain %46 %11 %45 ++%62 = OpAccessChain %46 %19 %45 +-%48 = OpLoad %2 %47 ++%48 = OpLoad %2 %62 + %49 = OpFAdd %2 %44 %48 +-OpStore %8 %49 ++OpStore %20 %49 +-%51 = OpLoad %2 %5 ++%63 = OpLoad %2 %6 +-OpStore %50 %51 ++OpStore %50 %63 + %52 = OpFunctionCall %2 %32 %50 +-%54 = OpLoad %2 %6 ++%64 = OpLoad %2 %4 +-OpStore %53 %54 ++OpStore %53 %64 + %55 = OpFunctionCall %2 %28 %53 + %56 = OpFAdd %2 %52 %55 + %57 = OpAccessChain %7 %25 %45 + OpStore %57 %56 ++%69 = OpAccessChain %7 %25 %67 ++%70 = OpLoad %2 %69 ++%71 = OpCompositeExtract %1 %70 0 ++%72 = OpCompositeExtract %1 %70 1 ++%73 = OpCompositeExtract %1 %70 2 ++%74 = OpCompositeExtract %1 %70 3 ++%76 = OpFNegate %1 %71 ++%77 = OpFAdd %1 %73 %74 ++%78 = OpFMul %1 %77 %68 ++%75 = OpCompositeConstruct %2 %72 %76 %78 %74 ++OpStore %69 %75 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +TEST(DiffTest, DifferentDecorationsVertexIgnoreSetBinding) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 58 ++; Bound: 73 + ; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 +-OpEntryPoint Vertex %40 "main" %4 %5 %6 %8 %20 %25 ++OpEntryPoint Vertex %40 "main" %4 %5 %6 %8 %25 + OpSource GLSL 450 + OpName %4 "_ub" + OpName %5 "_uc" + OpName %6 "_ud" + OpName %8 "_ue" + OpName %9 "defaultUniformsVS" + OpMemberName %9 0 "_ua" + OpName %11 "" + OpName %16 "ANGLEDepthRangeParams" + OpMemberName %16 0 "near" + OpMemberName %16 1 "far" + OpMemberName %16 2 "diff" + OpMemberName %16 3 "reserved" + OpName %17 "ANGLEUniformBlock" + OpMemberName %17 0 "viewport" + OpMemberName %17 1 "clipDistancesEnabled" + OpMemberName %17 2 "xfbActiveUnpaused" + OpMemberName %17 3 "xfbVerticesPerInstance" + OpMemberName %17 4 "numSamples" + OpMemberName %17 5 "xfbBufferOffsets" + OpMemberName %17 6 "acbBufferOffsets" + OpMemberName %17 7 "depthRange" + OpName %19 "ANGLEUniforms" +-OpName %20 "ANGLEXfbPosition" + OpName %23 "gl_PerVertex" + OpMemberName %23 0 "gl_Position" +-OpMemberName %23 1 "gl_PointSize" +-OpMemberName %23 2 "gl_ClipDistance" +-OpMemberName %23 3 "gl_CullDistance" + OpName %25 "" + OpName %29 "_ua" + OpName %28 "_uf" + OpName %33 "_uf" + OpName %32 "_ug" + OpName %40 "main" + OpName %42 "param" + OpName %50 "param" + OpName %53 "param" +-OpDecorate %4 Location 0 ++OpDecorate %4 Location 1 +-OpDecorate %5 Location 1 ++OpDecorate %5 Location 2 +-OpDecorate %6 Location 2 ++OpDecorate %6 Location 0 +-OpDecorate %8 Location 0 ++OpDecorate %8 Location 1 + OpMemberDecorate %9 0 Offset 0 + OpDecorate %9 Block + OpDecorate %11 DescriptorSet 0 +-OpDecorate %11 Binding 0 ++OpDecorate %11 Binding 1 + OpMemberDecorate %16 0 Offset 0 + OpMemberDecorate %16 1 Offset 4 + OpMemberDecorate %16 2 Offset 8 + OpMemberDecorate %16 3 Offset 12 + OpMemberDecorate %17 0 Offset 0 + OpMemberDecorate %17 1 Offset 16 + OpMemberDecorate %17 2 Offset 20 + OpMemberDecorate %17 3 Offset 24 + OpMemberDecorate %17 4 Offset 28 + OpMemberDecorate %17 5 Offset 32 + OpMemberDecorate %17 6 Offset 48 + OpMemberDecorate %17 7 Offset 64 + OpMemberDecorate %17 2 RelaxedPrecision + OpMemberDecorate %17 4 RelaxedPrecision + OpDecorate %17 Block +-OpDecorate %19 DescriptorSet 0 ++OpDecorate %19 DescriptorSet 2 +-OpDecorate %19 Binding 1 ++OpDecorate %19 Binding 0 +-OpDecorate %20 Location 1 + OpMemberDecorate %23 0 BuiltIn Position +-OpMemberDecorate %23 1 BuiltIn PointSize +-OpMemberDecorate %23 2 BuiltIn ClipDistance +-OpMemberDecorate %23 3 BuiltIn CullDistance + OpDecorate %23 Block + OpDecorate %28 RelaxedPrecision + OpDecorate %29 RelaxedPrecision + OpDecorate %31 RelaxedPrecision + OpDecorate %32 RelaxedPrecision + OpDecorate %33 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %44 RelaxedPrecision + OpDecorate %52 RelaxedPrecision + OpDecorate %55 RelaxedPrecision + OpDecorate %56 RelaxedPrecision + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 + %9 = OpTypeStruct %2 + %12 = OpTypeInt 32 0 + %13 = OpTypeInt 32 1 + %14 = OpTypeVector %13 4 + %15 = OpTypeVector %12 4 + %16 = OpTypeStruct %1 %1 %1 %1 + %17 = OpTypeStruct %2 %12 %12 %13 %13 %14 %15 %16 + %21 = OpConstant %12 8 + %22 = OpTypeArray %1 %21 +-%23 = OpTypeStruct %2 %1 %22 %22 ++%23 = OpTypeStruct %2 + %38 = OpTypeVoid + %45 = OpConstant %12 0 ++%59 = OpTypePointer Private %2 + %3 = OpTypePointer Input %2 ++%60 = OpTypePointer Private %2 + %7 = OpTypePointer Output %2 + %10 = OpTypePointer Uniform %9 + %18 = OpTypePointer Uniform %17 + %24 = OpTypePointer Output %23 + %26 = OpTypePointer Function %2 + %46 = OpTypePointer Uniform %2 + %27 = OpTypeFunction %2 %26 + %39 = OpTypeFunction %38 + %4 = OpVariable %3 Input + %5 = OpVariable %3 Input + %6 = OpVariable %3 Input + %8 = OpVariable %7 Output + %11 = OpVariable %10 Uniform + %19 = OpVariable %18 Uniform +-%20 = OpVariable %7 Output ++%58 = OpVariable %60 Private + %25 = OpVariable %24 Output ++%61 = OpConstant %13 0 ++%62 = OpConstant %1 0.5 + %28 = OpFunction %2 None %27 + %29 = OpFunctionParameter %26 + %30 = OpLabel + %31 = OpLoad %2 %29 + OpReturnValue %31 + OpFunctionEnd + %32 = OpFunction %2 None %27 + %33 = OpFunctionParameter %26 + %34 = OpLabel + %35 = OpLoad %2 %33 + %36 = OpLoad %2 %33 + %37 = OpFAdd %2 %35 %36 + OpReturnValue %37 + OpFunctionEnd + %40 = OpFunction %38 None %39 + %41 = OpLabel + %42 = OpVariable %26 Function + %50 = OpVariable %26 Function + %53 = OpVariable %26 Function + %43 = OpLoad %2 %4 + OpStore %42 %43 + %44 = OpFunctionCall %2 %28 %42 + %47 = OpAccessChain %46 %11 %45 + %48 = OpLoad %2 %47 + %49 = OpFAdd %2 %44 %48 + OpStore %8 %49 + %51 = OpLoad %2 %5 + OpStore %50 %51 + %52 = OpFunctionCall %2 %32 %50 + %54 = OpLoad %2 %6 + OpStore %53 %54 + %55 = OpFunctionCall %2 %28 %53 + %56 = OpFAdd %2 %52 %55 + %57 = OpAccessChain %7 %25 %45 + OpStore %57 %56 ++%63 = OpAccessChain %7 %25 %61 ++%64 = OpLoad %2 %63 ++%65 = OpCompositeExtract %1 %64 0 ++%66 = OpCompositeExtract %1 %64 1 ++%67 = OpCompositeExtract %1 %64 2 ++%68 = OpCompositeExtract %1 %64 3 ++%70 = OpFNegate %1 %65 ++%71 = OpFAdd %1 %67 %68 ++%72 = OpFMul %1 %71 %62 ++%69 = OpCompositeConstruct %2 %66 %70 %72 %68 ++OpStore %63 %69 + OpReturn + OpFunctionEnd +)"; + Options options; + options.ignore_set_binding = true; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, DifferentDecorationsVertexIgnoreSetBindingLocation) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 58 ++; Bound: 73 + ; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 +-OpEntryPoint Vertex %40 "main" %4 %5 %6 %8 %20 %25 ++OpEntryPoint Vertex %40 "main" %4 %5 %6 %8 %25 + OpSource GLSL 450 + OpName %4 "_ub" + OpName %5 "_uc" + OpName %6 "_ud" + OpName %8 "_ue" + OpName %9 "defaultUniformsVS" + OpMemberName %9 0 "_ua" + OpName %11 "" + OpName %16 "ANGLEDepthRangeParams" + OpMemberName %16 0 "near" + OpMemberName %16 1 "far" + OpMemberName %16 2 "diff" + OpMemberName %16 3 "reserved" + OpName %17 "ANGLEUniformBlock" + OpMemberName %17 0 "viewport" + OpMemberName %17 1 "clipDistancesEnabled" + OpMemberName %17 2 "xfbActiveUnpaused" + OpMemberName %17 3 "xfbVerticesPerInstance" + OpMemberName %17 4 "numSamples" + OpMemberName %17 5 "xfbBufferOffsets" + OpMemberName %17 6 "acbBufferOffsets" + OpMemberName %17 7 "depthRange" + OpName %19 "ANGLEUniforms" +-OpName %20 "ANGLEXfbPosition" + OpName %23 "gl_PerVertex" + OpMemberName %23 0 "gl_Position" +-OpMemberName %23 1 "gl_PointSize" +-OpMemberName %23 2 "gl_ClipDistance" +-OpMemberName %23 3 "gl_CullDistance" + OpName %25 "" + OpName %29 "_ua" + OpName %28 "_uf" + OpName %33 "_uf" + OpName %32 "_ug" + OpName %40 "main" + OpName %42 "param" + OpName %50 "param" + OpName %53 "param" +-OpDecorate %4 Location 0 ++OpDecorate %4 Location 1 +-OpDecorate %5 Location 1 ++OpDecorate %5 Location 2 +-OpDecorate %6 Location 2 ++OpDecorate %6 Location 0 +-OpDecorate %8 Location 0 ++OpDecorate %8 Location 1 + OpMemberDecorate %9 0 Offset 0 + OpDecorate %9 Block + OpDecorate %11 DescriptorSet 0 +-OpDecorate %11 Binding 0 ++OpDecorate %11 Binding 1 + OpMemberDecorate %16 0 Offset 0 + OpMemberDecorate %16 1 Offset 4 + OpMemberDecorate %16 2 Offset 8 + OpMemberDecorate %16 3 Offset 12 + OpMemberDecorate %17 0 Offset 0 + OpMemberDecorate %17 1 Offset 16 + OpMemberDecorate %17 2 Offset 20 + OpMemberDecorate %17 3 Offset 24 + OpMemberDecorate %17 4 Offset 28 + OpMemberDecorate %17 5 Offset 32 + OpMemberDecorate %17 6 Offset 48 + OpMemberDecorate %17 7 Offset 64 + OpMemberDecorate %17 2 RelaxedPrecision + OpMemberDecorate %17 4 RelaxedPrecision + OpDecorate %17 Block +-OpDecorate %19 DescriptorSet 0 ++OpDecorate %19 DescriptorSet 2 +-OpDecorate %19 Binding 1 ++OpDecorate %19 Binding 0 +-OpDecorate %20 Location 1 + OpMemberDecorate %23 0 BuiltIn Position +-OpMemberDecorate %23 1 BuiltIn PointSize +-OpMemberDecorate %23 2 BuiltIn ClipDistance +-OpMemberDecorate %23 3 BuiltIn CullDistance + OpDecorate %23 Block + OpDecorate %28 RelaxedPrecision + OpDecorate %29 RelaxedPrecision + OpDecorate %31 RelaxedPrecision + OpDecorate %32 RelaxedPrecision + OpDecorate %33 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %44 RelaxedPrecision + OpDecorate %52 RelaxedPrecision + OpDecorate %55 RelaxedPrecision + OpDecorate %56 RelaxedPrecision + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 + %9 = OpTypeStruct %2 + %12 = OpTypeInt 32 0 + %13 = OpTypeInt 32 1 + %14 = OpTypeVector %13 4 + %15 = OpTypeVector %12 4 + %16 = OpTypeStruct %1 %1 %1 %1 + %17 = OpTypeStruct %2 %12 %12 %13 %13 %14 %15 %16 + %21 = OpConstant %12 8 + %22 = OpTypeArray %1 %21 +-%23 = OpTypeStruct %2 %1 %22 %22 ++%23 = OpTypeStruct %2 + %38 = OpTypeVoid + %45 = OpConstant %12 0 ++%59 = OpTypePointer Private %2 + %3 = OpTypePointer Input %2 ++%60 = OpTypePointer Private %2 + %7 = OpTypePointer Output %2 + %10 = OpTypePointer Uniform %9 + %18 = OpTypePointer Uniform %17 + %24 = OpTypePointer Output %23 + %26 = OpTypePointer Function %2 + %46 = OpTypePointer Uniform %2 + %27 = OpTypeFunction %2 %26 + %39 = OpTypeFunction %38 + %4 = OpVariable %3 Input + %5 = OpVariable %3 Input + %6 = OpVariable %3 Input + %8 = OpVariable %7 Output + %11 = OpVariable %10 Uniform + %19 = OpVariable %18 Uniform +-%20 = OpVariable %7 Output ++%58 = OpVariable %60 Private + %25 = OpVariable %24 Output ++%61 = OpConstant %13 0 ++%62 = OpConstant %1 0.5 + %28 = OpFunction %2 None %27 + %29 = OpFunctionParameter %26 + %30 = OpLabel + %31 = OpLoad %2 %29 + OpReturnValue %31 + OpFunctionEnd + %32 = OpFunction %2 None %27 + %33 = OpFunctionParameter %26 + %34 = OpLabel + %35 = OpLoad %2 %33 + %36 = OpLoad %2 %33 + %37 = OpFAdd %2 %35 %36 + OpReturnValue %37 + OpFunctionEnd + %40 = OpFunction %38 None %39 + %41 = OpLabel + %42 = OpVariable %26 Function + %50 = OpVariable %26 Function + %53 = OpVariable %26 Function + %43 = OpLoad %2 %4 + OpStore %42 %43 + %44 = OpFunctionCall %2 %28 %42 + %47 = OpAccessChain %46 %11 %45 + %48 = OpLoad %2 %47 + %49 = OpFAdd %2 %44 %48 + OpStore %8 %49 + %51 = OpLoad %2 %5 + OpStore %50 %51 + %52 = OpFunctionCall %2 %32 %50 + %54 = OpLoad %2 %6 + OpStore %53 %54 + %55 = OpFunctionCall %2 %28 %53 + %56 = OpFAdd %2 %52 %55 + %57 = OpAccessChain %7 %25 %45 + OpStore %57 %56 ++%63 = OpAccessChain %7 %25 %61 ++%64 = OpLoad %2 %63 ++%65 = OpCompositeExtract %1 %64 0 ++%66 = OpCompositeExtract %1 %64 1 ++%67 = OpCompositeExtract %1 %64 2 ++%68 = OpCompositeExtract %1 %64 3 ++%70 = OpFNegate %1 %65 ++%71 = OpFAdd %1 %67 %68 ++%72 = OpFMul %1 %71 %62 ++%69 = OpCompositeConstruct %2 %66 %70 %72 %68 ++OpStore %63 %69 + OpReturn + OpFunctionEnd +)"; + Options options; + options.ignore_set_binding = true; + options.ignore_location = true; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/different_decorations_vertex_dst.spvasm b/test/diff/diff_files/different_decorations_vertex_dst.spvasm new file mode 100644 index 00000000..33c6a9c1 --- /dev/null +++ b/test/diff/diff_files/different_decorations_vertex_dst.spvasm @@ -0,0 +1,159 @@ +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %40 "main" %4 %5 %6 %8 %25 +OpSource GLSL 450 +OpName %4 "_ub" +OpName %5 "_uc" +OpName %6 "_ud" +OpName %8 "_ue" +OpName %9 "defaultUniformsVS" +OpMemberName %9 0 "_ua" +OpName %11 "" +OpName %16 "ANGLEDepthRangeParams" +OpMemberName %16 0 "near" +OpMemberName %16 1 "far" +OpMemberName %16 2 "diff" +OpMemberName %16 3 "reserved" +OpName %17 "ANGLEUniformBlock" +OpMemberName %17 0 "viewport" +OpMemberName %17 1 "clipDistancesEnabled" +OpMemberName %17 2 "xfbActiveUnpaused" +OpMemberName %17 3 "xfbVerticesPerInstance" +OpMemberName %17 4 "numSamples" +OpMemberName %17 5 "xfbBufferOffsets" +OpMemberName %17 6 "acbBufferOffsets" +OpMemberName %17 7 "depthRange" +OpName %19 "ANGLEUniforms" +OpName %23 "gl_PerVertex" +OpMemberName %23 0 "gl_Position" +OpName %25 "" +OpName %29 "_ua" +OpName %28 "_uf" +OpName %33 "_uf" +OpName %32 "_ug" +OpName %40 "main" +OpName %42 "param" +OpName %50 "param" +OpName %53 "param" +OpDecorate %4 Location 1 +OpDecorate %5 Location 2 +OpDecorate %6 Location 0 +OpDecorate %8 Location 1 +OpMemberDecorate %9 0 Offset 0 +OpDecorate %9 Block +OpDecorate %11 DescriptorSet 0 +OpDecorate %11 Binding 1 +OpMemberDecorate %16 0 Offset 0 +OpMemberDecorate %16 1 Offset 4 +OpMemberDecorate %16 2 Offset 8 +OpMemberDecorate %16 3 Offset 12 +OpMemberDecorate %17 0 Offset 0 +OpMemberDecorate %17 1 Offset 16 +OpMemberDecorate %17 2 Offset 20 +OpMemberDecorate %17 3 Offset 24 +OpMemberDecorate %17 4 Offset 28 +OpMemberDecorate %17 5 Offset 32 +OpMemberDecorate %17 6 Offset 48 +OpMemberDecorate %17 7 Offset 64 +OpMemberDecorate %17 2 RelaxedPrecision +OpMemberDecorate %17 4 RelaxedPrecision +OpDecorate %17 Block +OpDecorate %19 DescriptorSet 2 +OpDecorate %19 Binding 0 +OpMemberDecorate %23 0 BuiltIn Position +OpDecorate %23 Block +OpDecorate %28 RelaxedPrecision +OpDecorate %29 RelaxedPrecision +OpDecorate %31 RelaxedPrecision +OpDecorate %32 RelaxedPrecision +OpDecorate %33 RelaxedPrecision +OpDecorate %35 RelaxedPrecision +OpDecorate %36 RelaxedPrecision +OpDecorate %37 RelaxedPrecision +OpDecorate %44 RelaxedPrecision +OpDecorate %52 RelaxedPrecision +OpDecorate %55 RelaxedPrecision +OpDecorate %56 RelaxedPrecision +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%9 = OpTypeStruct %2 +%12 = OpTypeInt 32 0 +%13 = OpTypeInt 32 1 +%14 = OpTypeVector %13 4 +%15 = OpTypeVector %12 4 +%16 = OpTypeStruct %1 %1 %1 %1 +%17 = OpTypeStruct %2 %12 %12 %13 %13 %14 %15 %16 +%21 = OpConstant %12 8 +%22 = OpTypeArray %1 %21 +%23 = OpTypeStruct %2 +%38 = OpTypeVoid +%45 = OpConstant %12 0 +%58 = OpTypePointer Private %2 +%3 = OpTypePointer Input %2 +%59 = OpTypePointer Private %2 +%7 = OpTypePointer Output %2 +%10 = OpTypePointer Uniform %9 +%18 = OpTypePointer Uniform %17 +%24 = OpTypePointer Output %23 +%26 = OpTypePointer Function %2 +%46 = OpTypePointer Uniform %2 +%27 = OpTypeFunction %2 %26 +%39 = OpTypeFunction %38 +%4 = OpVariable %3 Input +%5 = OpVariable %3 Input +%6 = OpVariable %3 Input +%8 = OpVariable %7 Output +%11 = OpVariable %10 Uniform +%19 = OpVariable %18 Uniform +%20 = OpVariable %59 Private +%25 = OpVariable %24 Output +%60 = OpConstant %13 0 +%61 = OpConstant %1 0.5 +%28 = OpFunction %2 None %27 +%29 = OpFunctionParameter %26 +%30 = OpLabel +%31 = OpLoad %2 %29 +OpReturnValue %31 +OpFunctionEnd +%32 = OpFunction %2 None %27 +%33 = OpFunctionParameter %26 +%34 = OpLabel +%35 = OpLoad %2 %33 +%36 = OpLoad %2 %33 +%37 = OpFAdd %2 %35 %36 +OpReturnValue %37 +OpFunctionEnd +%40 = OpFunction %38 None %39 +%41 = OpLabel +%42 = OpVariable %26 Function +%50 = OpVariable %26 Function +%53 = OpVariable %26 Function +%43 = OpLoad %2 %4 +OpStore %42 %43 +%44 = OpFunctionCall %2 %28 %42 +%47 = OpAccessChain %46 %11 %45 +%48 = OpLoad %2 %47 +%49 = OpFAdd %2 %44 %48 +OpStore %8 %49 +%51 = OpLoad %2 %5 +OpStore %50 %51 +%52 = OpFunctionCall %2 %32 %50 +%54 = OpLoad %2 %6 +OpStore %53 %54 +%55 = OpFunctionCall %2 %28 %53 +%56 = OpFAdd %2 %52 %55 +%57 = OpAccessChain %7 %25 %45 +OpStore %57 %56 +%62 = OpAccessChain %7 %25 %60 +%63 = OpLoad %2 %62 +%64 = OpCompositeExtract %1 %63 0 +%65 = OpCompositeExtract %1 %63 1 +%66 = OpCompositeExtract %1 %63 2 +%67 = OpCompositeExtract %1 %63 3 +%69 = OpFNegate %1 %64 +%70 = OpFAdd %1 %66 %67 +%71 = OpFMul %1 %70 %61 +%68 = OpCompositeConstruct %2 %65 %69 %71 %67 +OpStore %62 %68 +OpReturn +OpFunctionEnd diff --git a/test/diff/diff_files/different_decorations_vertex_src.spvasm b/test/diff/diff_files/different_decorations_vertex_src.spvasm new file mode 100644 index 00000000..ce1680cb --- /dev/null +++ b/test/diff/diff_files/different_decorations_vertex_src.spvasm @@ -0,0 +1,155 @@ +;; Test where variable set/binding/location decorations are different between +;; src and dst vertex shaders. +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %40 "main" %4 %5 %6 %8 %20 %25 +OpSource GLSL 450 +OpName %4 "_ub" +OpName %5 "_uc" +OpName %6 "_ud" +OpName %8 "_ue" +OpName %9 "defaultUniformsVS" +OpMemberName %9 0 "_ua" +OpName %11 "" +OpName %16 "ANGLEDepthRangeParams" +OpMemberName %16 0 "near" +OpMemberName %16 1 "far" +OpMemberName %16 2 "diff" +OpMemberName %16 3 "reserved" +OpName %17 "ANGLEUniformBlock" +OpMemberName %17 0 "viewport" +OpMemberName %17 1 "clipDistancesEnabled" +OpMemberName %17 2 "xfbActiveUnpaused" +OpMemberName %17 3 "xfbVerticesPerInstance" +OpMemberName %17 4 "numSamples" +OpMemberName %17 5 "xfbBufferOffsets" +OpMemberName %17 6 "acbBufferOffsets" +OpMemberName %17 7 "depthRange" +OpName %19 "ANGLEUniforms" +OpName %20 "ANGLEXfbPosition" +OpName %23 "gl_PerVertex" +OpMemberName %23 0 "gl_Position" +OpMemberName %23 1 "gl_PointSize" +OpMemberName %23 2 "gl_ClipDistance" +OpMemberName %23 3 "gl_CullDistance" +OpName %25 "" +OpName %29 "_ua" +OpName %28 "_uf" +OpName %33 "_uf" +OpName %32 "_ug" +OpName %40 "main" +OpName %42 "param" +OpName %50 "param" +OpName %53 "param" +OpDecorate %4 Location 0 +OpDecorate %5 Location 1 +OpDecorate %6 Location 2 +OpDecorate %8 Location 0 +OpMemberDecorate %9 0 Offset 0 +OpDecorate %9 Block +OpDecorate %11 DescriptorSet 0 +OpDecorate %11 Binding 0 +OpMemberDecorate %16 0 Offset 0 +OpMemberDecorate %16 1 Offset 4 +OpMemberDecorate %16 2 Offset 8 +OpMemberDecorate %16 3 Offset 12 +OpMemberDecorate %17 0 Offset 0 +OpMemberDecorate %17 1 Offset 16 +OpMemberDecorate %17 2 Offset 20 +OpMemberDecorate %17 3 Offset 24 +OpMemberDecorate %17 4 Offset 28 +OpMemberDecorate %17 5 Offset 32 +OpMemberDecorate %17 6 Offset 48 +OpMemberDecorate %17 7 Offset 64 +OpMemberDecorate %17 2 RelaxedPrecision +OpMemberDecorate %17 4 RelaxedPrecision +OpDecorate %17 Block +OpDecorate %19 DescriptorSet 0 +OpDecorate %19 Binding 1 +OpDecorate %20 Location 1 +OpMemberDecorate %23 0 BuiltIn Position +OpMemberDecorate %23 1 BuiltIn PointSize +OpMemberDecorate %23 2 BuiltIn ClipDistance +OpMemberDecorate %23 3 BuiltIn CullDistance +OpDecorate %23 Block +OpDecorate %28 RelaxedPrecision +OpDecorate %29 RelaxedPrecision +OpDecorate %31 RelaxedPrecision +OpDecorate %32 RelaxedPrecision +OpDecorate %33 RelaxedPrecision +OpDecorate %35 RelaxedPrecision +OpDecorate %36 RelaxedPrecision +OpDecorate %37 RelaxedPrecision +OpDecorate %44 RelaxedPrecision +OpDecorate %52 RelaxedPrecision +OpDecorate %55 RelaxedPrecision +OpDecorate %56 RelaxedPrecision +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%9 = OpTypeStruct %2 +%12 = OpTypeInt 32 0 +%13 = OpTypeInt 32 1 +%14 = OpTypeVector %13 4 +%15 = OpTypeVector %12 4 +%16 = OpTypeStruct %1 %1 %1 %1 +%17 = OpTypeStruct %2 %12 %12 %13 %13 %14 %15 %16 +%21 = OpConstant %12 8 +%22 = OpTypeArray %1 %21 +%23 = OpTypeStruct %2 %1 %22 %22 +%38 = OpTypeVoid +%45 = OpConstant %12 0 +%3 = OpTypePointer Input %2 +%7 = OpTypePointer Output %2 +%10 = OpTypePointer Uniform %9 +%18 = OpTypePointer Uniform %17 +%24 = OpTypePointer Output %23 +%26 = OpTypePointer Function %2 +%46 = OpTypePointer Uniform %2 +%27 = OpTypeFunction %2 %26 +%39 = OpTypeFunction %38 +%4 = OpVariable %3 Input +%5 = OpVariable %3 Input +%6 = OpVariable %3 Input +%8 = OpVariable %7 Output +%11 = OpVariable %10 Uniform +%19 = OpVariable %18 Uniform +%20 = OpVariable %7 Output +%25 = OpVariable %24 Output +%28 = OpFunction %2 None %27 +%29 = OpFunctionParameter %26 +%30 = OpLabel +%31 = OpLoad %2 %29 +OpReturnValue %31 +OpFunctionEnd +%32 = OpFunction %2 None %27 +%33 = OpFunctionParameter %26 +%34 = OpLabel +%35 = OpLoad %2 %33 +%36 = OpLoad %2 %33 +%37 = OpFAdd %2 %35 %36 +OpReturnValue %37 +OpFunctionEnd +%40 = OpFunction %38 None %39 +%41 = OpLabel +%42 = OpVariable %26 Function +%50 = OpVariable %26 Function +%53 = OpVariable %26 Function +%43 = OpLoad %2 %4 +OpStore %42 %43 +%44 = OpFunctionCall %2 %28 %42 +%47 = OpAccessChain %46 %11 %45 +%48 = OpLoad %2 %47 +%49 = OpFAdd %2 %44 %48 +OpStore %8 %49 +%51 = OpLoad %2 %5 +OpStore %50 %51 +%52 = OpFunctionCall %2 %32 %50 +%54 = OpLoad %2 %6 +OpStore %53 %54 +%55 = OpFunctionCall %2 %28 %53 +%56 = OpFAdd %2 %52 %55 +%57 = OpAccessChain %7 %25 %45 +OpStore %57 %56 +OpReturn +OpFunctionEnd + diff --git a/test/diff/diff_files/different_function_parameter_count_autogen.cpp b/test/diff/diff_files/different_function_parameter_count_autogen.cpp new file mode 100644 index 00000000..3a077fb0 --- /dev/null +++ b/test/diff/diff_files/different_function_parameter_count_autogen.cpp @@ -0,0 +1,339 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Test where src and dst have a function with different parameter count. +constexpr char kSrc[] = R"(; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 25 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %20 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpName %4 "main" + OpName %11 "f(vf2;" + OpName %10 "v" + OpName %20 "o" + OpName %23 "param" + OpDecorate %20 RelaxedPrecision + OpDecorate %20 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 2 + %8 = OpTypePointer Function %7 + %9 = OpTypeFunction %7 %8 + %14 = OpConstant %6 0.5 + %15 = OpConstantComposite %7 %14 %14 + %19 = OpTypePointer Output %7 + %20 = OpVariable %19 Output + %21 = OpConstant %6 0 + %22 = OpConstantComposite %7 %21 %21 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %23 = OpVariable %8 Function + OpStore %23 %22 + %24 = OpFunctionCall %7 %11 %23 + OpStore %20 %24 + OpReturn + OpFunctionEnd + %11 = OpFunction %7 None %9 + %10 = OpFunctionParameter %8 + %12 = OpLabel + %13 = OpLoad %7 %10 + %16 = OpFAdd %7 %13 %15 + OpReturnValue %16 + OpFunctionEnd)"; +constexpr char kDst[] = R"(; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 28 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %20 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpName %4 "main" + OpName %12 "f(vf2;vf2;" + OpName %10 "v" + OpName %11 "v2" + OpName %20 "o" + OpName %25 "param" + OpName %26 "param" + OpDecorate %20 RelaxedPrecision + OpDecorate %20 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 2 + %8 = OpTypePointer Function %7 + %9 = OpTypeFunction %7 %8 %8 + %19 = OpTypePointer Output %7 + %20 = OpVariable %19 Output + %21 = OpConstant %6 0 + %22 = OpConstantComposite %7 %21 %21 + %23 = OpConstant %6 0.5 + %24 = OpConstantComposite %7 %23 %23 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %25 = OpVariable %8 Function + %26 = OpVariable %8 Function + OpStore %25 %22 + OpStore %26 %24 + %27 = OpFunctionCall %7 %12 %25 %26 + OpStore %20 %27 + OpReturn + OpFunctionEnd + %12 = OpFunction %7 None %9 + %10 = OpFunctionParameter %8 + %11 = OpFunctionParameter %8 + %13 = OpLabel + %14 = OpLoad %7 %10 + %15 = OpLoad %7 %11 + %16 = OpFAdd %7 %14 %15 + OpReturnValue %16 + OpFunctionEnd + +)"; + +TEST(DiffTest, DifferentFunctionParameterCount) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 25 ++; Bound: 33 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %20 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpName %4 "main" +-OpName %11 "f(vf2;" ++OpName %11 "f(vf2;vf2;" + OpName %10 "v" ++OpName %26 "v2" + OpName %20 "o" + OpName %23 "param" ++OpName %31 "param" + OpDecorate %20 RelaxedPrecision + OpDecorate %20 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 2 + %8 = OpTypePointer Function %7 +-%9 = OpTypeFunction %7 %8 ++%25 = OpTypeFunction %7 %8 %8 + %14 = OpConstant %6 0.5 + %15 = OpConstantComposite %7 %14 %14 + %19 = OpTypePointer Output %7 + %20 = OpVariable %19 Output + %21 = OpConstant %6 0 + %22 = OpConstantComposite %7 %21 %21 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %23 = OpVariable %8 Function ++%31 = OpVariable %8 Function + OpStore %23 %22 +-%24 = OpFunctionCall %7 %11 %23 ++OpStore %31 %15 ++%32 = OpFunctionCall %7 %11 %23 %31 +-OpStore %20 %24 ++OpStore %20 %32 + OpReturn + OpFunctionEnd +-%11 = OpFunction %7 None %9 ++%11 = OpFunction %7 None %25 + %10 = OpFunctionParameter %8 ++%26 = OpFunctionParameter %8 + %12 = OpLabel + %13 = OpLoad %7 %10 +-%16 = OpFAdd %7 %13 %15 ++%27 = OpLoad %7 %26 ++%28 = OpFAdd %7 %13 %27 +-OpReturnValue %16 ++OpReturnValue %28 + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, DifferentFunctionParameterCountNoDebug) { + constexpr char kSrcNoDebug[] = R"(; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 25 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %20 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpDecorate %20 RelaxedPrecision + OpDecorate %20 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 2 + %8 = OpTypePointer Function %7 + %9 = OpTypeFunction %7 %8 + %14 = OpConstant %6 0.5 + %15 = OpConstantComposite %7 %14 %14 + %19 = OpTypePointer Output %7 + %20 = OpVariable %19 Output + %21 = OpConstant %6 0 + %22 = OpConstantComposite %7 %21 %21 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %23 = OpVariable %8 Function + OpStore %23 %22 + %24 = OpFunctionCall %7 %11 %23 + OpStore %20 %24 + OpReturn + OpFunctionEnd + %11 = OpFunction %7 None %9 + %10 = OpFunctionParameter %8 + %12 = OpLabel + %13 = OpLoad %7 %10 + %16 = OpFAdd %7 %13 %15 + OpReturnValue %16 + OpFunctionEnd +)"; + constexpr char kDstNoDebug[] = R"(; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 28 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %20 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpDecorate %20 RelaxedPrecision + OpDecorate %20 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 2 + %8 = OpTypePointer Function %7 + %9 = OpTypeFunction %7 %8 %8 + %19 = OpTypePointer Output %7 + %20 = OpVariable %19 Output + %21 = OpConstant %6 0 + %22 = OpConstantComposite %7 %21 %21 + %23 = OpConstant %6 0.5 + %24 = OpConstantComposite %7 %23 %23 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %25 = OpVariable %8 Function + %26 = OpVariable %8 Function + OpStore %25 %22 + OpStore %26 %24 + %27 = OpFunctionCall %7 %12 %25 %26 + OpStore %20 %27 + OpReturn + OpFunctionEnd + %12 = OpFunction %7 None %9 + %10 = OpFunctionParameter %8 + %11 = OpFunctionParameter %8 + %13 = OpLabel + %14 = OpLoad %7 %10 + %15 = OpLoad %7 %11 + %16 = OpFAdd %7 %14 %15 + OpReturnValue %16 + OpFunctionEnd + +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 25 ++; Bound: 34 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %20 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpDecorate %20 RelaxedPrecision + OpDecorate %20 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 2 + %8 = OpTypePointer Function %7 +-%9 = OpTypeFunction %7 %8 ++%25 = OpTypeFunction %7 %8 %8 + %14 = OpConstant %6 0.5 + %15 = OpConstantComposite %7 %14 %14 + %19 = OpTypePointer Output %7 + %20 = OpVariable %19 Output + %21 = OpConstant %6 0 + %22 = OpConstantComposite %7 %21 %21 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %23 = OpVariable %8 Function ++%32 = OpVariable %8 Function + OpStore %23 %22 +-%24 = OpFunctionCall %7 %11 %23 ++OpStore %32 %15 ++%33 = OpFunctionCall %7 %11 %23 %32 +-OpStore %20 %24 ++OpStore %20 %33 + OpReturn + OpFunctionEnd +-%11 = OpFunction %7 None %9 ++%11 = OpFunction %7 None %25 +-%10 = OpFunctionParameter %8 ++%26 = OpFunctionParameter %8 ++%27 = OpFunctionParameter %8 + %12 = OpLabel +-%13 = OpLoad %7 %10 ++%13 = OpLoad %7 %26 +-%16 = OpFAdd %7 %13 %15 ++%28 = OpLoad %7 %27 ++%29 = OpFAdd %7 %13 %28 +-OpReturnValue %16 ++OpReturnValue %29 + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/different_function_parameter_count_dst.spvasm b/test/diff/diff_files/different_function_parameter_count_dst.spvasm new file mode 100644 index 00000000..92442601 --- /dev/null +++ b/test/diff/diff_files/different_function_parameter_count_dst.spvasm @@ -0,0 +1,52 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 28 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %20 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpName %4 "main" + OpName %12 "f(vf2;vf2;" + OpName %10 "v" + OpName %11 "v2" + OpName %20 "o" + OpName %25 "param" + OpName %26 "param" + OpDecorate %20 RelaxedPrecision + OpDecorate %20 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 2 + %8 = OpTypePointer Function %7 + %9 = OpTypeFunction %7 %8 %8 + %19 = OpTypePointer Output %7 + %20 = OpVariable %19 Output + %21 = OpConstant %6 0 + %22 = OpConstantComposite %7 %21 %21 + %23 = OpConstant %6 0.5 + %24 = OpConstantComposite %7 %23 %23 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %25 = OpVariable %8 Function + %26 = OpVariable %8 Function + OpStore %25 %22 + OpStore %26 %24 + %27 = OpFunctionCall %7 %12 %25 %26 + OpStore %20 %27 + OpReturn + OpFunctionEnd + %12 = OpFunction %7 None %9 + %10 = OpFunctionParameter %8 + %11 = OpFunctionParameter %8 + %13 = OpLabel + %14 = OpLoad %7 %10 + %15 = OpLoad %7 %11 + %16 = OpFAdd %7 %14 %15 + OpReturnValue %16 + OpFunctionEnd + diff --git a/test/diff/diff_files/different_function_parameter_count_src.spvasm b/test/diff/diff_files/different_function_parameter_count_src.spvasm new file mode 100644 index 00000000..6ee2408a --- /dev/null +++ b/test/diff/diff_files/different_function_parameter_count_src.spvasm @@ -0,0 +1,46 @@ +;; Test where src and dst have a function with different parameter count. +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 25 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %20 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpName %4 "main" + OpName %11 "f(vf2;" + OpName %10 "v" + OpName %20 "o" + OpName %23 "param" + OpDecorate %20 RelaxedPrecision + OpDecorate %20 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 2 + %8 = OpTypePointer Function %7 + %9 = OpTypeFunction %7 %8 + %14 = OpConstant %6 0.5 + %15 = OpConstantComposite %7 %14 %14 + %19 = OpTypePointer Output %7 + %20 = OpVariable %19 Output + %21 = OpConstant %6 0 + %22 = OpConstantComposite %7 %21 %21 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %23 = OpVariable %8 Function + OpStore %23 %22 + %24 = OpFunctionCall %7 %11 %23 + OpStore %20 %24 + OpReturn + OpFunctionEnd + %11 = OpFunction %7 None %9 + %10 = OpFunctionParameter %8 + %12 = OpLabel + %13 = OpLoad %7 %10 + %16 = OpFAdd %7 %13 %15 + OpReturnValue %16 + OpFunctionEnd diff --git a/test/diff/diff_files/extra_if_block_autogen.cpp b/test/diff/diff_files/extra_if_block_autogen.cpp new file mode 100644 index 00000000..4f913198 --- /dev/null +++ b/test/diff/diff_files/extra_if_block_autogen.cpp @@ -0,0 +1,867 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Test where src has an extra if block in one function, and dst has an extra +// if block in another function. +constexpr char kSrc[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %63 %68 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "f1(" + OpName %10 "f2(" + OpName %13 "v" + OpName %16 "Buffer" + OpMemberName %16 0 "flag1" + OpMemberName %16 1 "flag2" + OpName %18 "" + OpName %45 "v" + OpName %63 "color" + OpName %68 "v" + OpDecorate %8 RelaxedPrecision + OpDecorate %10 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + OpMemberDecorate %16 0 RelaxedPrecision + OpMemberDecorate %16 0 Offset 0 + OpMemberDecorate %16 1 RelaxedPrecision + OpMemberDecorate %16 1 Offset 4 + OpDecorate %16 Block + OpDecorate %18 DescriptorSet 0 + OpDecorate %18 Binding 0 + OpDecorate %23 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %31 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %38 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %45 RelaxedPrecision + OpDecorate %47 RelaxedPrecision + OpDecorate %48 RelaxedPrecision + OpDecorate %50 RelaxedPrecision + OpDecorate %51 RelaxedPrecision + OpDecorate %54 RelaxedPrecision + OpDecorate %55 RelaxedPrecision + OpDecorate %56 RelaxedPrecision + OpDecorate %57 RelaxedPrecision + OpDecorate %58 RelaxedPrecision + OpDecorate %63 RelaxedPrecision + OpDecorate %63 Location 0 + OpDecorate %64 RelaxedPrecision + OpDecorate %65 RelaxedPrecision + OpDecorate %66 RelaxedPrecision + OpDecorate %68 RelaxedPrecision + OpDecorate %68 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeFunction %6 + %12 = OpTypePointer Function %6 + %14 = OpConstant %6 0 + %15 = OpTypeInt 32 0 + %16 = OpTypeStruct %15 %15 + %17 = OpTypePointer Uniform %16 + %18 = OpVariable %17 Uniform + %19 = OpTypeInt 32 1 + %20 = OpConstant %19 0 + %21 = OpTypePointer Uniform %15 + %24 = OpConstant %15 0 + %25 = OpTypeBool + %29 = OpConstant %6 1 + %32 = OpConstant %19 1 + %49 = OpConstant %6 10 + %52 = OpConstant %6 0.5 + %53 = OpConstant %6 0.699999988 + %61 = OpTypeVector %6 4 + %62 = OpTypePointer Output %61 + %63 = OpVariable %62 Output + %67 = OpTypePointer Input %6 + %68 = OpVariable %67 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %64 = OpFunctionCall %6 %8 + %65 = OpFunctionCall %6 %10 + %66 = OpCompositeConstruct %61 %64 %65 %14 %29 + OpStore %63 %66 + OpReturn + OpFunctionEnd + %8 = OpFunction %6 None %7 + %9 = OpLabel + %13 = OpVariable %12 Function + OpStore %13 %14 + %22 = OpAccessChain %21 %18 %20 + %23 = OpLoad %15 %22 + %26 = OpINotEqual %25 %23 %24 + OpSelectionMerge %28 None + OpBranchConditional %26 %27 %28 + %27 = OpLabel + %30 = OpLoad %6 %13 + %31 = OpFAdd %6 %30 %29 + OpStore %13 %31 + OpBranch %28 + %28 = OpLabel + %33 = OpAccessChain %21 %18 %32 + %34 = OpLoad %15 %33 + %35 = OpConvertUToF %6 %34 + %36 = OpExtInst %6 %1 Log2 %35 + %37 = OpLoad %6 %13 + %38 = OpFAdd %6 %37 %36 + OpStore %13 %38 + %39 = OpLoad %6 %13 + %40 = OpLoad %6 %13 + %41 = OpExtInst %6 %1 Sqrt %40 + %42 = OpFSub %6 %39 %41 + OpReturnValue %42 + OpFunctionEnd + %10 = OpFunction %6 None %7 + %11 = OpLabel + %45 = OpVariable %12 Function + %46 = OpAccessChain %21 %18 %20 + %47 = OpLoad %15 %46 + %48 = OpConvertUToF %6 %47 + %50 = OpFDiv %6 %48 %49 + OpStore %45 %50 + %51 = OpLoad %6 %45 + %54 = OpExtInst %6 %1 FClamp %51 %52 %53 + %55 = OpLoad %6 %45 + %56 = OpFMul %6 %55 %54 + OpStore %45 %56 + %57 = OpLoad %6 %45 + %58 = OpExtInst %6 %1 Exp %57 + OpReturnValue %58 + OpFunctionEnd +)"; +constexpr char kDst[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %63 %69 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "f1(" + OpName %10 "f2(" + OpName %13 "v" + OpName %16 "Buffer" + OpMemberName %16 0 "flag1" + OpMemberName %16 1 "flag2" + OpName %18 "" + OpName %34 "v" + OpName %63 "color" + OpName %69 "v" + OpDecorate %8 RelaxedPrecision + OpDecorate %10 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + OpMemberDecorate %16 0 RelaxedPrecision + OpMemberDecorate %16 0 Offset 0 + OpMemberDecorate %16 1 RelaxedPrecision + OpMemberDecorate %16 1 Offset 4 + OpDecorate %16 Block + OpDecorate %18 DescriptorSet 0 + OpDecorate %18 Binding 0 + OpDecorate %23 RelaxedPrecision + OpDecorate %24 RelaxedPrecision + OpDecorate %25 RelaxedPrecision + OpDecorate %26 RelaxedPrecision + OpDecorate %27 RelaxedPrecision + OpDecorate %28 RelaxedPrecision + OpDecorate %29 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %31 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %38 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %44 RelaxedPrecision + OpDecorate %45 RelaxedPrecision + OpDecorate %46 RelaxedPrecision + OpDecorate %48 RelaxedPrecision + OpDecorate %55 RelaxedPrecision + OpDecorate %56 RelaxedPrecision + OpDecorate %57 RelaxedPrecision + OpDecorate %58 RelaxedPrecision + OpDecorate %63 RelaxedPrecision + OpDecorate %63 Location 0 + OpDecorate %64 RelaxedPrecision + OpDecorate %65 RelaxedPrecision + OpDecorate %67 RelaxedPrecision + OpDecorate %69 RelaxedPrecision + OpDecorate %69 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeFunction %6 + %12 = OpTypePointer Function %6 + %14 = OpConstant %6 0 + %15 = OpTypeInt 32 0 + %16 = OpTypeStruct %15 %15 + %17 = OpTypePointer Uniform %16 + %18 = OpVariable %17 Uniform + %19 = OpTypeInt 32 1 + %20 = OpConstant %19 1 + %21 = OpTypePointer Uniform %15 + %35 = OpConstant %19 0 + %39 = OpConstant %6 10 + %42 = OpConstant %6 0.5 + %43 = OpConstant %6 0.699999988 + %49 = OpConstant %15 0 + %50 = OpTypeBool + %54 = OpConstant %6 0.100000001 + %61 = OpTypeVector %6 4 + %62 = OpTypePointer Output %61 + %63 = OpVariable %62 Output + %66 = OpConstant %6 1 + %68 = OpTypePointer Input %6 + %69 = OpVariable %68 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %64 = OpFunctionCall %6 %8 + %65 = OpFunctionCall %6 %10 + %67 = OpCompositeConstruct %61 %64 %65 %14 %66 + OpStore %63 %67 + OpReturn + OpFunctionEnd + %8 = OpFunction %6 None %7 + %9 = OpLabel + %13 = OpVariable %12 Function + OpStore %13 %14 + %22 = OpAccessChain %21 %18 %20 + %23 = OpLoad %15 %22 + %24 = OpConvertUToF %6 %23 + %25 = OpExtInst %6 %1 Log2 %24 + %26 = OpLoad %6 %13 + %27 = OpFAdd %6 %26 %25 + OpStore %13 %27 + %28 = OpLoad %6 %13 + %29 = OpLoad %6 %13 + %30 = OpExtInst %6 %1 Sqrt %29 + %31 = OpFSub %6 %28 %30 + OpReturnValue %31 + OpFunctionEnd + %10 = OpFunction %6 None %7 + %11 = OpLabel + %34 = OpVariable %12 Function + %36 = OpAccessChain %21 %18 %35 + %37 = OpLoad %15 %36 + %38 = OpConvertUToF %6 %37 + %40 = OpFDiv %6 %38 %39 + OpStore %34 %40 + %41 = OpLoad %6 %34 + %44 = OpExtInst %6 %1 FClamp %41 %42 %43 + %45 = OpLoad %6 %34 + %46 = OpFMul %6 %45 %44 + OpStore %34 %46 + %47 = OpAccessChain %21 %18 %20 + %48 = OpLoad %15 %47 + %51 = OpINotEqual %50 %48 %49 + OpSelectionMerge %53 None + OpBranchConditional %51 %52 %53 + %52 = OpLabel + %55 = OpLoad %6 %34 + %56 = OpFSub %6 %55 %54 + OpStore %34 %56 + OpBranch %53 + %53 = OpLabel + %57 = OpLoad %6 %34 + %58 = OpExtInst %6 %1 Exp %57 + OpReturnValue %58 + OpFunctionEnd + +)"; + +TEST(DiffTest, ExtraIfBlock) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 69 ++; Bound: 81 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %63 %68 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "f1(" + OpName %10 "f2(" + OpName %13 "v" + OpName %16 "Buffer" + OpMemberName %16 0 "flag1" + OpMemberName %16 1 "flag2" + OpName %18 "" + OpName %45 "v" + OpName %63 "color" + OpName %68 "v" + OpDecorate %8 RelaxedPrecision + OpDecorate %10 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + OpMemberDecorate %16 0 RelaxedPrecision + OpMemberDecorate %16 0 Offset 0 + OpMemberDecorate %16 1 RelaxedPrecision + OpMemberDecorate %16 1 Offset 4 + OpDecorate %16 Block + OpDecorate %18 DescriptorSet 0 + OpDecorate %18 Binding 0 +-OpDecorate %23 RelaxedPrecision +-OpDecorate %30 RelaxedPrecision +-OpDecorate %31 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %38 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %45 RelaxedPrecision + OpDecorate %47 RelaxedPrecision + OpDecorate %48 RelaxedPrecision + OpDecorate %50 RelaxedPrecision + OpDecorate %51 RelaxedPrecision + OpDecorate %54 RelaxedPrecision + OpDecorate %55 RelaxedPrecision + OpDecorate %56 RelaxedPrecision ++OpDecorate %72 RelaxedPrecision + OpDecorate %57 RelaxedPrecision ++OpDecorate %77 RelaxedPrecision ++OpDecorate %78 RelaxedPrecision + OpDecorate %58 RelaxedPrecision + OpDecorate %63 RelaxedPrecision + OpDecorate %63 Location 0 + OpDecorate %64 RelaxedPrecision + OpDecorate %65 RelaxedPrecision + OpDecorate %66 RelaxedPrecision + OpDecorate %68 RelaxedPrecision + OpDecorate %68 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeFunction %6 + %12 = OpTypePointer Function %6 + %14 = OpConstant %6 0 + %15 = OpTypeInt 32 0 + %16 = OpTypeStruct %15 %15 + %17 = OpTypePointer Uniform %16 + %18 = OpVariable %17 Uniform + %19 = OpTypeInt 32 1 + %20 = OpConstant %19 0 + %21 = OpTypePointer Uniform %15 + %24 = OpConstant %15 0 + %25 = OpTypeBool + %29 = OpConstant %6 1 + %32 = OpConstant %19 1 + %49 = OpConstant %6 10 + %52 = OpConstant %6 0.5 ++%76 = OpConstant %6 0.100000001 + %53 = OpConstant %6 0.699999988 + %61 = OpTypeVector %6 4 + %62 = OpTypePointer Output %61 + %63 = OpVariable %62 Output + %67 = OpTypePointer Input %6 + %68 = OpVariable %67 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %64 = OpFunctionCall %6 %8 + %65 = OpFunctionCall %6 %10 + %66 = OpCompositeConstruct %61 %64 %65 %14 %29 + OpStore %63 %66 + OpReturn + OpFunctionEnd + %8 = OpFunction %6 None %7 + %9 = OpLabel + %13 = OpVariable %12 Function + OpStore %13 %14 +-%22 = OpAccessChain %21 %18 %20 +-%23 = OpLoad %15 %22 +-%26 = OpINotEqual %25 %23 %24 +-OpSelectionMerge %28 None +-OpBranchConditional %26 %27 %28 +-%27 = OpLabel +-%30 = OpLoad %6 %13 +-%31 = OpFAdd %6 %30 %29 +-OpStore %13 %31 +-OpBranch %28 +-%28 = OpLabel + %33 = OpAccessChain %21 %18 %32 + %34 = OpLoad %15 %33 + %35 = OpConvertUToF %6 %34 + %36 = OpExtInst %6 %1 Log2 %35 + %37 = OpLoad %6 %13 + %38 = OpFAdd %6 %37 %36 + OpStore %13 %38 + %39 = OpLoad %6 %13 + %40 = OpLoad %6 %13 + %41 = OpExtInst %6 %1 Sqrt %40 + %42 = OpFSub %6 %39 %41 + OpReturnValue %42 + OpFunctionEnd + %10 = OpFunction %6 None %7 + %11 = OpLabel + %45 = OpVariable %12 Function + %46 = OpAccessChain %21 %18 %20 + %47 = OpLoad %15 %46 + %48 = OpConvertUToF %6 %47 + %50 = OpFDiv %6 %48 %49 + OpStore %45 %50 + %51 = OpLoad %6 %45 + %54 = OpExtInst %6 %1 FClamp %51 %52 %53 + %55 = OpLoad %6 %45 + %56 = OpFMul %6 %55 %54 + OpStore %45 %56 ++%71 = OpAccessChain %21 %18 %32 ++%72 = OpLoad %15 %71 ++%73 = OpINotEqual %25 %72 %24 ++OpSelectionMerge %75 None ++OpBranchConditional %73 %74 %75 ++%74 = OpLabel + %57 = OpLoad %6 %45 ++%77 = OpFSub %6 %57 %76 ++OpStore %45 %77 ++OpBranch %75 ++%75 = OpLabel ++%78 = OpLoad %6 %45 +-%58 = OpExtInst %6 %1 Exp %57 ++%58 = OpExtInst %6 %1 Exp %78 + OpReturnValue %58 + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, ExtraIfBlockNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %63 %68 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %8 RelaxedPrecision + OpDecorate %10 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + OpMemberDecorate %16 0 RelaxedPrecision + OpMemberDecorate %16 0 Offset 0 + OpMemberDecorate %16 1 RelaxedPrecision + OpMemberDecorate %16 1 Offset 4 + OpDecorate %16 Block + OpDecorate %18 DescriptorSet 0 + OpDecorate %18 Binding 0 + OpDecorate %23 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %31 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %38 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %45 RelaxedPrecision + OpDecorate %47 RelaxedPrecision + OpDecorate %48 RelaxedPrecision + OpDecorate %50 RelaxedPrecision + OpDecorate %51 RelaxedPrecision + OpDecorate %54 RelaxedPrecision + OpDecorate %55 RelaxedPrecision + OpDecorate %56 RelaxedPrecision + OpDecorate %57 RelaxedPrecision + OpDecorate %58 RelaxedPrecision + OpDecorate %63 RelaxedPrecision + OpDecorate %63 Location 0 + OpDecorate %64 RelaxedPrecision + OpDecorate %65 RelaxedPrecision + OpDecorate %66 RelaxedPrecision + OpDecorate %68 RelaxedPrecision + OpDecorate %68 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeFunction %6 + %12 = OpTypePointer Function %6 + %14 = OpConstant %6 0 + %15 = OpTypeInt 32 0 + %16 = OpTypeStruct %15 %15 + %17 = OpTypePointer Uniform %16 + %18 = OpVariable %17 Uniform + %19 = OpTypeInt 32 1 + %20 = OpConstant %19 0 + %21 = OpTypePointer Uniform %15 + %24 = OpConstant %15 0 + %25 = OpTypeBool + %29 = OpConstant %6 1 + %32 = OpConstant %19 1 + %49 = OpConstant %6 10 + %52 = OpConstant %6 0.5 + %53 = OpConstant %6 0.699999988 + %61 = OpTypeVector %6 4 + %62 = OpTypePointer Output %61 + %63 = OpVariable %62 Output + %67 = OpTypePointer Input %6 + %68 = OpVariable %67 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %64 = OpFunctionCall %6 %8 + %65 = OpFunctionCall %6 %10 + %66 = OpCompositeConstruct %61 %64 %65 %14 %29 + OpStore %63 %66 + OpReturn + OpFunctionEnd + %8 = OpFunction %6 None %7 + %9 = OpLabel + %13 = OpVariable %12 Function + OpStore %13 %14 + %22 = OpAccessChain %21 %18 %20 + %23 = OpLoad %15 %22 + %26 = OpINotEqual %25 %23 %24 + OpSelectionMerge %28 None + OpBranchConditional %26 %27 %28 + %27 = OpLabel + %30 = OpLoad %6 %13 + %31 = OpFAdd %6 %30 %29 + OpStore %13 %31 + OpBranch %28 + %28 = OpLabel + %33 = OpAccessChain %21 %18 %32 + %34 = OpLoad %15 %33 + %35 = OpConvertUToF %6 %34 + %36 = OpExtInst %6 %1 Log2 %35 + %37 = OpLoad %6 %13 + %38 = OpFAdd %6 %37 %36 + OpStore %13 %38 + %39 = OpLoad %6 %13 + %40 = OpLoad %6 %13 + %41 = OpExtInst %6 %1 Sqrt %40 + %42 = OpFSub %6 %39 %41 + OpReturnValue %42 + OpFunctionEnd + %10 = OpFunction %6 None %7 + %11 = OpLabel + %45 = OpVariable %12 Function + %46 = OpAccessChain %21 %18 %20 + %47 = OpLoad %15 %46 + %48 = OpConvertUToF %6 %47 + %50 = OpFDiv %6 %48 %49 + OpStore %45 %50 + %51 = OpLoad %6 %45 + %54 = OpExtInst %6 %1 FClamp %51 %52 %53 + %55 = OpLoad %6 %45 + %56 = OpFMul %6 %55 %54 + OpStore %45 %56 + %57 = OpLoad %6 %45 + %58 = OpExtInst %6 %1 Exp %57 + OpReturnValue %58 + OpFunctionEnd + +)"; + constexpr char kDstNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %63 %69 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %8 RelaxedPrecision + OpDecorate %10 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + OpMemberDecorate %16 0 RelaxedPrecision + OpMemberDecorate %16 0 Offset 0 + OpMemberDecorate %16 1 RelaxedPrecision + OpMemberDecorate %16 1 Offset 4 + OpDecorate %16 Block + OpDecorate %18 DescriptorSet 0 + OpDecorate %18 Binding 0 + OpDecorate %23 RelaxedPrecision + OpDecorate %24 RelaxedPrecision + OpDecorate %25 RelaxedPrecision + OpDecorate %26 RelaxedPrecision + OpDecorate %27 RelaxedPrecision + OpDecorate %28 RelaxedPrecision + OpDecorate %29 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %31 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %38 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %44 RelaxedPrecision + OpDecorate %45 RelaxedPrecision + OpDecorate %46 RelaxedPrecision + OpDecorate %48 RelaxedPrecision + OpDecorate %55 RelaxedPrecision + OpDecorate %56 RelaxedPrecision + OpDecorate %57 RelaxedPrecision + OpDecorate %58 RelaxedPrecision + OpDecorate %63 RelaxedPrecision + OpDecorate %63 Location 0 + OpDecorate %64 RelaxedPrecision + OpDecorate %65 RelaxedPrecision + OpDecorate %67 RelaxedPrecision + OpDecorate %69 RelaxedPrecision + OpDecorate %69 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeFunction %6 + %12 = OpTypePointer Function %6 + %14 = OpConstant %6 0 + %15 = OpTypeInt 32 0 + %16 = OpTypeStruct %15 %15 + %17 = OpTypePointer Uniform %16 + %18 = OpVariable %17 Uniform + %19 = OpTypeInt 32 1 + %20 = OpConstant %19 1 + %21 = OpTypePointer Uniform %15 + %35 = OpConstant %19 0 + %39 = OpConstant %6 10 + %42 = OpConstant %6 0.5 + %43 = OpConstant %6 0.699999988 + %49 = OpConstant %15 0 + %50 = OpTypeBool + %54 = OpConstant %6 0.100000001 + %61 = OpTypeVector %6 4 + %62 = OpTypePointer Output %61 + %63 = OpVariable %62 Output + %66 = OpConstant %6 1 + %68 = OpTypePointer Input %6 + %69 = OpVariable %68 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %64 = OpFunctionCall %6 %8 + %65 = OpFunctionCall %6 %10 + %67 = OpCompositeConstruct %61 %64 %65 %14 %66 + OpStore %63 %67 + OpReturn + OpFunctionEnd + %8 = OpFunction %6 None %7 + %9 = OpLabel + %13 = OpVariable %12 Function + OpStore %13 %14 + %22 = OpAccessChain %21 %18 %20 + %23 = OpLoad %15 %22 + %24 = OpConvertUToF %6 %23 + %25 = OpExtInst %6 %1 Log2 %24 + %26 = OpLoad %6 %13 + %27 = OpFAdd %6 %26 %25 + OpStore %13 %27 + %28 = OpLoad %6 %13 + %29 = OpLoad %6 %13 + %30 = OpExtInst %6 %1 Sqrt %29 + %31 = OpFSub %6 %28 %30 + OpReturnValue %31 + OpFunctionEnd + %10 = OpFunction %6 None %7 + %11 = OpLabel + %34 = OpVariable %12 Function + %36 = OpAccessChain %21 %18 %35 + %37 = OpLoad %15 %36 + %38 = OpConvertUToF %6 %37 + %40 = OpFDiv %6 %38 %39 + OpStore %34 %40 + %41 = OpLoad %6 %34 + %44 = OpExtInst %6 %1 FClamp %41 %42 %43 + %45 = OpLoad %6 %34 + %46 = OpFMul %6 %45 %44 + OpStore %34 %46 + %47 = OpAccessChain %21 %18 %20 + %48 = OpLoad %15 %47 + %51 = OpINotEqual %50 %48 %49 + OpSelectionMerge %53 None + OpBranchConditional %51 %52 %53 + %52 = OpLabel + %55 = OpLoad %6 %34 + %56 = OpFSub %6 %55 %54 + OpStore %34 %56 + OpBranch %53 + %53 = OpLabel + %57 = OpLoad %6 %34 + %58 = OpExtInst %6 %1 Exp %57 + OpReturnValue %58 + OpFunctionEnd + +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 69 ++; Bound: 81 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %63 %68 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %8 RelaxedPrecision + OpDecorate %10 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + OpMemberDecorate %16 0 RelaxedPrecision + OpMemberDecorate %16 0 Offset 0 + OpMemberDecorate %16 1 RelaxedPrecision + OpMemberDecorate %16 1 Offset 4 + OpDecorate %16 Block + OpDecorate %18 DescriptorSet 0 + OpDecorate %18 Binding 0 +-OpDecorate %23 RelaxedPrecision +-OpDecorate %30 RelaxedPrecision +-OpDecorate %31 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %38 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %45 RelaxedPrecision + OpDecorate %47 RelaxedPrecision + OpDecorate %48 RelaxedPrecision + OpDecorate %50 RelaxedPrecision + OpDecorate %51 RelaxedPrecision + OpDecorate %54 RelaxedPrecision + OpDecorate %55 RelaxedPrecision + OpDecorate %56 RelaxedPrecision ++OpDecorate %72 RelaxedPrecision + OpDecorate %57 RelaxedPrecision ++OpDecorate %77 RelaxedPrecision ++OpDecorate %78 RelaxedPrecision + OpDecorate %58 RelaxedPrecision + OpDecorate %63 RelaxedPrecision + OpDecorate %63 Location 0 + OpDecorate %64 RelaxedPrecision + OpDecorate %65 RelaxedPrecision + OpDecorate %66 RelaxedPrecision + OpDecorate %68 RelaxedPrecision + OpDecorate %68 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeFunction %6 + %12 = OpTypePointer Function %6 + %14 = OpConstant %6 0 + %15 = OpTypeInt 32 0 + %16 = OpTypeStruct %15 %15 + %17 = OpTypePointer Uniform %16 + %18 = OpVariable %17 Uniform + %19 = OpTypeInt 32 1 + %20 = OpConstant %19 0 + %21 = OpTypePointer Uniform %15 + %24 = OpConstant %15 0 + %25 = OpTypeBool + %29 = OpConstant %6 1 + %32 = OpConstant %19 1 + %49 = OpConstant %6 10 + %52 = OpConstant %6 0.5 ++%76 = OpConstant %6 0.100000001 + %53 = OpConstant %6 0.699999988 + %61 = OpTypeVector %6 4 + %62 = OpTypePointer Output %61 + %63 = OpVariable %62 Output + %67 = OpTypePointer Input %6 + %68 = OpVariable %67 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %64 = OpFunctionCall %6 %8 + %65 = OpFunctionCall %6 %10 + %66 = OpCompositeConstruct %61 %64 %65 %14 %29 + OpStore %63 %66 + OpReturn + OpFunctionEnd + %8 = OpFunction %6 None %7 + %9 = OpLabel + %13 = OpVariable %12 Function + OpStore %13 %14 +-%22 = OpAccessChain %21 %18 %20 +-%23 = OpLoad %15 %22 +-%26 = OpINotEqual %25 %23 %24 +-OpSelectionMerge %28 None +-OpBranchConditional %26 %27 %28 +-%27 = OpLabel +-%30 = OpLoad %6 %13 +-%31 = OpFAdd %6 %30 %29 +-OpStore %13 %31 +-OpBranch %28 +-%28 = OpLabel + %33 = OpAccessChain %21 %18 %32 + %34 = OpLoad %15 %33 + %35 = OpConvertUToF %6 %34 + %36 = OpExtInst %6 %1 Log2 %35 + %37 = OpLoad %6 %13 + %38 = OpFAdd %6 %37 %36 + OpStore %13 %38 + %39 = OpLoad %6 %13 + %40 = OpLoad %6 %13 + %41 = OpExtInst %6 %1 Sqrt %40 + %42 = OpFSub %6 %39 %41 + OpReturnValue %42 + OpFunctionEnd + %10 = OpFunction %6 None %7 + %11 = OpLabel + %45 = OpVariable %12 Function + %46 = OpAccessChain %21 %18 %20 + %47 = OpLoad %15 %46 + %48 = OpConvertUToF %6 %47 + %50 = OpFDiv %6 %48 %49 + OpStore %45 %50 + %51 = OpLoad %6 %45 + %54 = OpExtInst %6 %1 FClamp %51 %52 %53 + %55 = OpLoad %6 %45 + %56 = OpFMul %6 %55 %54 + OpStore %45 %56 ++%71 = OpAccessChain %21 %18 %32 ++%72 = OpLoad %15 %71 ++%73 = OpINotEqual %25 %72 %24 ++OpSelectionMerge %75 None ++OpBranchConditional %73 %74 %75 ++%74 = OpLabel + %57 = OpLoad %6 %45 ++%77 = OpFSub %6 %57 %76 ++OpStore %45 %77 ++OpBranch %75 ++%75 = OpLabel ++%78 = OpLoad %6 %45 +-%58 = OpExtInst %6 %1 Exp %57 ++%58 = OpExtInst %6 %1 Exp %78 + OpReturnValue %58 + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/extra_if_block_dst.spvasm b/test/diff/diff_files/extra_if_block_dst.spvasm new file mode 100644 index 00000000..79bda830 --- /dev/null +++ b/test/diff/diff_files/extra_if_block_dst.spvasm @@ -0,0 +1,136 @@ + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %63 %69 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "f1(" + OpName %10 "f2(" + OpName %13 "v" + OpName %16 "Buffer" + OpMemberName %16 0 "flag1" + OpMemberName %16 1 "flag2" + OpName %18 "" + OpName %34 "v" + OpName %63 "color" + OpName %69 "v" + OpDecorate %8 RelaxedPrecision + OpDecorate %10 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + OpMemberDecorate %16 0 RelaxedPrecision + OpMemberDecorate %16 0 Offset 0 + OpMemberDecorate %16 1 RelaxedPrecision + OpMemberDecorate %16 1 Offset 4 + OpDecorate %16 Block + OpDecorate %18 DescriptorSet 0 + OpDecorate %18 Binding 0 + OpDecorate %23 RelaxedPrecision + OpDecorate %24 RelaxedPrecision + OpDecorate %25 RelaxedPrecision + OpDecorate %26 RelaxedPrecision + OpDecorate %27 RelaxedPrecision + OpDecorate %28 RelaxedPrecision + OpDecorate %29 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %31 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %38 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %44 RelaxedPrecision + OpDecorate %45 RelaxedPrecision + OpDecorate %46 RelaxedPrecision + OpDecorate %48 RelaxedPrecision + OpDecorate %55 RelaxedPrecision + OpDecorate %56 RelaxedPrecision + OpDecorate %57 RelaxedPrecision + OpDecorate %58 RelaxedPrecision + OpDecorate %63 RelaxedPrecision + OpDecorate %63 Location 0 + OpDecorate %64 RelaxedPrecision + OpDecorate %65 RelaxedPrecision + OpDecorate %67 RelaxedPrecision + OpDecorate %69 RelaxedPrecision + OpDecorate %69 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeFunction %6 + %12 = OpTypePointer Function %6 + %14 = OpConstant %6 0 + %15 = OpTypeInt 32 0 + %16 = OpTypeStruct %15 %15 + %17 = OpTypePointer Uniform %16 + %18 = OpVariable %17 Uniform + %19 = OpTypeInt 32 1 + %20 = OpConstant %19 1 + %21 = OpTypePointer Uniform %15 + %35 = OpConstant %19 0 + %39 = OpConstant %6 10 + %42 = OpConstant %6 0.5 + %43 = OpConstant %6 0.699999988 + %49 = OpConstant %15 0 + %50 = OpTypeBool + %54 = OpConstant %6 0.100000001 + %61 = OpTypeVector %6 4 + %62 = OpTypePointer Output %61 + %63 = OpVariable %62 Output + %66 = OpConstant %6 1 + %68 = OpTypePointer Input %6 + %69 = OpVariable %68 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %64 = OpFunctionCall %6 %8 + %65 = OpFunctionCall %6 %10 + %67 = OpCompositeConstruct %61 %64 %65 %14 %66 + OpStore %63 %67 + OpReturn + OpFunctionEnd + %8 = OpFunction %6 None %7 + %9 = OpLabel + %13 = OpVariable %12 Function + OpStore %13 %14 + %22 = OpAccessChain %21 %18 %20 + %23 = OpLoad %15 %22 + %24 = OpConvertUToF %6 %23 + %25 = OpExtInst %6 %1 Log2 %24 + %26 = OpLoad %6 %13 + %27 = OpFAdd %6 %26 %25 + OpStore %13 %27 + %28 = OpLoad %6 %13 + %29 = OpLoad %6 %13 + %30 = OpExtInst %6 %1 Sqrt %29 + %31 = OpFSub %6 %28 %30 + OpReturnValue %31 + OpFunctionEnd + %10 = OpFunction %6 None %7 + %11 = OpLabel + %34 = OpVariable %12 Function + %36 = OpAccessChain %21 %18 %35 + %37 = OpLoad %15 %36 + %38 = OpConvertUToF %6 %37 + %40 = OpFDiv %6 %38 %39 + OpStore %34 %40 + %41 = OpLoad %6 %34 + %44 = OpExtInst %6 %1 FClamp %41 %42 %43 + %45 = OpLoad %6 %34 + %46 = OpFMul %6 %45 %44 + OpStore %34 %46 + %47 = OpAccessChain %21 %18 %20 + %48 = OpLoad %15 %47 + %51 = OpINotEqual %50 %48 %49 + OpSelectionMerge %53 None + OpBranchConditional %51 %52 %53 + %52 = OpLabel + %55 = OpLoad %6 %34 + %56 = OpFSub %6 %55 %54 + OpStore %34 %56 + OpBranch %53 + %53 = OpLabel + %57 = OpLoad %6 %34 + %58 = OpExtInst %6 %1 Exp %57 + OpReturnValue %58 + OpFunctionEnd + diff --git a/test/diff/diff_files/extra_if_block_src.spvasm b/test/diff/diff_files/extra_if_block_src.spvasm new file mode 100644 index 00000000..1b43ccb1 --- /dev/null +++ b/test/diff/diff_files/extra_if_block_src.spvasm @@ -0,0 +1,137 @@ +;; Test where src has an extra if block in one function, and dst has an extra +;; if block in another function. + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %63 %68 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "f1(" + OpName %10 "f2(" + OpName %13 "v" + OpName %16 "Buffer" + OpMemberName %16 0 "flag1" + OpMemberName %16 1 "flag2" + OpName %18 "" + OpName %45 "v" + OpName %63 "color" + OpName %68 "v" + OpDecorate %8 RelaxedPrecision + OpDecorate %10 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + OpMemberDecorate %16 0 RelaxedPrecision + OpMemberDecorate %16 0 Offset 0 + OpMemberDecorate %16 1 RelaxedPrecision + OpMemberDecorate %16 1 Offset 4 + OpDecorate %16 Block + OpDecorate %18 DescriptorSet 0 + OpDecorate %18 Binding 0 + OpDecorate %23 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %31 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %38 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %45 RelaxedPrecision + OpDecorate %47 RelaxedPrecision + OpDecorate %48 RelaxedPrecision + OpDecorate %50 RelaxedPrecision + OpDecorate %51 RelaxedPrecision + OpDecorate %54 RelaxedPrecision + OpDecorate %55 RelaxedPrecision + OpDecorate %56 RelaxedPrecision + OpDecorate %57 RelaxedPrecision + OpDecorate %58 RelaxedPrecision + OpDecorate %63 RelaxedPrecision + OpDecorate %63 Location 0 + OpDecorate %64 RelaxedPrecision + OpDecorate %65 RelaxedPrecision + OpDecorate %66 RelaxedPrecision + OpDecorate %68 RelaxedPrecision + OpDecorate %68 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeFunction %6 + %12 = OpTypePointer Function %6 + %14 = OpConstant %6 0 + %15 = OpTypeInt 32 0 + %16 = OpTypeStruct %15 %15 + %17 = OpTypePointer Uniform %16 + %18 = OpVariable %17 Uniform + %19 = OpTypeInt 32 1 + %20 = OpConstant %19 0 + %21 = OpTypePointer Uniform %15 + %24 = OpConstant %15 0 + %25 = OpTypeBool + %29 = OpConstant %6 1 + %32 = OpConstant %19 1 + %49 = OpConstant %6 10 + %52 = OpConstant %6 0.5 + %53 = OpConstant %6 0.699999988 + %61 = OpTypeVector %6 4 + %62 = OpTypePointer Output %61 + %63 = OpVariable %62 Output + %67 = OpTypePointer Input %6 + %68 = OpVariable %67 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %64 = OpFunctionCall %6 %8 + %65 = OpFunctionCall %6 %10 + %66 = OpCompositeConstruct %61 %64 %65 %14 %29 + OpStore %63 %66 + OpReturn + OpFunctionEnd + %8 = OpFunction %6 None %7 + %9 = OpLabel + %13 = OpVariable %12 Function + OpStore %13 %14 + %22 = OpAccessChain %21 %18 %20 + %23 = OpLoad %15 %22 + %26 = OpINotEqual %25 %23 %24 + OpSelectionMerge %28 None + OpBranchConditional %26 %27 %28 + %27 = OpLabel + %30 = OpLoad %6 %13 + %31 = OpFAdd %6 %30 %29 + OpStore %13 %31 + OpBranch %28 + %28 = OpLabel + %33 = OpAccessChain %21 %18 %32 + %34 = OpLoad %15 %33 + %35 = OpConvertUToF %6 %34 + %36 = OpExtInst %6 %1 Log2 %35 + %37 = OpLoad %6 %13 + %38 = OpFAdd %6 %37 %36 + OpStore %13 %38 + %39 = OpLoad %6 %13 + %40 = OpLoad %6 %13 + %41 = OpExtInst %6 %1 Sqrt %40 + %42 = OpFSub %6 %39 %41 + OpReturnValue %42 + OpFunctionEnd + %10 = OpFunction %6 None %7 + %11 = OpLabel + %45 = OpVariable %12 Function + %46 = OpAccessChain %21 %18 %20 + %47 = OpLoad %15 %46 + %48 = OpConvertUToF %6 %47 + %50 = OpFDiv %6 %48 %49 + OpStore %45 %50 + %51 = OpLoad %6 %45 + %54 = OpExtInst %6 %1 FClamp %51 %52 %53 + %55 = OpLoad %6 %45 + %56 = OpFMul %6 %55 %54 + OpStore %45 %56 + %57 = OpLoad %6 %45 + %58 = OpExtInst %6 %1 Exp %57 + OpReturnValue %58 + OpFunctionEnd + diff --git a/test/diff/diff_files/generate_tests.py b/test/diff/diff_files/generate_tests.py new file mode 100755 index 00000000..cc3175d3 --- /dev/null +++ b/test/diff/diff_files/generate_tests.py @@ -0,0 +1,304 @@ +#! /usr/bin/python3 +# +# Copyright (c) 2022 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. + +import glob +import os +import subprocess +import sys + +# A handful of relevant tests are hand-picked to generate extra unit tests with +# specific options of spirv-diff. +IGNORE_SET_BINDING_TESTS = ['different_decorations_vertex'] +IGNORE_LOCATION_TESTS = ['different_decorations_fragment'] +IGNORE_DECORATIONS_TESTS = ['different_decorations_vertex', 'different_decorations_fragment'] +DUMP_IDS_TESTS = ['basic', 'int_vs_uint_constants', 'multiple_same_entry_points', 'small_functions_small_diffs'] + +LICENSE = u"""Copyright (c) 2022 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. +""" + +TEMPLATE_TEST_FILE = u"""// GENERATED FILE - DO NOT EDIT. +// Generated by {script_name} +// +{license} + +#include "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools {{ +namespace diff {{ +namespace {{ + +{test_comment} +constexpr char kSrc[] = R"({src_spirv})"; +constexpr char kDst[] = R"({dst_spirv})"; + +TEST(DiffTest, {test_name}) {{ + constexpr char kDiff[] = R"({diff_spirv})"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +}} + +TEST(DiffTest, {test_name}NoDebug) {{ + constexpr char kSrcNoDebug[] = R"({src_spirv_no_debug})"; + constexpr char kDstNoDebug[] = R"({dst_spirv_no_debug})"; + constexpr char kDiff[] = R"({diff_spirv_no_debug})"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +}} +{extra_tests} +}} // namespace +}} // namespace diff +}} // namespace spvtools +""" + +TEMPLATE_TEST_FUNC = u""" +TEST(DiffTest, {test_name}{test_tag}) {{ + constexpr char kDiff[] = R"({diff_spirv})"; + Options options; + {test_options} + DoStringDiffTest(kSrc, kDst, kDiff, options); +}} +""" + +TEMPLATE_TEST_FILES_CMAKE = u"""# GENERATED FILE - DO NOT EDIT. +# Generated by {script_name} +# +{license} + +list(APPEND DIFF_TEST_FILES +{test_files} +) +""" + +VARIANT_NONE = 0 +VARIANT_IGNORE_SET_BINDING = 1 +VARIANT_IGNORE_LOCATION = 2 +VARIANT_IGNORE_DECORATIONS = 3 +VARIANT_DUMP_IDS = 4 + +def print_usage(): + print("Usage: {} <path-to-spirv-diff>".format(sys.argv[0])) + +def remove_debug_info(in_path): + tmp_dir = '.no_dbg' + + if not os.path.exists(tmp_dir): + os.makedirs(tmp_dir) + + (in_basename, in_ext) = os.path.splitext(in_path) + out_name = in_basename + '_no_dbg' + in_ext + out_path = os.path.join(tmp_dir, out_name) + + with open(in_path, 'r') as fin: + with open(out_path, 'w') as fout: + for line in fin: + ops = line.strip().split() + op = ops[0] if len(ops) > 0 else '' + if (op != ';;' and op != 'OpName' and op != 'OpMemberName' and op != 'OpString' and + op != 'OpLine' and op != 'OpNoLine' and op != 'OpModuleProcessed'): + fout.write(line) + + return out_path + +def make_src_file(test_name): + return '{}_src.spvasm'.format(test_name) + +def make_dst_file(test_name): + return '{}_dst.spvasm'.format(test_name) + +def make_cpp_file(test_name): + return '{}_autogen.cpp'.format(test_name) + +def make_camel_case(test_name): + return test_name.replace('_', ' ').title().replace(' ', '') + +def make_comment(text, comment_prefix): + return '\n'.join([comment_prefix + (' ' if line.strip() else '') + line for line in text.splitlines()]) + +def read_file(file_name): + with open(file_name, 'r') as f: + content = f.read() + + # Use unix line endings. + content = content.replace('\r\n', '\n') + + return content + +def parse_test_comment(src_spirv_file_name, src_spirv): + src_spirv_lines = src_spirv.splitlines() + comment_line_count = 0 + while comment_line_count < len(src_spirv_lines): + if not src_spirv_lines[comment_line_count].strip().startswith(';;'): + break + comment_line_count += 1 + + if comment_line_count == 0: + print("Expected comment on test file '{}'. See README.md next to this file.".format(src_spirv_file_name)) + sys.exit(1) + + comment_block = src_spirv_lines[:comment_line_count] + spirv_block = src_spirv_lines[comment_line_count:] + + comment_block = ['// ' + line.replace(';;', '').strip() for line in comment_block] + + return '\n'.join(spirv_block), '\n'.join(comment_block) + +def run_diff_tool(diff_tool, src_file, dst_file, variant): + args = [diff_tool] + + if variant == VARIANT_IGNORE_SET_BINDING or variant == VARIANT_IGNORE_DECORATIONS: + args.append('--ignore-set-binding') + + if variant == VARIANT_IGNORE_LOCATION or variant == VARIANT_IGNORE_DECORATIONS: + args.append('--ignore-location') + + if variant == VARIANT_DUMP_IDS: + args.append('--with-id-map') + + args.append('--no-color') + args.append('--no-indent') + + args.append(src_file) + args.append(dst_file) + + success = True + print(' '.join(args)) + process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + out, err = process.communicate() + + if process.returncode != 0: + print(err) + sys.exit(process.returncode) + + # Use unix line endings. + out = out.replace('\r\n', '\n') + + return out + +def generate_extra_test(diff_tool, src_file, dst_file, variant, test_name_camel_case, test_tag, test_options): + diff = run_diff_tool(diff_tool, src_file, dst_file, variant) + return TEMPLATE_TEST_FUNC.format( + test_name = test_name_camel_case, + test_tag = test_tag, + test_options = test_options, + diff_spirv = diff) + +def generate_test(diff_tool, test_name): + src_file = make_src_file(test_name) + dst_file = make_dst_file(test_name) + src_file_no_debug = remove_debug_info(src_file) + dst_file_no_debug = remove_debug_info(dst_file) + + src_spirv = read_file(src_file) + dst_spirv = read_file(dst_file) + src_spirv_no_debug = read_file(src_file_no_debug) + dst_spirv_no_debug = read_file(dst_file_no_debug) + + test_name_camel_case = make_camel_case(test_name) + + diff_spirv = run_diff_tool(diff_tool, src_file, dst_file, VARIANT_NONE) + diff_spirv_no_debug = run_diff_tool(diff_tool, src_file_no_debug, dst_file_no_debug, VARIANT_NONE) + + extra_tests = [] + + if test_name in IGNORE_SET_BINDING_TESTS: + extra_tests.append(generate_extra_test(diff_tool, src_file, dst_file, VARIANT_IGNORE_SET_BINDING, + test_name_camel_case, 'IgnoreSetBinding', 'options.ignore_set_binding = true;')) + + if test_name in IGNORE_LOCATION_TESTS: + extra_tests.append(generate_extra_test(diff_tool, src_file, dst_file, VARIANT_IGNORE_LOCATION, + test_name_camel_case, 'IgnoreLocation', 'options.ignore_location = true;')) + + if test_name in IGNORE_DECORATIONS_TESTS: + extra_tests.append(generate_extra_test(diff_tool, src_file, dst_file, VARIANT_IGNORE_DECORATIONS, + test_name_camel_case, 'IgnoreSetBindingLocation', + '\n '.join(['options.ignore_set_binding = true;', 'options.ignore_location = true;']))) + + if test_name in DUMP_IDS_TESTS: + extra_tests.append(generate_extra_test(diff_tool, src_file, dst_file, VARIANT_DUMP_IDS, + test_name_camel_case, 'DumpIds', 'options.dump_id_map = true;')) + + src_spirv, test_comment = parse_test_comment(src_file, src_spirv) + + test_file = TEMPLATE_TEST_FILE.format( + script_name = os.path.basename(__file__), + license = make_comment(LICENSE, '//'), + test_comment = test_comment, + test_name = test_name_camel_case, + src_spirv = src_spirv, + dst_spirv = dst_spirv, + diff_spirv = diff_spirv, + src_spirv_no_debug = src_spirv_no_debug, + dst_spirv_no_debug = dst_spirv_no_debug, + diff_spirv_no_debug = diff_spirv_no_debug, + extra_tests = ''.join(extra_tests)) + + test_file_name = make_cpp_file(test_name) + with open(test_file_name, 'wb') as fout: + fout.write(str.encode(test_file)) + + return test_file_name + +def generate_tests(diff_tool, test_names): + return [generate_test(diff_tool, test_name) for test_name in test_names] + +def generate_cmake(test_files): + cmake = TEMPLATE_TEST_FILES_CMAKE.format( + script_name = os.path.basename(__file__), + license = make_comment(LICENSE, '#'), + test_files = '\n'.join(['"diff_files/{}"'.format(f) for f in test_files])) + + with open('diff_test_files_autogen.cmake', 'wb') as fout: + fout.write(str.encode(cmake)) + +def main(): + + if len(sys.argv) != 2: + print_usage() + return 1 + + diff_tool = sys.argv[1] + if not os.path.exists(diff_tool): + print("No such file: {}".format(diff_tool)) + print_usage() + return 1 + + diff_tool = os.path.realpath(diff_tool) + os.chdir(os.path.dirname(__file__)) + + test_names = sorted([f[:-11] for f in glob.glob("*_src.spvasm")]) + + test_files = generate_tests(diff_tool, test_names) + + generate_cmake(test_files) + + return 0 + +if __name__ == '__main__': + sys.exit(main()) diff --git a/test/diff/diff_files/index_signedness_autogen.cpp b/test/diff/diff_files/index_signedness_autogen.cpp new file mode 100644 index 00000000..ab650be1 --- /dev/null +++ b/test/diff/diff_files/index_signedness_autogen.cpp @@ -0,0 +1,733 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Test where signedness of indices are different between src and dst. +constexpr char kSrc[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %13 "BufferOut" + OpMemberName %13 0 "o1" + OpMemberName %13 1 "o2" + OpMemberName %13 2 "o3" + OpName %15 "" + OpName %22 "BufferIn" + OpMemberName %22 0 "i1" + OpMemberName %22 1 "i2" + OpName %24 "" + OpDecorate %8 ArrayStride 4 + OpDecorate %9 ArrayStride 4 + OpDecorate %11 ArrayStride 4 + OpDecorate %12 ArrayStride 8 + OpMemberDecorate %13 0 Offset 0 + OpMemberDecorate %13 1 Offset 12 + OpMemberDecorate %13 2 Offset 24 + OpDecorate %13 BufferBlock + OpDecorate %15 DescriptorSet 0 + OpDecorate %15 Binding 1 + OpDecorate %18 ArrayStride 16 + OpDecorate %19 ArrayStride 48 + OpDecorate %21 ArrayStride 16 + OpMemberDecorate %22 0 Offset 0 + OpMemberDecorate %22 1 Offset 96 + OpDecorate %22 Block + OpDecorate %24 DescriptorSet 0 + OpDecorate %24 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpConstant %6 3 + %8 = OpTypeArray %6 %7 + %9 = OpTypeArray %6 %7 + %10 = OpConstant %6 2 + %11 = OpTypeArray %6 %10 + %12 = OpTypeArray %11 %10 + %13 = OpTypeStruct %8 %9 %12 + %14 = OpTypePointer Uniform %13 + %15 = OpVariable %14 Uniform + %16 = OpTypeInt 32 1 + %17 = OpConstant %16 0 + %18 = OpTypeArray %6 %7 + %19 = OpTypeArray %18 %10 + %20 = OpConstant %6 4 + %21 = OpTypeArray %6 %20 + %22 = OpTypeStruct %19 %21 + %23 = OpTypePointer Uniform %22 + %24 = OpVariable %23 Uniform + %25 = OpTypePointer Uniform %6 + %28 = OpConstant %6 1 + %31 = OpConstant %16 1 + %34 = OpConstant %6 0 + %37 = OpConstant %16 2 + %61 = OpConstant %16 3 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %26 = OpAccessChain %25 %24 %17 %17 %17 + %27 = OpLoad %6 %26 + %29 = OpIAdd %6 %27 %28 + %30 = OpAccessChain %25 %15 %17 %17 + OpStore %30 %29 + %32 = OpAccessChain %25 %24 %17 %31 %17 + %33 = OpLoad %6 %32 + %35 = OpIAdd %6 %33 %34 + %36 = OpAccessChain %25 %15 %17 %31 + OpStore %36 %35 + %38 = OpAccessChain %25 %24 %17 %31 %31 + %39 = OpLoad %6 %38 + %40 = OpIAdd %6 %39 %10 + %41 = OpAccessChain %25 %15 %17 %37 + OpStore %41 %40 + %42 = OpAccessChain %25 %24 %17 %17 %37 + %43 = OpLoad %6 %42 + %44 = OpAccessChain %25 %15 %31 %17 + OpStore %44 %43 + %45 = OpAccessChain %25 %24 %17 %17 %31 + %46 = OpLoad %6 %45 + %47 = OpIMul %6 %46 %7 + %48 = OpAccessChain %25 %15 %31 %31 + OpStore %48 %47 + %49 = OpAccessChain %25 %24 %17 %31 %37 + %50 = OpLoad %6 %49 + %51 = OpAccessChain %25 %15 %31 %37 + OpStore %51 %50 + %52 = OpAccessChain %25 %24 %31 %17 + %53 = OpLoad %6 %52 + %54 = OpAccessChain %25 %15 %37 %17 %17 + OpStore %54 %53 + %55 = OpAccessChain %25 %24 %31 %31 + %56 = OpLoad %6 %55 + %57 = OpAccessChain %25 %15 %37 %17 %31 + OpStore %57 %56 + %58 = OpAccessChain %25 %24 %31 %37 + %59 = OpLoad %6 %58 + %60 = OpAccessChain %25 %15 %37 %31 %17 + OpStore %60 %59 + %62 = OpAccessChain %25 %24 %31 %61 + %63 = OpLoad %6 %62 + %64 = OpAccessChain %25 %15 %37 %31 %31 + OpStore %64 %63 + OpReturn + OpFunctionEnd +)"; +constexpr char kDst[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %13 "BufferOut" + OpMemberName %13 0 "o1" + OpMemberName %13 1 "o2" + OpMemberName %13 2 "o3" + OpName %15 "" + OpName %22 "BufferIn" + OpMemberName %22 0 "i1" + OpMemberName %22 1 "i2" + OpName %24 "" + OpDecorate %8 ArrayStride 4 + OpDecorate %9 ArrayStride 4 + OpDecorate %11 ArrayStride 4 + OpDecorate %12 ArrayStride 8 + OpMemberDecorate %13 0 Offset 0 + OpMemberDecorate %13 1 Offset 12 + OpMemberDecorate %13 2 Offset 24 + OpDecorate %13 BufferBlock + OpDecorate %15 DescriptorSet 0 + OpDecorate %15 Binding 1 + OpDecorate %18 ArrayStride 16 + OpDecorate %19 ArrayStride 48 + OpDecorate %21 ArrayStride 16 + OpMemberDecorate %22 0 Offset 0 + OpMemberDecorate %22 1 Offset 96 + OpDecorate %22 Block + OpDecorate %24 DescriptorSet 0 + OpDecorate %24 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %16 = OpTypeInt 32 1 + %7 = OpConstant %16 3 + %8 = OpTypeArray %6 %7 + %9 = OpTypeArray %6 %7 + %10 = OpConstant %16 2 + %11 = OpTypeArray %6 %10 + %12 = OpTypeArray %11 %10 + %13 = OpTypeStruct %8 %9 %12 + %14 = OpTypePointer Uniform %13 + %15 = OpVariable %14 Uniform + %18 = OpTypeArray %6 %7 + %19 = OpTypeArray %18 %10 + %20 = OpConstant %16 4 + %21 = OpTypeArray %6 %20 + %22 = OpTypeStruct %19 %21 + %23 = OpTypePointer Uniform %22 + %24 = OpVariable %23 Uniform + %25 = OpTypePointer Uniform %6 + %17 = OpConstant %16 0 + %28 = OpConstant %16 1 + %31 = OpConstant %6 1 + %34 = OpConstant %6 0 + %37 = OpConstant %6 2 + %61 = OpConstant %6 3 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %26 = OpAccessChain %25 %24 %17 %17 %17 + %27 = OpLoad %6 %26 + %29 = OpIAdd %6 %27 %28 + %30 = OpAccessChain %25 %15 %17 %17 + OpStore %30 %29 + %32 = OpAccessChain %25 %24 %17 %31 %17 + %33 = OpLoad %6 %32 + %35 = OpIAdd %6 %33 %34 + %36 = OpAccessChain %25 %15 %17 %31 + OpStore %36 %35 + %38 = OpAccessChain %25 %24 %17 %31 %31 + %39 = OpLoad %6 %38 + %40 = OpIAdd %6 %39 %37 + %41 = OpAccessChain %25 %15 %17 %10 + OpStore %41 %40 + %42 = OpAccessChain %25 %24 %17 %17 %10 + %43 = OpLoad %6 %42 + %44 = OpAccessChain %25 %15 %31 %17 + OpStore %44 %43 + %45 = OpAccessChain %25 %24 %17 %17 %31 + %46 = OpLoad %6 %45 + %47 = OpIMul %6 %46 %7 + %48 = OpAccessChain %25 %15 %31 %31 + OpStore %48 %47 + %49 = OpAccessChain %25 %24 %17 %31 %10 + %50 = OpLoad %6 %49 + %51 = OpAccessChain %25 %15 %31 %10 + OpStore %51 %50 + %52 = OpAccessChain %25 %24 %31 %17 + %53 = OpLoad %6 %52 + %54 = OpAccessChain %25 %15 %37 %17 %17 + OpStore %54 %53 + %55 = OpAccessChain %25 %24 %31 %31 + %56 = OpLoad %6 %55 + %57 = OpAccessChain %25 %15 %37 %17 %31 + OpStore %57 %56 + %58 = OpAccessChain %25 %24 %31 %37 + %59 = OpLoad %6 %58 + %60 = OpAccessChain %25 %15 %37 %31 %17 + OpStore %60 %59 + %62 = OpAccessChain %25 %24 %31 %61 + %63 = OpLoad %6 %62 + %64 = OpAccessChain %25 %15 %37 %31 %31 + OpStore %64 %63 + OpReturn + OpFunctionEnd + +)"; + +TEST(DiffTest, IndexSignedness) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 + ; Bound: 65 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %13 "BufferOut" + OpMemberName %13 0 "o1" + OpMemberName %13 1 "o2" + OpMemberName %13 2 "o3" + OpName %15 "" + OpName %22 "BufferIn" + OpMemberName %22 0 "i1" + OpMemberName %22 1 "i2" + OpName %24 "" + OpDecorate %8 ArrayStride 4 + OpDecorate %9 ArrayStride 4 + OpDecorate %11 ArrayStride 4 + OpDecorate %12 ArrayStride 8 + OpMemberDecorate %13 0 Offset 0 + OpMemberDecorate %13 1 Offset 12 + OpMemberDecorate %13 2 Offset 24 + OpDecorate %13 BufferBlock + OpDecorate %15 DescriptorSet 0 + OpDecorate %15 Binding 1 + OpDecorate %18 ArrayStride 16 + OpDecorate %19 ArrayStride 48 + OpDecorate %21 ArrayStride 16 + OpMemberDecorate %22 0 Offset 0 + OpMemberDecorate %22 1 Offset 96 + OpDecorate %22 Block + OpDecorate %24 DescriptorSet 0 + OpDecorate %24 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpConstant %6 3 +-%8 = OpTypeArray %6 %7 ++%8 = OpTypeArray %6 %61 +-%9 = OpTypeArray %6 %7 ++%9 = OpTypeArray %6 %61 + %10 = OpConstant %6 2 +-%11 = OpTypeArray %6 %10 ++%11 = OpTypeArray %6 %37 +-%12 = OpTypeArray %11 %10 ++%12 = OpTypeArray %11 %37 + %13 = OpTypeStruct %8 %9 %12 + %14 = OpTypePointer Uniform %13 + %15 = OpVariable %14 Uniform + %16 = OpTypeInt 32 1 + %17 = OpConstant %16 0 +-%18 = OpTypeArray %6 %7 ++%18 = OpTypeArray %6 %61 +-%19 = OpTypeArray %18 %10 ++%19 = OpTypeArray %18 %37 +-%20 = OpConstant %6 4 ++%20 = OpConstant %16 4 + %21 = OpTypeArray %6 %20 + %22 = OpTypeStruct %19 %21 + %23 = OpTypePointer Uniform %22 + %24 = OpVariable %23 Uniform + %25 = OpTypePointer Uniform %6 + %28 = OpConstant %6 1 + %31 = OpConstant %16 1 + %34 = OpConstant %6 0 + %37 = OpConstant %16 2 + %61 = OpConstant %16 3 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %26 = OpAccessChain %25 %24 %17 %17 %17 + %27 = OpLoad %6 %26 +-%29 = OpIAdd %6 %27 %28 ++%29 = OpIAdd %6 %27 %31 + %30 = OpAccessChain %25 %15 %17 %17 + OpStore %30 %29 +-%32 = OpAccessChain %25 %24 %17 %31 %17 ++%32 = OpAccessChain %25 %24 %17 %28 %17 + %33 = OpLoad %6 %32 + %35 = OpIAdd %6 %33 %34 +-%36 = OpAccessChain %25 %15 %17 %31 ++%36 = OpAccessChain %25 %15 %17 %28 + OpStore %36 %35 +-%38 = OpAccessChain %25 %24 %17 %31 %31 ++%38 = OpAccessChain %25 %24 %17 %28 %28 + %39 = OpLoad %6 %38 + %40 = OpIAdd %6 %39 %10 + %41 = OpAccessChain %25 %15 %17 %37 + OpStore %41 %40 + %42 = OpAccessChain %25 %24 %17 %17 %37 + %43 = OpLoad %6 %42 +-%44 = OpAccessChain %25 %15 %31 %17 ++%44 = OpAccessChain %25 %15 %28 %17 + OpStore %44 %43 +-%45 = OpAccessChain %25 %24 %17 %17 %31 ++%45 = OpAccessChain %25 %24 %17 %17 %28 + %46 = OpLoad %6 %45 +-%47 = OpIMul %6 %46 %7 ++%47 = OpIMul %6 %46 %61 +-%48 = OpAccessChain %25 %15 %31 %31 ++%48 = OpAccessChain %25 %15 %28 %28 + OpStore %48 %47 +-%49 = OpAccessChain %25 %24 %17 %31 %37 ++%49 = OpAccessChain %25 %24 %17 %28 %37 + %50 = OpLoad %6 %49 +-%51 = OpAccessChain %25 %15 %31 %37 ++%51 = OpAccessChain %25 %15 %28 %37 + OpStore %51 %50 +-%52 = OpAccessChain %25 %24 %31 %17 ++%52 = OpAccessChain %25 %24 %28 %17 + %53 = OpLoad %6 %52 +-%54 = OpAccessChain %25 %15 %37 %17 %17 ++%54 = OpAccessChain %25 %15 %10 %17 %17 + OpStore %54 %53 +-%55 = OpAccessChain %25 %24 %31 %31 ++%55 = OpAccessChain %25 %24 %28 %28 + %56 = OpLoad %6 %55 +-%57 = OpAccessChain %25 %15 %37 %17 %31 ++%57 = OpAccessChain %25 %15 %10 %17 %28 + OpStore %57 %56 +-%58 = OpAccessChain %25 %24 %31 %37 ++%58 = OpAccessChain %25 %24 %28 %10 + %59 = OpLoad %6 %58 +-%60 = OpAccessChain %25 %15 %37 %31 %17 ++%60 = OpAccessChain %25 %15 %10 %28 %17 + OpStore %60 %59 +-%62 = OpAccessChain %25 %24 %31 %61 ++%62 = OpAccessChain %25 %24 %28 %7 + %63 = OpLoad %6 %62 +-%64 = OpAccessChain %25 %15 %37 %31 %31 ++%64 = OpAccessChain %25 %15 %10 %28 %28 + OpStore %64 %63 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, IndexSignednessNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpDecorate %8 ArrayStride 4 + OpDecorate %9 ArrayStride 4 + OpDecorate %11 ArrayStride 4 + OpDecorate %12 ArrayStride 8 + OpMemberDecorate %13 0 Offset 0 + OpMemberDecorate %13 1 Offset 12 + OpMemberDecorate %13 2 Offset 24 + OpDecorate %13 BufferBlock + OpDecorate %15 DescriptorSet 0 + OpDecorate %15 Binding 1 + OpDecorate %18 ArrayStride 16 + OpDecorate %19 ArrayStride 48 + OpDecorate %21 ArrayStride 16 + OpMemberDecorate %22 0 Offset 0 + OpMemberDecorate %22 1 Offset 96 + OpDecorate %22 Block + OpDecorate %24 DescriptorSet 0 + OpDecorate %24 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpConstant %6 3 + %8 = OpTypeArray %6 %7 + %9 = OpTypeArray %6 %7 + %10 = OpConstant %6 2 + %11 = OpTypeArray %6 %10 + %12 = OpTypeArray %11 %10 + %13 = OpTypeStruct %8 %9 %12 + %14 = OpTypePointer Uniform %13 + %15 = OpVariable %14 Uniform + %16 = OpTypeInt 32 1 + %17 = OpConstant %16 0 + %18 = OpTypeArray %6 %7 + %19 = OpTypeArray %18 %10 + %20 = OpConstant %6 4 + %21 = OpTypeArray %6 %20 + %22 = OpTypeStruct %19 %21 + %23 = OpTypePointer Uniform %22 + %24 = OpVariable %23 Uniform + %25 = OpTypePointer Uniform %6 + %28 = OpConstant %6 1 + %31 = OpConstant %16 1 + %34 = OpConstant %6 0 + %37 = OpConstant %16 2 + %61 = OpConstant %16 3 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %26 = OpAccessChain %25 %24 %17 %17 %17 + %27 = OpLoad %6 %26 + %29 = OpIAdd %6 %27 %28 + %30 = OpAccessChain %25 %15 %17 %17 + OpStore %30 %29 + %32 = OpAccessChain %25 %24 %17 %31 %17 + %33 = OpLoad %6 %32 + %35 = OpIAdd %6 %33 %34 + %36 = OpAccessChain %25 %15 %17 %31 + OpStore %36 %35 + %38 = OpAccessChain %25 %24 %17 %31 %31 + %39 = OpLoad %6 %38 + %40 = OpIAdd %6 %39 %10 + %41 = OpAccessChain %25 %15 %17 %37 + OpStore %41 %40 + %42 = OpAccessChain %25 %24 %17 %17 %37 + %43 = OpLoad %6 %42 + %44 = OpAccessChain %25 %15 %31 %17 + OpStore %44 %43 + %45 = OpAccessChain %25 %24 %17 %17 %31 + %46 = OpLoad %6 %45 + %47 = OpIMul %6 %46 %7 + %48 = OpAccessChain %25 %15 %31 %31 + OpStore %48 %47 + %49 = OpAccessChain %25 %24 %17 %31 %37 + %50 = OpLoad %6 %49 + %51 = OpAccessChain %25 %15 %31 %37 + OpStore %51 %50 + %52 = OpAccessChain %25 %24 %31 %17 + %53 = OpLoad %6 %52 + %54 = OpAccessChain %25 %15 %37 %17 %17 + OpStore %54 %53 + %55 = OpAccessChain %25 %24 %31 %31 + %56 = OpLoad %6 %55 + %57 = OpAccessChain %25 %15 %37 %17 %31 + OpStore %57 %56 + %58 = OpAccessChain %25 %24 %31 %37 + %59 = OpLoad %6 %58 + %60 = OpAccessChain %25 %15 %37 %31 %17 + OpStore %60 %59 + %62 = OpAccessChain %25 %24 %31 %61 + %63 = OpLoad %6 %62 + %64 = OpAccessChain %25 %15 %37 %31 %31 + OpStore %64 %63 + OpReturn + OpFunctionEnd + +)"; + constexpr char kDstNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpDecorate %8 ArrayStride 4 + OpDecorate %9 ArrayStride 4 + OpDecorate %11 ArrayStride 4 + OpDecorate %12 ArrayStride 8 + OpMemberDecorate %13 0 Offset 0 + OpMemberDecorate %13 1 Offset 12 + OpMemberDecorate %13 2 Offset 24 + OpDecorate %13 BufferBlock + OpDecorate %15 DescriptorSet 0 + OpDecorate %15 Binding 1 + OpDecorate %18 ArrayStride 16 + OpDecorate %19 ArrayStride 48 + OpDecorate %21 ArrayStride 16 + OpMemberDecorate %22 0 Offset 0 + OpMemberDecorate %22 1 Offset 96 + OpDecorate %22 Block + OpDecorate %24 DescriptorSet 0 + OpDecorate %24 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %16 = OpTypeInt 32 1 + %7 = OpConstant %16 3 + %8 = OpTypeArray %6 %7 + %9 = OpTypeArray %6 %7 + %10 = OpConstant %16 2 + %11 = OpTypeArray %6 %10 + %12 = OpTypeArray %11 %10 + %13 = OpTypeStruct %8 %9 %12 + %14 = OpTypePointer Uniform %13 + %15 = OpVariable %14 Uniform + %18 = OpTypeArray %6 %7 + %19 = OpTypeArray %18 %10 + %20 = OpConstant %16 4 + %21 = OpTypeArray %6 %20 + %22 = OpTypeStruct %19 %21 + %23 = OpTypePointer Uniform %22 + %24 = OpVariable %23 Uniform + %25 = OpTypePointer Uniform %6 + %17 = OpConstant %16 0 + %28 = OpConstant %16 1 + %31 = OpConstant %6 1 + %34 = OpConstant %6 0 + %37 = OpConstant %6 2 + %61 = OpConstant %6 3 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %26 = OpAccessChain %25 %24 %17 %17 %17 + %27 = OpLoad %6 %26 + %29 = OpIAdd %6 %27 %28 + %30 = OpAccessChain %25 %15 %17 %17 + OpStore %30 %29 + %32 = OpAccessChain %25 %24 %17 %31 %17 + %33 = OpLoad %6 %32 + %35 = OpIAdd %6 %33 %34 + %36 = OpAccessChain %25 %15 %17 %31 + OpStore %36 %35 + %38 = OpAccessChain %25 %24 %17 %31 %31 + %39 = OpLoad %6 %38 + %40 = OpIAdd %6 %39 %37 + %41 = OpAccessChain %25 %15 %17 %10 + OpStore %41 %40 + %42 = OpAccessChain %25 %24 %17 %17 %10 + %43 = OpLoad %6 %42 + %44 = OpAccessChain %25 %15 %31 %17 + OpStore %44 %43 + %45 = OpAccessChain %25 %24 %17 %17 %31 + %46 = OpLoad %6 %45 + %47 = OpIMul %6 %46 %7 + %48 = OpAccessChain %25 %15 %31 %31 + OpStore %48 %47 + %49 = OpAccessChain %25 %24 %17 %31 %10 + %50 = OpLoad %6 %49 + %51 = OpAccessChain %25 %15 %31 %10 + OpStore %51 %50 + %52 = OpAccessChain %25 %24 %31 %17 + %53 = OpLoad %6 %52 + %54 = OpAccessChain %25 %15 %37 %17 %17 + OpStore %54 %53 + %55 = OpAccessChain %25 %24 %31 %31 + %56 = OpLoad %6 %55 + %57 = OpAccessChain %25 %15 %37 %17 %31 + OpStore %57 %56 + %58 = OpAccessChain %25 %24 %31 %37 + %59 = OpLoad %6 %58 + %60 = OpAccessChain %25 %15 %37 %31 %17 + OpStore %60 %59 + %62 = OpAccessChain %25 %24 %31 %61 + %63 = OpLoad %6 %62 + %64 = OpAccessChain %25 %15 %37 %31 %31 + OpStore %64 %63 + OpReturn + OpFunctionEnd + +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 + ; Bound: 65 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpDecorate %8 ArrayStride 4 + OpDecorate %9 ArrayStride 4 + OpDecorate %11 ArrayStride 4 + OpDecorate %12 ArrayStride 8 + OpMemberDecorate %13 0 Offset 0 + OpMemberDecorate %13 1 Offset 12 + OpMemberDecorate %13 2 Offset 24 + OpDecorate %13 BufferBlock + OpDecorate %15 DescriptorSet 0 + OpDecorate %15 Binding 1 + OpDecorate %18 ArrayStride 16 + OpDecorate %19 ArrayStride 48 + OpDecorate %21 ArrayStride 16 + OpMemberDecorate %22 0 Offset 0 + OpMemberDecorate %22 1 Offset 96 + OpDecorate %22 Block + OpDecorate %24 DescriptorSet 0 + OpDecorate %24 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpConstant %6 3 +-%8 = OpTypeArray %6 %7 ++%8 = OpTypeArray %6 %61 +-%9 = OpTypeArray %6 %7 ++%9 = OpTypeArray %6 %61 + %10 = OpConstant %6 2 +-%11 = OpTypeArray %6 %10 ++%11 = OpTypeArray %6 %37 +-%12 = OpTypeArray %11 %10 ++%12 = OpTypeArray %11 %37 + %13 = OpTypeStruct %8 %9 %12 + %14 = OpTypePointer Uniform %13 + %15 = OpVariable %14 Uniform + %16 = OpTypeInt 32 1 + %17 = OpConstant %16 0 +-%18 = OpTypeArray %6 %7 ++%18 = OpTypeArray %6 %61 +-%19 = OpTypeArray %18 %10 ++%19 = OpTypeArray %18 %37 +-%20 = OpConstant %6 4 ++%20 = OpConstant %16 4 + %21 = OpTypeArray %6 %20 + %22 = OpTypeStruct %19 %21 + %23 = OpTypePointer Uniform %22 + %24 = OpVariable %23 Uniform + %25 = OpTypePointer Uniform %6 + %28 = OpConstant %6 1 + %31 = OpConstant %16 1 + %34 = OpConstant %6 0 + %37 = OpConstant %16 2 + %61 = OpConstant %16 3 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %26 = OpAccessChain %25 %24 %17 %17 %17 + %27 = OpLoad %6 %26 +-%29 = OpIAdd %6 %27 %28 ++%29 = OpIAdd %6 %27 %31 + %30 = OpAccessChain %25 %15 %17 %17 + OpStore %30 %29 +-%32 = OpAccessChain %25 %24 %17 %31 %17 ++%32 = OpAccessChain %25 %24 %17 %28 %17 + %33 = OpLoad %6 %32 + %35 = OpIAdd %6 %33 %34 +-%36 = OpAccessChain %25 %15 %17 %31 ++%36 = OpAccessChain %25 %15 %17 %28 + OpStore %36 %35 +-%38 = OpAccessChain %25 %24 %17 %31 %31 ++%38 = OpAccessChain %25 %24 %17 %28 %28 + %39 = OpLoad %6 %38 + %40 = OpIAdd %6 %39 %10 + %41 = OpAccessChain %25 %15 %17 %37 + OpStore %41 %40 + %42 = OpAccessChain %25 %24 %17 %17 %37 + %43 = OpLoad %6 %42 +-%44 = OpAccessChain %25 %15 %31 %17 ++%44 = OpAccessChain %25 %15 %28 %17 + OpStore %44 %43 +-%45 = OpAccessChain %25 %24 %17 %17 %31 ++%45 = OpAccessChain %25 %24 %17 %17 %28 + %46 = OpLoad %6 %45 +-%47 = OpIMul %6 %46 %7 ++%47 = OpIMul %6 %46 %61 +-%48 = OpAccessChain %25 %15 %31 %31 ++%48 = OpAccessChain %25 %15 %28 %28 + OpStore %48 %47 +-%49 = OpAccessChain %25 %24 %17 %31 %37 ++%49 = OpAccessChain %25 %24 %17 %28 %37 + %50 = OpLoad %6 %49 +-%51 = OpAccessChain %25 %15 %31 %37 ++%51 = OpAccessChain %25 %15 %28 %37 + OpStore %51 %50 +-%52 = OpAccessChain %25 %24 %31 %17 ++%52 = OpAccessChain %25 %24 %28 %17 + %53 = OpLoad %6 %52 +-%54 = OpAccessChain %25 %15 %37 %17 %17 ++%54 = OpAccessChain %25 %15 %10 %17 %17 + OpStore %54 %53 +-%55 = OpAccessChain %25 %24 %31 %31 ++%55 = OpAccessChain %25 %24 %28 %28 + %56 = OpLoad %6 %55 +-%57 = OpAccessChain %25 %15 %37 %17 %31 ++%57 = OpAccessChain %25 %15 %10 %17 %28 + OpStore %57 %56 +-%58 = OpAccessChain %25 %24 %31 %37 ++%58 = OpAccessChain %25 %24 %28 %10 + %59 = OpLoad %6 %58 +-%60 = OpAccessChain %25 %15 %37 %31 %17 ++%60 = OpAccessChain %25 %15 %10 %28 %17 + OpStore %60 %59 +-%62 = OpAccessChain %25 %24 %31 %61 ++%62 = OpAccessChain %25 %24 %28 %7 + %63 = OpLoad %6 %62 +-%64 = OpAccessChain %25 %15 %37 %31 %31 ++%64 = OpAccessChain %25 %15 %10 %28 %28 + OpStore %64 %63 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/index_signedness_dst.spvasm b/test/diff/diff_files/index_signedness_dst.spvasm new file mode 100644 index 00000000..08c1939b --- /dev/null +++ b/test/diff/diff_files/index_signedness_dst.spvasm @@ -0,0 +1,110 @@ + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %13 "BufferOut" + OpMemberName %13 0 "o1" + OpMemberName %13 1 "o2" + OpMemberName %13 2 "o3" + OpName %15 "" + OpName %22 "BufferIn" + OpMemberName %22 0 "i1" + OpMemberName %22 1 "i2" + OpName %24 "" + OpDecorate %8 ArrayStride 4 + OpDecorate %9 ArrayStride 4 + OpDecorate %11 ArrayStride 4 + OpDecorate %12 ArrayStride 8 + OpMemberDecorate %13 0 Offset 0 + OpMemberDecorate %13 1 Offset 12 + OpMemberDecorate %13 2 Offset 24 + OpDecorate %13 BufferBlock + OpDecorate %15 DescriptorSet 0 + OpDecorate %15 Binding 1 + OpDecorate %18 ArrayStride 16 + OpDecorate %19 ArrayStride 48 + OpDecorate %21 ArrayStride 16 + OpMemberDecorate %22 0 Offset 0 + OpMemberDecorate %22 1 Offset 96 + OpDecorate %22 Block + OpDecorate %24 DescriptorSet 0 + OpDecorate %24 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %16 = OpTypeInt 32 1 + %7 = OpConstant %16 3 + %8 = OpTypeArray %6 %7 + %9 = OpTypeArray %6 %7 + %10 = OpConstant %16 2 + %11 = OpTypeArray %6 %10 + %12 = OpTypeArray %11 %10 + %13 = OpTypeStruct %8 %9 %12 + %14 = OpTypePointer Uniform %13 + %15 = OpVariable %14 Uniform + %18 = OpTypeArray %6 %7 + %19 = OpTypeArray %18 %10 + %20 = OpConstant %16 4 + %21 = OpTypeArray %6 %20 + %22 = OpTypeStruct %19 %21 + %23 = OpTypePointer Uniform %22 + %24 = OpVariable %23 Uniform + %25 = OpTypePointer Uniform %6 + %17 = OpConstant %16 0 + %28 = OpConstant %16 1 + %31 = OpConstant %6 1 + %34 = OpConstant %6 0 + %37 = OpConstant %6 2 + %61 = OpConstant %6 3 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %26 = OpAccessChain %25 %24 %17 %17 %17 + %27 = OpLoad %6 %26 + %29 = OpIAdd %6 %27 %28 + %30 = OpAccessChain %25 %15 %17 %17 + OpStore %30 %29 + %32 = OpAccessChain %25 %24 %17 %31 %17 + %33 = OpLoad %6 %32 + %35 = OpIAdd %6 %33 %34 + %36 = OpAccessChain %25 %15 %17 %31 + OpStore %36 %35 + %38 = OpAccessChain %25 %24 %17 %31 %31 + %39 = OpLoad %6 %38 + %40 = OpIAdd %6 %39 %37 + %41 = OpAccessChain %25 %15 %17 %10 + OpStore %41 %40 + %42 = OpAccessChain %25 %24 %17 %17 %10 + %43 = OpLoad %6 %42 + %44 = OpAccessChain %25 %15 %31 %17 + OpStore %44 %43 + %45 = OpAccessChain %25 %24 %17 %17 %31 + %46 = OpLoad %6 %45 + %47 = OpIMul %6 %46 %7 + %48 = OpAccessChain %25 %15 %31 %31 + OpStore %48 %47 + %49 = OpAccessChain %25 %24 %17 %31 %10 + %50 = OpLoad %6 %49 + %51 = OpAccessChain %25 %15 %31 %10 + OpStore %51 %50 + %52 = OpAccessChain %25 %24 %31 %17 + %53 = OpLoad %6 %52 + %54 = OpAccessChain %25 %15 %37 %17 %17 + OpStore %54 %53 + %55 = OpAccessChain %25 %24 %31 %31 + %56 = OpLoad %6 %55 + %57 = OpAccessChain %25 %15 %37 %17 %31 + OpStore %57 %56 + %58 = OpAccessChain %25 %24 %31 %37 + %59 = OpLoad %6 %58 + %60 = OpAccessChain %25 %15 %37 %31 %17 + OpStore %60 %59 + %62 = OpAccessChain %25 %24 %31 %61 + %63 = OpLoad %6 %62 + %64 = OpAccessChain %25 %15 %37 %31 %31 + OpStore %64 %63 + OpReturn + OpFunctionEnd + diff --git a/test/diff/diff_files/index_signedness_src.spvasm b/test/diff/diff_files/index_signedness_src.spvasm new file mode 100644 index 00000000..06dfd18b --- /dev/null +++ b/test/diff/diff_files/index_signedness_src.spvasm @@ -0,0 +1,111 @@ +;; Test where signedness of indices are different between src and dst. + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %13 "BufferOut" + OpMemberName %13 0 "o1" + OpMemberName %13 1 "o2" + OpMemberName %13 2 "o3" + OpName %15 "" + OpName %22 "BufferIn" + OpMemberName %22 0 "i1" + OpMemberName %22 1 "i2" + OpName %24 "" + OpDecorate %8 ArrayStride 4 + OpDecorate %9 ArrayStride 4 + OpDecorate %11 ArrayStride 4 + OpDecorate %12 ArrayStride 8 + OpMemberDecorate %13 0 Offset 0 + OpMemberDecorate %13 1 Offset 12 + OpMemberDecorate %13 2 Offset 24 + OpDecorate %13 BufferBlock + OpDecorate %15 DescriptorSet 0 + OpDecorate %15 Binding 1 + OpDecorate %18 ArrayStride 16 + OpDecorate %19 ArrayStride 48 + OpDecorate %21 ArrayStride 16 + OpMemberDecorate %22 0 Offset 0 + OpMemberDecorate %22 1 Offset 96 + OpDecorate %22 Block + OpDecorate %24 DescriptorSet 0 + OpDecorate %24 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpConstant %6 3 + %8 = OpTypeArray %6 %7 + %9 = OpTypeArray %6 %7 + %10 = OpConstant %6 2 + %11 = OpTypeArray %6 %10 + %12 = OpTypeArray %11 %10 + %13 = OpTypeStruct %8 %9 %12 + %14 = OpTypePointer Uniform %13 + %15 = OpVariable %14 Uniform + %16 = OpTypeInt 32 1 + %17 = OpConstant %16 0 + %18 = OpTypeArray %6 %7 + %19 = OpTypeArray %18 %10 + %20 = OpConstant %6 4 + %21 = OpTypeArray %6 %20 + %22 = OpTypeStruct %19 %21 + %23 = OpTypePointer Uniform %22 + %24 = OpVariable %23 Uniform + %25 = OpTypePointer Uniform %6 + %28 = OpConstant %6 1 + %31 = OpConstant %16 1 + %34 = OpConstant %6 0 + %37 = OpConstant %16 2 + %61 = OpConstant %16 3 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %26 = OpAccessChain %25 %24 %17 %17 %17 + %27 = OpLoad %6 %26 + %29 = OpIAdd %6 %27 %28 + %30 = OpAccessChain %25 %15 %17 %17 + OpStore %30 %29 + %32 = OpAccessChain %25 %24 %17 %31 %17 + %33 = OpLoad %6 %32 + %35 = OpIAdd %6 %33 %34 + %36 = OpAccessChain %25 %15 %17 %31 + OpStore %36 %35 + %38 = OpAccessChain %25 %24 %17 %31 %31 + %39 = OpLoad %6 %38 + %40 = OpIAdd %6 %39 %10 + %41 = OpAccessChain %25 %15 %17 %37 + OpStore %41 %40 + %42 = OpAccessChain %25 %24 %17 %17 %37 + %43 = OpLoad %6 %42 + %44 = OpAccessChain %25 %15 %31 %17 + OpStore %44 %43 + %45 = OpAccessChain %25 %24 %17 %17 %31 + %46 = OpLoad %6 %45 + %47 = OpIMul %6 %46 %7 + %48 = OpAccessChain %25 %15 %31 %31 + OpStore %48 %47 + %49 = OpAccessChain %25 %24 %17 %31 %37 + %50 = OpLoad %6 %49 + %51 = OpAccessChain %25 %15 %31 %37 + OpStore %51 %50 + %52 = OpAccessChain %25 %24 %31 %17 + %53 = OpLoad %6 %52 + %54 = OpAccessChain %25 %15 %37 %17 %17 + OpStore %54 %53 + %55 = OpAccessChain %25 %24 %31 %31 + %56 = OpLoad %6 %55 + %57 = OpAccessChain %25 %15 %37 %17 %31 + OpStore %57 %56 + %58 = OpAccessChain %25 %24 %31 %37 + %59 = OpLoad %6 %58 + %60 = OpAccessChain %25 %15 %37 %31 %17 + OpStore %60 %59 + %62 = OpAccessChain %25 %24 %31 %61 + %63 = OpLoad %6 %62 + %64 = OpAccessChain %25 %15 %37 %31 %31 + OpStore %64 %63 + OpReturn + OpFunctionEnd + diff --git a/test/diff/diff_files/int_vs_uint_constants_autogen.cpp b/test/diff/diff_files/int_vs_uint_constants_autogen.cpp new file mode 100644 index 00000000..187722e8 --- /dev/null +++ b/test/diff/diff_files/int_vs_uint_constants_autogen.cpp @@ -0,0 +1,396 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Tests that identical integer constants are matched, regardless of int or +// uint. This helps compare output from different generators that default to +// int or uint for constants such as those passed to OpAccessChain. +constexpr char kSrc[] = R"(; SPIR-V +; Version: 1.0 +; Generator: Google ANGLE Shader Compiler; 0 +; Bound: 27 +; Schema: 0 +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %22 "main" %4 %19 +OpSource GLSL 450 +OpName %4 "_ua_position" +OpName %17 "gl_PerVertex" +OpMemberName %17 0 "gl_Position" +OpMemberName %17 1 "gl_PointSize" +OpMemberName %17 2 "gl_ClipDistance" +OpMemberName %17 3 "gl_CullDistance" +OpName %19 "" +OpName %22 "main" +OpDecorate %4 Location 0 +OpMemberDecorate %17 1 RelaxedPrecision +OpMemberDecorate %17 0 BuiltIn Position +OpMemberDecorate %17 1 BuiltIn PointSize +OpMemberDecorate %17 2 BuiltIn ClipDistance +OpMemberDecorate %17 3 BuiltIn CullDistance +OpDecorate %17 Block +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeInt 32 0 +%8 = OpTypeVector %5 4 +%15 = OpConstant %5 8 +%16 = OpTypeArray %1 %15 +%17 = OpTypeStruct %2 %1 %16 %16 +%20 = OpTypeVoid +%25 = OpConstant %5 0 +%3 = OpTypePointer Input %2 +%13 = OpTypePointer Output %2 +%18 = OpTypePointer Output %17 +%21 = OpTypeFunction %20 +%4 = OpVariable %3 Input +%19 = OpVariable %18 Output +%22 = OpFunction %20 None %21 +%23 = OpLabel +%24 = OpLoad %2 %4 +%26 = OpAccessChain %13 %19 %25 +OpStore %26 %24 +OpReturn +OpFunctionEnd)"; +constexpr char kDst[] = R"(; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 28 +; Schema: 0 +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %4 "main" %13 %17 +OpSource GLSL 450 +OpName %4 "main" +OpName %11 "gl_PerVertex" +OpMemberName %11 0 "gl_Position" +OpMemberName %11 1 "gl_PointSize" +OpMemberName %11 2 "gl_ClipDistance" +OpMemberName %11 3 "gl_CullDistance" +OpName %13 "" +OpName %17 "_ua_position" +OpMemberDecorate %11 0 BuiltIn Position +OpMemberDecorate %11 1 BuiltIn PointSize +OpMemberDecorate %11 2 BuiltIn ClipDistance +OpMemberDecorate %11 3 BuiltIn CullDistance +OpDecorate %11 Block +OpDecorate %17 Location 0 +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%6 = OpTypeFloat 32 +%7 = OpTypeVector %6 4 +%8 = OpTypeInt 32 0 +%9 = OpConstant %8 1 +%10 = OpTypeArray %6 %9 +%11 = OpTypeStruct %7 %6 %10 %10 +%12 = OpTypePointer Output %11 +%13 = OpVariable %12 Output +%14 = OpTypeInt 32 1 +%15 = OpConstant %14 0 +%16 = OpTypePointer Input %7 +%17 = OpVariable %16 Input +%19 = OpTypePointer Output %7 +%4 = OpFunction %2 None %3 +%5 = OpLabel +%18 = OpLoad %7 %17 +%20 = OpAccessChain %19 %13 %15 +OpStore %20 %18 +OpReturn +OpFunctionEnd +)"; + +TEST(DiffTest, IntVsUintConstants) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 27 ++; Bound: 31 + ; Schema: 0 + OpCapability Shader ++%27 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 +-OpEntryPoint Vertex %22 "main" %4 %19 ++OpEntryPoint Vertex %22 "main" %19 %4 + OpSource GLSL 450 + OpName %4 "_ua_position" + OpName %17 "gl_PerVertex" + OpMemberName %17 0 "gl_Position" + OpMemberName %17 1 "gl_PointSize" + OpMemberName %17 2 "gl_ClipDistance" + OpMemberName %17 3 "gl_CullDistance" + OpName %19 "" + OpName %22 "main" + OpDecorate %4 Location 0 +-OpMemberDecorate %17 1 RelaxedPrecision + OpMemberDecorate %17 0 BuiltIn Position + OpMemberDecorate %17 1 BuiltIn PointSize + OpMemberDecorate %17 2 BuiltIn ClipDistance + OpMemberDecorate %17 3 BuiltIn CullDistance + OpDecorate %17 Block + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 + %5 = OpTypeInt 32 0 +-%8 = OpTypeVector %5 4 +-%15 = OpConstant %5 8 +-%16 = OpTypeArray %1 %15 +-%17 = OpTypeStruct %2 %1 %16 %16 ++%17 = OpTypeStruct %2 %1 %29 %29 + %20 = OpTypeVoid ++%28 = OpConstant %5 1 ++%29 = OpTypeArray %1 %28 +-%25 = OpConstant %5 0 ++%25 = OpConstant %30 0 + %3 = OpTypePointer Input %2 + %13 = OpTypePointer Output %2 ++%30 = OpTypeInt 32 1 + %18 = OpTypePointer Output %17 + %21 = OpTypeFunction %20 + %4 = OpVariable %3 Input + %19 = OpVariable %18 Output + %22 = OpFunction %20 None %21 + %23 = OpLabel + %24 = OpLoad %2 %4 + %26 = OpAccessChain %13 %19 %25 + OpStore %26 %24 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, IntVsUintConstantsNoDebug) { + constexpr char kSrcNoDebug[] = R"(; SPIR-V +; Version: 1.0 +; Generator: Google ANGLE Shader Compiler; 0 +; Bound: 27 +; Schema: 0 +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %22 "main" %4 %19 +OpSource GLSL 450 +OpDecorate %4 Location 0 +OpMemberDecorate %17 1 RelaxedPrecision +OpMemberDecorate %17 0 BuiltIn Position +OpMemberDecorate %17 1 BuiltIn PointSize +OpMemberDecorate %17 2 BuiltIn ClipDistance +OpMemberDecorate %17 3 BuiltIn CullDistance +OpDecorate %17 Block +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeInt 32 0 +%8 = OpTypeVector %5 4 +%15 = OpConstant %5 8 +%16 = OpTypeArray %1 %15 +%17 = OpTypeStruct %2 %1 %16 %16 +%20 = OpTypeVoid +%25 = OpConstant %5 0 +%3 = OpTypePointer Input %2 +%13 = OpTypePointer Output %2 +%18 = OpTypePointer Output %17 +%21 = OpTypeFunction %20 +%4 = OpVariable %3 Input +%19 = OpVariable %18 Output +%22 = OpFunction %20 None %21 +%23 = OpLabel +%24 = OpLoad %2 %4 +%26 = OpAccessChain %13 %19 %25 +OpStore %26 %24 +OpReturn +OpFunctionEnd +)"; + constexpr char kDstNoDebug[] = R"(; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 28 +; Schema: 0 +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %4 "main" %13 %17 +OpSource GLSL 450 +OpMemberDecorate %11 0 BuiltIn Position +OpMemberDecorate %11 1 BuiltIn PointSize +OpMemberDecorate %11 2 BuiltIn ClipDistance +OpMemberDecorate %11 3 BuiltIn CullDistance +OpDecorate %11 Block +OpDecorate %17 Location 0 +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%6 = OpTypeFloat 32 +%7 = OpTypeVector %6 4 +%8 = OpTypeInt 32 0 +%9 = OpConstant %8 1 +%10 = OpTypeArray %6 %9 +%11 = OpTypeStruct %7 %6 %10 %10 +%12 = OpTypePointer Output %11 +%13 = OpVariable %12 Output +%14 = OpTypeInt 32 1 +%15 = OpConstant %14 0 +%16 = OpTypePointer Input %7 +%17 = OpVariable %16 Input +%19 = OpTypePointer Output %7 +%4 = OpFunction %2 None %3 +%5 = OpLabel +%18 = OpLoad %7 %17 +%20 = OpAccessChain %19 %13 %15 +OpStore %20 %18 +OpReturn +OpFunctionEnd +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 27 ++; Bound: 31 + ; Schema: 0 + OpCapability Shader ++%27 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 +-OpEntryPoint Vertex %22 "main" %4 %19 ++OpEntryPoint Vertex %22 "main" %19 %4 + OpSource GLSL 450 + OpDecorate %4 Location 0 +-OpMemberDecorate %17 1 RelaxedPrecision + OpMemberDecorate %17 0 BuiltIn Position + OpMemberDecorate %17 1 BuiltIn PointSize + OpMemberDecorate %17 2 BuiltIn ClipDistance + OpMemberDecorate %17 3 BuiltIn CullDistance + OpDecorate %17 Block + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 + %5 = OpTypeInt 32 0 +-%8 = OpTypeVector %5 4 +-%15 = OpConstant %5 8 +-%16 = OpTypeArray %1 %15 +-%17 = OpTypeStruct %2 %1 %16 %16 ++%17 = OpTypeStruct %2 %1 %29 %29 + %20 = OpTypeVoid ++%28 = OpConstant %5 1 ++%29 = OpTypeArray %1 %28 +-%25 = OpConstant %5 0 ++%25 = OpConstant %30 0 + %3 = OpTypePointer Input %2 + %13 = OpTypePointer Output %2 ++%30 = OpTypeInt 32 1 + %18 = OpTypePointer Output %17 + %21 = OpTypeFunction %20 + %4 = OpVariable %3 Input + %19 = OpVariable %18 Output + %22 = OpFunction %20 None %21 + %23 = OpLabel + %24 = OpLoad %2 %4 + %26 = OpAccessChain %13 %19 %25 + OpStore %26 %24 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +TEST(DiffTest, IntVsUintConstantsDumpIds) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 27 ++; Bound: 31 + ; Schema: 0 + OpCapability Shader ++%27 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 +-OpEntryPoint Vertex %22 "main" %4 %19 ++OpEntryPoint Vertex %22 "main" %19 %4 + OpSource GLSL 450 + OpName %4 "_ua_position" + OpName %17 "gl_PerVertex" + OpMemberName %17 0 "gl_Position" + OpMemberName %17 1 "gl_PointSize" + OpMemberName %17 2 "gl_ClipDistance" + OpMemberName %17 3 "gl_CullDistance" + OpName %19 "" + OpName %22 "main" + OpDecorate %4 Location 0 +-OpMemberDecorate %17 1 RelaxedPrecision + OpMemberDecorate %17 0 BuiltIn Position + OpMemberDecorate %17 1 BuiltIn PointSize + OpMemberDecorate %17 2 BuiltIn ClipDistance + OpMemberDecorate %17 3 BuiltIn CullDistance + OpDecorate %17 Block + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 + %5 = OpTypeInt 32 0 +-%8 = OpTypeVector %5 4 +-%15 = OpConstant %5 8 +-%16 = OpTypeArray %1 %15 +-%17 = OpTypeStruct %2 %1 %16 %16 ++%17 = OpTypeStruct %2 %1 %29 %29 + %20 = OpTypeVoid ++%28 = OpConstant %5 1 ++%29 = OpTypeArray %1 %28 +-%25 = OpConstant %5 0 ++%25 = OpConstant %30 0 + %3 = OpTypePointer Input %2 + %13 = OpTypePointer Output %2 ++%30 = OpTypeInt 32 1 + %18 = OpTypePointer Output %17 + %21 = OpTypeFunction %20 + %4 = OpVariable %3 Input + %19 = OpVariable %18 Output + %22 = OpFunction %20 None %21 + %23 = OpLabel + %24 = OpLoad %2 %4 + %26 = OpAccessChain %13 %19 %25 + OpStore %26 %24 + OpReturn + OpFunctionEnd + Src -> Dst + 1 -> 6 [TypeFloat] + 2 -> 7 [TypeVector] + 3 -> 16 [TypePointer] + 4 -> 17 [Variable] + 5 -> 8 [TypeInt] + 8 -> 23 [TypeVector] + 13 -> 19 [TypePointer] + 15 -> 29 [Constant] + 16 -> 30 [TypeArray] + 17 -> 11 [TypeStruct] + 18 -> 12 [TypePointer] + 19 -> 13 [Variable] + 20 -> 2 [TypeVoid] + 21 -> 3 [TypeFunction] + 22 -> 4 [Function] + 23 -> 5 [Label] + 24 -> 18 [Load] + 25 -> 15 [Constant] + 26 -> 20 [AccessChain] +)"; + Options options; + options.dump_id_map = true; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/int_vs_uint_constants_dst.spvasm b/test/diff/diff_files/int_vs_uint_constants_dst.spvasm new file mode 100644 index 00000000..da2618b2 --- /dev/null +++ b/test/diff/diff_files/int_vs_uint_constants_dst.spvasm @@ -0,0 +1,46 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 28 +; Schema: 0 +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %4 "main" %13 %17 +OpSource GLSL 450 +OpName %4 "main" +OpName %11 "gl_PerVertex" +OpMemberName %11 0 "gl_Position" +OpMemberName %11 1 "gl_PointSize" +OpMemberName %11 2 "gl_ClipDistance" +OpMemberName %11 3 "gl_CullDistance" +OpName %13 "" +OpName %17 "_ua_position" +OpMemberDecorate %11 0 BuiltIn Position +OpMemberDecorate %11 1 BuiltIn PointSize +OpMemberDecorate %11 2 BuiltIn ClipDistance +OpMemberDecorate %11 3 BuiltIn CullDistance +OpDecorate %11 Block +OpDecorate %17 Location 0 +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%6 = OpTypeFloat 32 +%7 = OpTypeVector %6 4 +%8 = OpTypeInt 32 0 +%9 = OpConstant %8 1 +%10 = OpTypeArray %6 %9 +%11 = OpTypeStruct %7 %6 %10 %10 +%12 = OpTypePointer Output %11 +%13 = OpVariable %12 Output +%14 = OpTypeInt 32 1 +%15 = OpConstant %14 0 +%16 = OpTypePointer Input %7 +%17 = OpVariable %16 Input +%19 = OpTypePointer Output %7 +%4 = OpFunction %2 None %3 +%5 = OpLabel +%18 = OpLoad %7 %17 +%20 = OpAccessChain %19 %13 %15 +OpStore %20 %18 +OpReturn +OpFunctionEnd diff --git a/test/diff/diff_files/int_vs_uint_constants_src.spvasm b/test/diff/diff_files/int_vs_uint_constants_src.spvasm new file mode 100644 index 00000000..214b49bd --- /dev/null +++ b/test/diff/diff_files/int_vs_uint_constants_src.spvasm @@ -0,0 +1,49 @@ +;; Tests that identical integer constants are matched, regardless of int or +;; uint. This helps compare output from different generators that default to +;; int or uint for constants such as those passed to OpAccessChain. +; SPIR-V +; Version: 1.0 +; Generator: Google ANGLE Shader Compiler; 0 +; Bound: 27 +; Schema: 0 +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %22 "main" %4 %19 +OpSource GLSL 450 +OpName %4 "_ua_position" +OpName %17 "gl_PerVertex" +OpMemberName %17 0 "gl_Position" +OpMemberName %17 1 "gl_PointSize" +OpMemberName %17 2 "gl_ClipDistance" +OpMemberName %17 3 "gl_CullDistance" +OpName %19 "" +OpName %22 "main" +OpDecorate %4 Location 0 +OpMemberDecorate %17 1 RelaxedPrecision +OpMemberDecorate %17 0 BuiltIn Position +OpMemberDecorate %17 1 BuiltIn PointSize +OpMemberDecorate %17 2 BuiltIn ClipDistance +OpMemberDecorate %17 3 BuiltIn CullDistance +OpDecorate %17 Block +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeInt 32 0 +%8 = OpTypeVector %5 4 +%15 = OpConstant %5 8 +%16 = OpTypeArray %1 %15 +%17 = OpTypeStruct %2 %1 %16 %16 +%20 = OpTypeVoid +%25 = OpConstant %5 0 +%3 = OpTypePointer Input %2 +%13 = OpTypePointer Output %2 +%18 = OpTypePointer Output %17 +%21 = OpTypeFunction %20 +%4 = OpVariable %3 Input +%19 = OpVariable %18 Output +%22 = OpFunction %20 None %21 +%23 = OpLabel +%24 = OpLoad %2 %4 +%26 = OpAccessChain %13 %19 %25 +OpStore %26 %24 +OpReturn +OpFunctionEnd diff --git a/test/diff/diff_files/large_functions_large_diffs_autogen.cpp b/test/diff/diff_files/large_functions_large_diffs_autogen.cpp new file mode 100644 index 00000000..12cb6219 --- /dev/null +++ b/test/diff/diff_files/large_functions_large_diffs_autogen.cpp @@ -0,0 +1,1534 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Test where src and dst have a few large functions with large differences. +constexpr char kSrc[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" %15 + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "f1(" + OpName %8 "f2(" + OpName %12 "x" + OpName %15 "gl_LocalInvocationID" + OpName %20 "y" + OpName %27 "image" + OpName %44 "sum" + OpName %46 "i" + OpName %56 "j" + OpName %80 "BufferOut" + OpMemberName %80 0 "o_uv4" + OpMemberName %80 1 "o_v3" + OpMemberName %80 2 "o_i" + OpName %82 "" + OpName %88 "BufferIn" + OpMemberName %88 0 "i_u" + OpMemberName %88 1 "i_v4" + OpMemberName %88 2 "i_f" + OpName %90 "" + OpName %101 "i" + OpName %128 "image2" + OpDecorate %15 BuiltIn LocalInvocationId + OpDecorate %27 DescriptorSet 0 + OpDecorate %27 Binding 2 + OpMemberDecorate %80 0 Offset 0 + OpMemberDecorate %80 1 Offset 16 + OpMemberDecorate %80 2 Offset 28 + OpDecorate %80 BufferBlock + OpDecorate %82 DescriptorSet 0 + OpDecorate %82 Binding 1 + OpMemberDecorate %88 0 Offset 0 + OpMemberDecorate %88 1 RowMajor + OpMemberDecorate %88 1 Offset 16 + OpMemberDecorate %88 1 MatrixStride 16 + OpMemberDecorate %88 2 Offset 80 + OpDecorate %88 Block + OpDecorate %90 DescriptorSet 0 + OpDecorate %90 Binding 0 + OpDecorate %128 DescriptorSet 0 + OpDecorate %128 Binding 3 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 0 + %11 = OpTypePointer Function %10 + %13 = OpTypeVector %10 3 + %14 = OpTypePointer Input %13 + %15 = OpVariable %14 Input + %16 = OpConstant %10 0 + %17 = OpTypePointer Input %10 + %21 = OpConstant %10 1 + %24 = OpTypeInt 32 1 + %25 = OpTypeImage %24 2D 0 0 0 2 R32i + %26 = OpTypePointer UniformConstant %25 + %27 = OpVariable %26 UniformConstant + %29 = OpTypeVector %10 2 + %32 = OpTypeVector %24 2 + %38 = OpTypeVector %24 4 + %40 = OpConstant %10 2 + %41 = OpConstant %10 3400 + %42 = OpConstant %10 264 + %43 = OpTypePointer Function %24 + %45 = OpConstant %24 0 + %53 = OpConstant %24 2 + %54 = OpTypeBool + %73 = OpConstant %24 1 + %77 = OpTypeVector %10 4 + %78 = OpTypeFloat 32 + %79 = OpTypeVector %78 3 + %80 = OpTypeStruct %77 %79 %24 + %81 = OpTypePointer Uniform %80 + %82 = OpVariable %81 Uniform + %84 = OpTypePointer Uniform %24 + %86 = OpTypeVector %78 4 + %87 = OpTypeMatrix %86 4 + %88 = OpTypeStruct %10 %87 %78 + %89 = OpTypePointer Uniform %88 + %90 = OpVariable %89 Uniform + %91 = OpTypePointer Uniform %87 + %94 = OpTypePointer Uniform %77 + %108 = OpConstant %24 3 + %110 = OpTypePointer Uniform %79 + %113 = OpTypePointer Uniform %78 + %128 = OpVariable %26 UniformConstant + %130 = OpConstantComposite %32 %45 %45 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %136 = OpFunctionCall %2 %6 + %137 = OpFunctionCall %2 %8 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %12 = OpVariable %11 Function + %20 = OpVariable %11 Function + %44 = OpVariable %43 Function + %46 = OpVariable %43 Function + %56 = OpVariable %43 Function + %18 = OpAccessChain %17 %15 %16 + %19 = OpLoad %10 %18 + OpStore %12 %19 + %22 = OpAccessChain %17 %15 %21 + %23 = OpLoad %10 %22 + OpStore %20 %23 + %28 = OpLoad %25 %27 + %30 = OpLoad %13 %15 + %31 = OpVectorShuffle %29 %30 %30 0 1 + %33 = OpBitcast %32 %31 + %34 = OpLoad %10 %12 + %35 = OpLoad %10 %20 + %36 = OpIAdd %10 %34 %35 + %37 = OpBitcast %24 %36 + %39 = OpCompositeConstruct %38 %37 %37 %37 %37 + OpImageWrite %28 %33 %39 + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + OpStore %44 %45 + OpStore %46 %45 + OpBranch %47 + %47 = OpLabel + OpLoopMerge %49 %50 None + OpBranch %51 + %51 = OpLabel + %52 = OpLoad %24 %46 + %55 = OpSLessThan %54 %52 %53 + OpBranchConditional %55 %48 %49 + %48 = OpLabel + OpStore %56 %45 + OpBranch %57 + %57 = OpLabel + OpLoopMerge %59 %60 None + OpBranch %61 + %61 = OpLabel + %62 = OpLoad %24 %56 + %63 = OpSLessThan %54 %62 %53 + OpBranchConditional %63 %58 %59 + %58 = OpLabel + %64 = OpLoad %25 %27 + %65 = OpLoad %24 %46 + %66 = OpLoad %24 %56 + %67 = OpCompositeConstruct %32 %65 %66 + %68 = OpImageRead %38 %64 %67 + %69 = OpCompositeExtract %24 %68 0 + %70 = OpLoad %24 %44 + %71 = OpIMul %24 %70 %69 + OpStore %44 %71 + OpBranch %60 + %60 = OpLabel + %72 = OpLoad %24 %56 + %74 = OpIAdd %24 %72 %73 + OpStore %56 %74 + OpBranch %57 + %59 = OpLabel + OpBranch %50 + %50 = OpLabel + %75 = OpLoad %24 %46 + %76 = OpIAdd %24 %75 %73 + OpStore %46 %76 + OpBranch %47 + %49 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + %83 = OpLoad %24 %44 + %85 = OpAccessChain %84 %82 %53 + OpStore %85 %83 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %101 = OpVariable %43 Function + %92 = OpAccessChain %91 %90 %73 + %93 = OpLoad %87 %92 + %95 = OpAccessChain %94 %82 %45 + %96 = OpLoad %77 %95 + %97 = OpConvertUToF %86 %96 + %98 = OpMatrixTimesVector %86 %93 %97 + %99 = OpConvertFToU %77 %98 + %100 = OpAccessChain %94 %82 %45 + OpStore %100 %99 + OpStore %101 %45 + OpBranch %102 + %102 = OpLabel + OpLoopMerge %104 %105 None + OpBranch %106 + %106 = OpLabel + %107 = OpLoad %24 %101 + %109 = OpSLessThan %54 %107 %108 + OpBranchConditional %109 %103 %104 + %103 = OpLabel + %111 = OpAccessChain %110 %82 %73 + %112 = OpLoad %79 %111 + %114 = OpAccessChain %113 %90 %53 + %115 = OpLoad %78 %114 + %116 = OpVectorTimesScalar %79 %112 %115 + %117 = OpConvertFToU %13 %116 + %118 = OpCompositeExtract %10 %117 0 + %119 = OpCompositeExtract %10 %117 1 + %120 = OpCompositeExtract %10 %117 2 + %121 = OpCompositeConstruct %77 %118 %119 %120 %16 + %122 = OpAccessChain %94 %82 %45 + %123 = OpLoad %77 %122 + %124 = OpIAdd %77 %123 %121 + %125 = OpAccessChain %94 %82 %45 + OpStore %125 %124 + OpBranch %105 + %105 = OpLabel + %126 = OpLoad %24 %101 + %127 = OpIAdd %24 %126 %73 + OpStore %101 %127 + OpBranch %102 + %104 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + %129 = OpLoad %25 %128 + %131 = OpImageRead %38 %129 %130 + %132 = OpCompositeExtract %24 %131 0 + %133 = OpConvertSToF %78 %132 + %134 = OpCompositeConstruct %79 %133 %133 %133 + %135 = OpAccessChain %110 %82 %73 + OpStore %135 %134 + OpReturn + OpFunctionEnd +)"; +constexpr char kDst[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" %15 %110 + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "f1(" + OpName %8 "f2(" + OpName %12 "x" + OpName %15 "gl_GlobalInvocationID" + OpName %20 "z" + OpName %26 "i" + OpName %40 "BufferOut" + OpMemberName %40 0 "o_uv4" + OpMemberName %40 1 "o_v3" + OpMemberName %40 2 "o_i" + OpName %42 "" + OpName %63 "image2" + OpName %79 "image" + OpName %89 "i" + OpName %110 "gl_LocalInvocationID" + OpName %127 "BufferIn" + OpMemberName %127 0 "i_u" + OpMemberName %127 1 "i_v4" + OpMemberName %127 2 "i_f" + OpName %129 "" + OpDecorate %15 BuiltIn GlobalInvocationId + OpMemberDecorate %40 0 Offset 0 + OpMemberDecorate %40 1 Offset 16 + OpMemberDecorate %40 2 Offset 28 + OpDecorate %40 BufferBlock + OpDecorate %42 DescriptorSet 0 + OpDecorate %42 Binding 1 + OpDecorate %63 DescriptorSet 0 + OpDecorate %63 Binding 3 + OpDecorate %79 DescriptorSet 0 + OpDecorate %79 Binding 2 + OpDecorate %110 BuiltIn LocalInvocationId + OpMemberDecorate %127 0 Offset 0 + OpMemberDecorate %127 1 RowMajor + OpMemberDecorate %127 1 Offset 16 + OpMemberDecorate %127 1 MatrixStride 16 + OpMemberDecorate %127 2 Offset 80 + OpDecorate %127 Block + OpDecorate %129 DescriptorSet 0 + OpDecorate %129 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 0 + %11 = OpTypePointer Function %10 + %13 = OpTypeVector %10 3 + %14 = OpTypePointer Input %13 + %15 = OpVariable %14 Input + %16 = OpConstant %10 0 + %17 = OpTypePointer Input %10 + %21 = OpConstant %10 1 + %24 = OpTypeInt 32 1 + %25 = OpTypePointer Function %24 + %27 = OpConstant %24 0 + %34 = OpConstant %24 2 + %35 = OpTypeBool + %37 = OpTypeVector %10 4 + %38 = OpTypeFloat 32 + %39 = OpTypeVector %38 3 + %40 = OpTypeStruct %37 %39 %24 + %41 = OpTypePointer Uniform %40 + %42 = OpVariable %41 Uniform + %46 = OpTypeVector %10 2 + %48 = OpTypePointer Uniform %37 + %53 = OpTypePointer Uniform %10 + %59 = OpConstant %24 1 + %61 = OpTypeImage %24 2D 0 0 0 2 R32i + %62 = OpTypePointer UniformConstant %61 + %63 = OpVariable %62 UniformConstant + %69 = OpTypeVector %24 2 + %71 = OpTypeVector %24 4 + %74 = OpTypePointer Uniform %24 + %76 = OpConstant %10 2 + %77 = OpConstant %10 3400 + %78 = OpConstant %10 264 + %79 = OpVariable %62 UniformConstant + %96 = OpConstant %24 3 + %103 = OpConstantComposite %69 %27 %27 + %107 = OpTypePointer Uniform %38 + %110 = OpVariable %14 Input + %113 = OpTypeVector %38 2 + %125 = OpTypeVector %38 4 + %126 = OpTypeMatrix %125 4 + %127 = OpTypeStruct %10 %126 %38 + %128 = OpTypePointer Uniform %127 + %129 = OpVariable %128 Uniform + %4 = OpFunction %2 None %3 + %5 = OpLabel + %123 = OpFunctionCall %2 %8 + %124 = OpFunctionCall %2 %6 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %12 = OpVariable %11 Function + %20 = OpVariable %11 Function + %26 = OpVariable %25 Function + %18 = OpAccessChain %17 %15 %16 + %19 = OpLoad %10 %18 + OpStore %12 %19 + %22 = OpAccessChain %17 %15 %21 + %23 = OpLoad %10 %22 + OpStore %20 %23 + OpStore %26 %27 + OpBranch %28 + %28 = OpLabel + OpLoopMerge %30 %31 None + OpBranch %32 + %32 = OpLabel + %33 = OpLoad %24 %26 + %36 = OpSLessThan %35 %33 %34 + OpBranchConditional %36 %29 %30 + %29 = OpLabel + %43 = OpLoad %10 %12 + %44 = OpLoad %10 %20 + %45 = OpIAdd %10 %43 %44 + %47 = OpCompositeConstruct %46 %45 %45 + %49 = OpAccessChain %48 %42 %27 + %50 = OpLoad %37 %49 + %51 = OpVectorShuffle %46 %50 %50 0 1 + %52 = OpIAdd %46 %51 %47 + %54 = OpAccessChain %53 %42 %27 %16 + %55 = OpCompositeExtract %10 %52 0 + OpStore %54 %55 + %56 = OpAccessChain %53 %42 %27 %21 + %57 = OpCompositeExtract %10 %52 1 + OpStore %56 %57 + OpBranch %31 + %31 = OpLabel + %58 = OpLoad %24 %26 + %60 = OpIAdd %24 %58 %59 + OpStore %26 %60 + OpBranch %28 + %30 = OpLabel + %64 = OpLoad %61 %63 + %65 = OpLoad %10 %12 + %66 = OpBitcast %24 %65 + %67 = OpLoad %10 %20 + %68 = OpBitcast %24 %67 + %70 = OpCompositeConstruct %69 %66 %68 + %72 = OpImageRead %71 %64 %70 + %73 = OpCompositeExtract %24 %72 1 + %75 = OpAccessChain %74 %42 %34 + OpStore %75 %73 + OpMemoryBarrier %76 %77 + OpControlBarrier %76 %76 %78 + %80 = OpLoad %61 %79 + %81 = OpLoad %10 %20 + %82 = OpBitcast %24 %81 + %83 = OpLoad %10 %12 + %84 = OpBitcast %24 %83 + %85 = OpCompositeConstruct %69 %82 %84 + %86 = OpAccessChain %74 %42 %34 + %87 = OpLoad %24 %86 + %88 = OpCompositeConstruct %71 %87 %27 %27 %27 + OpImageWrite %80 %85 %88 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %89 = OpVariable %25 Function + OpStore %89 %27 + OpBranch %90 + %90 = OpLabel + OpLoopMerge %92 %93 None + OpBranch %94 + %94 = OpLabel + %95 = OpLoad %24 %89 + %97 = OpSLessThan %35 %95 %96 + OpBranchConditional %97 %91 %92 + %91 = OpLabel + %98 = OpLoad %24 %89 + %99 = OpIEqual %35 %98 %27 + OpSelectionMerge %101 None + OpBranchConditional %99 %100 %109 + %100 = OpLabel + %102 = OpLoad %61 %63 + %104 = OpImageRead %71 %102 %103 + %105 = OpCompositeExtract %24 %104 0 + %106 = OpConvertSToF %38 %105 + %108 = OpAccessChain %107 %42 %59 %16 + OpStore %108 %106 + OpBranch %101 + %109 = OpLabel + %111 = OpLoad %13 %110 + %112 = OpConvertUToF %39 %111 + %114 = OpCompositeExtract %38 %112 0 + %115 = OpCompositeExtract %38 %112 1 + %116 = OpCompositeConstruct %113 %114 %115 + %117 = OpAccessChain %107 %42 %59 %21 + %118 = OpCompositeExtract %38 %116 0 + OpStore %117 %118 + %119 = OpAccessChain %107 %42 %59 %76 + %120 = OpCompositeExtract %38 %116 1 + OpStore %119 %120 + OpBranch %101 + %101 = OpLabel + OpBranch %93 + %93 = OpLabel + %121 = OpLoad %24 %89 + %122 = OpIAdd %24 %121 %59 + OpStore %89 %122 + OpBranch %90 + %92 = OpLabel + OpReturn + OpFunctionEnd + +)"; + +TEST(DiffTest, LargeFunctionsLargeDiffs) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 138 ++; Bound: 190 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 +-OpEntryPoint GLCompute %4 "main" %15 ++OpEntryPoint GLCompute %4 "main" %138 %15 + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "f1(" + OpName %8 "f2(" + OpName %12 "x" ++OpName %138 "gl_GlobalInvocationID" + OpName %15 "gl_LocalInvocationID" +-OpName %20 "y" ++OpName %20 "z" + OpName %27 "image" +-OpName %44 "sum" ++OpName %44 "i" +-OpName %46 "i" +-OpName %56 "j" + OpName %80 "BufferOut" + OpMemberName %80 0 "o_uv4" + OpMemberName %80 1 "o_v3" + OpMemberName %80 2 "o_i" + OpName %82 "" + OpName %88 "BufferIn" + OpMemberName %88 0 "i_u" + OpMemberName %88 1 "i_v4" + OpMemberName %88 2 "i_f" + OpName %90 "" + OpName %101 "i" + OpName %128 "image2" ++OpDecorate %138 BuiltIn GlobalInvocationId + OpDecorate %15 BuiltIn LocalInvocationId + OpDecorate %27 DescriptorSet 0 + OpDecorate %27 Binding 2 + OpMemberDecorate %80 0 Offset 0 + OpMemberDecorate %80 1 Offset 16 + OpMemberDecorate %80 2 Offset 28 + OpDecorate %80 BufferBlock + OpDecorate %82 DescriptorSet 0 + OpDecorate %82 Binding 1 + OpMemberDecorate %88 0 Offset 0 + OpMemberDecorate %88 1 RowMajor + OpMemberDecorate %88 1 Offset 16 + OpMemberDecorate %88 1 MatrixStride 16 + OpMemberDecorate %88 2 Offset 80 + OpDecorate %88 Block + OpDecorate %90 DescriptorSet 0 + OpDecorate %90 Binding 0 + OpDecorate %128 DescriptorSet 0 + OpDecorate %128 Binding 3 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 0 + %11 = OpTypePointer Function %10 + %13 = OpTypeVector %10 3 + %14 = OpTypePointer Input %13 ++%138 = OpVariable %14 Input + %15 = OpVariable %14 Input + %16 = OpConstant %10 0 + %17 = OpTypePointer Input %10 + %21 = OpConstant %10 1 + %24 = OpTypeInt 32 1 + %25 = OpTypeImage %24 2D 0 0 0 2 R32i + %26 = OpTypePointer UniformConstant %25 + %27 = OpVariable %26 UniformConstant + %29 = OpTypeVector %10 2 + %32 = OpTypeVector %24 2 + %38 = OpTypeVector %24 4 + %40 = OpConstant %10 2 + %41 = OpConstant %10 3400 + %42 = OpConstant %10 264 + %43 = OpTypePointer Function %24 + %45 = OpConstant %24 0 ++%149 = OpTypePointer Uniform %10 + %53 = OpConstant %24 2 + %54 = OpTypeBool + %73 = OpConstant %24 1 + %77 = OpTypeVector %10 4 + %78 = OpTypeFloat 32 + %79 = OpTypeVector %78 3 + %80 = OpTypeStruct %77 %79 %24 + %81 = OpTypePointer Uniform %80 + %82 = OpVariable %81 Uniform + %84 = OpTypePointer Uniform %24 + %86 = OpTypeVector %78 4 + %87 = OpTypeMatrix %86 4 + %88 = OpTypeStruct %10 %87 %78 + %89 = OpTypePointer Uniform %88 + %90 = OpVariable %89 Uniform +-%91 = OpTypePointer Uniform %87 ++%179 = OpTypeVector %78 2 + %94 = OpTypePointer Uniform %77 + %108 = OpConstant %24 3 +-%110 = OpTypePointer Uniform %79 + %113 = OpTypePointer Uniform %78 + %128 = OpVariable %26 UniformConstant + %130 = OpConstantComposite %32 %45 %45 + %4 = OpFunction %2 None %3 + %5 = OpLabel +-%136 = OpFunctionCall %2 %6 + %137 = OpFunctionCall %2 %8 ++%189 = OpFunctionCall %2 %6 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %12 = OpVariable %11 Function + %20 = OpVariable %11 Function + %44 = OpVariable %43 Function +-%46 = OpVariable %43 Function +-%56 = OpVariable %43 Function +-%18 = OpAccessChain %17 %15 %16 ++%139 = OpAccessChain %17 %138 %16 +-%19 = OpLoad %10 %18 ++%19 = OpLoad %10 %139 + OpStore %12 %19 +-%22 = OpAccessChain %17 %15 %21 ++%140 = OpAccessChain %17 %138 %21 +-%23 = OpLoad %10 %22 ++%23 = OpLoad %10 %140 + OpStore %20 %23 +-%28 = OpLoad %25 %27 +-%30 = OpLoad %13 %15 +-%31 = OpVectorShuffle %29 %30 %30 0 1 +-%33 = OpBitcast %32 %31 +-%34 = OpLoad %10 %12 +-%35 = OpLoad %10 %20 +-%36 = OpIAdd %10 %34 %35 +-%37 = OpBitcast %24 %36 +-%39 = OpCompositeConstruct %38 %37 %37 %37 %37 +-OpImageWrite %28 %33 %39 +-OpMemoryBarrier %40 %41 +-OpControlBarrier %40 %40 %42 + OpStore %44 %45 +-OpStore %46 %45 + OpBranch %47 + %47 = OpLabel +-OpLoopMerge %49 %50 None ++OpLoopMerge %49 %59 None + OpBranch %51 + %51 = OpLabel +-%52 = OpLoad %24 %46 ++%52 = OpLoad %24 %44 + %55 = OpSLessThan %54 %52 %53 + OpBranchConditional %55 %48 %49 + %48 = OpLabel +-OpStore %56 %45 +-OpBranch %57 +-%57 = OpLabel +-OpLoopMerge %59 %60 None +-OpBranch %61 +-%61 = OpLabel +-%62 = OpLoad %24 %56 +-%63 = OpSLessThan %54 %62 %53 +-OpBranchConditional %63 %58 %59 +-%58 = OpLabel +-%64 = OpLoad %25 %27 +-%65 = OpLoad %24 %46 +-%66 = OpLoad %24 %56 +-%67 = OpCompositeConstruct %32 %65 %66 +-%68 = OpImageRead %38 %64 %67 +-%69 = OpCompositeExtract %24 %68 0 +-%70 = OpLoad %24 %44 +-%71 = OpIMul %24 %70 %69 ++%141 = OpLoad %10 %12 ++%142 = OpLoad %10 %20 ++%143 = OpIAdd %10 %141 %142 ++%144 = OpCompositeConstruct %29 %143 %143 ++%145 = OpAccessChain %94 %82 %45 ++%146 = OpLoad %77 %145 ++%147 = OpVectorShuffle %29 %146 %146 0 1 ++%148 = OpIAdd %29 %147 %144 ++%150 = OpAccessChain %149 %82 %45 %16 ++%151 = OpCompositeExtract %10 %148 0 +-OpStore %44 %71 ++OpStore %150 %151 +-OpBranch %60 +-%60 = OpLabel +-%72 = OpLoad %24 %56 +-%74 = OpIAdd %24 %72 %73 ++%152 = OpAccessChain %149 %82 %45 %21 ++%153 = OpCompositeExtract %10 %148 1 +-OpStore %56 %74 ++OpStore %152 %153 +-OpBranch %57 ++OpBranch %59 + %59 = OpLabel +-OpBranch %50 +-%50 = OpLabel +-%75 = OpLoad %24 %46 ++%75 = OpLoad %24 %44 + %76 = OpIAdd %24 %75 %73 +-OpStore %46 %76 ++OpStore %44 %76 + OpBranch %47 + %49 = OpLabel ++%154 = OpLoad %25 %128 ++%155 = OpLoad %10 %12 ++%156 = OpBitcast %24 %155 ++%157 = OpLoad %10 %20 ++%158 = OpBitcast %24 %157 ++%159 = OpCompositeConstruct %32 %156 %158 ++%160 = OpImageRead %38 %154 %159 ++%161 = OpCompositeExtract %24 %160 1 ++%162 = OpAccessChain %84 %82 %53 ++OpStore %162 %161 + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 +-%83 = OpLoad %24 %44 ++%163 = OpLoad %25 %27 ++%164 = OpLoad %10 %20 ++%165 = OpBitcast %24 %164 ++%166 = OpLoad %10 %12 ++%167 = OpBitcast %24 %166 ++%168 = OpCompositeConstruct %32 %165 %167 + %85 = OpAccessChain %84 %82 %53 +-OpStore %85 %83 ++%169 = OpLoad %24 %85 ++%170 = OpCompositeConstruct %38 %169 %45 %45 %45 ++OpImageWrite %163 %168 %170 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %101 = OpVariable %43 Function +-%92 = OpAccessChain %91 %90 %73 +-%93 = OpLoad %87 %92 +-%95 = OpAccessChain %94 %82 %45 +-%96 = OpLoad %77 %95 +-%97 = OpConvertUToF %86 %96 +-%98 = OpMatrixTimesVector %86 %93 %97 +-%99 = OpConvertFToU %77 %98 +-%100 = OpAccessChain %94 %82 %45 +-OpStore %100 %99 ++OpStore %101 %45 +-OpStore %101 %45 + OpBranch %102 + %102 = OpLabel +-OpLoopMerge %104 %105 None ++OpLoopMerge %171 %172 None + OpBranch %106 + %106 = OpLabel + %107 = OpLoad %24 %101 + %109 = OpSLessThan %54 %107 %108 +-OpBranchConditional %109 %103 %104 ++OpBranchConditional %109 %103 %171 + %103 = OpLabel +-%111 = OpAccessChain %110 %82 %73 +-%112 = OpLoad %79 %111 +-%114 = OpAccessChain %113 %90 %53 +-%115 = OpLoad %78 %114 +-%116 = OpVectorTimesScalar %79 %112 %115 +-%117 = OpConvertFToU %13 %116 +-%118 = OpCompositeExtract %10 %117 0 +-%119 = OpCompositeExtract %10 %117 1 +-%120 = OpCompositeExtract %10 %117 2 +-%121 = OpCompositeConstruct %77 %118 %119 %120 %16 +-%122 = OpAccessChain %94 %82 %45 +-%123 = OpLoad %77 %122 +-%124 = OpIAdd %77 %123 %121 +-%125 = OpAccessChain %94 %82 %45 +-OpStore %125 %124 +-OpBranch %105 +-%105 = OpLabel + %126 = OpLoad %24 %101 +-%127 = OpIAdd %24 %126 %73 ++%173 = OpIEqual %54 %126 %45 ++OpSelectionMerge %174 None ++OpBranchConditional %173 %104 %176 ++%176 = OpLabel ++%177 = OpLoad %13 %15 ++%178 = OpConvertUToF %79 %177 ++%180 = OpCompositeExtract %78 %178 0 ++%181 = OpCompositeExtract %78 %178 1 ++%182 = OpCompositeConstruct %179 %180 %181 ++%183 = OpAccessChain %113 %82 %73 %21 ++%184 = OpCompositeExtract %78 %182 0 +-OpStore %101 %127 ++OpStore %183 %184 ++%185 = OpAccessChain %113 %82 %73 %40 ++%186 = OpCompositeExtract %78 %182 1 ++OpStore %185 %186 +-OpBranch %102 ++OpBranch %174 + %104 = OpLabel +-OpMemoryBarrier %40 %41 +-OpControlBarrier %40 %40 %42 + %129 = OpLoad %25 %128 + %131 = OpImageRead %38 %129 %130 + %132 = OpCompositeExtract %24 %131 0 + %133 = OpConvertSToF %78 %132 +-%134 = OpCompositeConstruct %79 %133 %133 %133 +-%135 = OpAccessChain %110 %82 %73 ++%175 = OpAccessChain %113 %82 %73 %16 +-OpStore %135 %134 ++OpStore %175 %133 ++OpBranch %174 ++%174 = OpLabel ++OpBranch %172 ++%172 = OpLabel ++%187 = OpLoad %24 %101 ++%188 = OpIAdd %24 %187 %73 ++OpStore %101 %188 ++OpBranch %102 ++%171 = OpLabel + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, LargeFunctionsLargeDiffsNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" %15 + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpDecorate %15 BuiltIn LocalInvocationId + OpDecorate %27 DescriptorSet 0 + OpDecorate %27 Binding 2 + OpMemberDecorate %80 0 Offset 0 + OpMemberDecorate %80 1 Offset 16 + OpMemberDecorate %80 2 Offset 28 + OpDecorate %80 BufferBlock + OpDecorate %82 DescriptorSet 0 + OpDecorate %82 Binding 1 + OpMemberDecorate %88 0 Offset 0 + OpMemberDecorate %88 1 RowMajor + OpMemberDecorate %88 1 Offset 16 + OpMemberDecorate %88 1 MatrixStride 16 + OpMemberDecorate %88 2 Offset 80 + OpDecorate %88 Block + OpDecorate %90 DescriptorSet 0 + OpDecorate %90 Binding 0 + OpDecorate %128 DescriptorSet 0 + OpDecorate %128 Binding 3 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 0 + %11 = OpTypePointer Function %10 + %13 = OpTypeVector %10 3 + %14 = OpTypePointer Input %13 + %15 = OpVariable %14 Input + %16 = OpConstant %10 0 + %17 = OpTypePointer Input %10 + %21 = OpConstant %10 1 + %24 = OpTypeInt 32 1 + %25 = OpTypeImage %24 2D 0 0 0 2 R32i + %26 = OpTypePointer UniformConstant %25 + %27 = OpVariable %26 UniformConstant + %29 = OpTypeVector %10 2 + %32 = OpTypeVector %24 2 + %38 = OpTypeVector %24 4 + %40 = OpConstant %10 2 + %41 = OpConstant %10 3400 + %42 = OpConstant %10 264 + %43 = OpTypePointer Function %24 + %45 = OpConstant %24 0 + %53 = OpConstant %24 2 + %54 = OpTypeBool + %73 = OpConstant %24 1 + %77 = OpTypeVector %10 4 + %78 = OpTypeFloat 32 + %79 = OpTypeVector %78 3 + %80 = OpTypeStruct %77 %79 %24 + %81 = OpTypePointer Uniform %80 + %82 = OpVariable %81 Uniform + %84 = OpTypePointer Uniform %24 + %86 = OpTypeVector %78 4 + %87 = OpTypeMatrix %86 4 + %88 = OpTypeStruct %10 %87 %78 + %89 = OpTypePointer Uniform %88 + %90 = OpVariable %89 Uniform + %91 = OpTypePointer Uniform %87 + %94 = OpTypePointer Uniform %77 + %108 = OpConstant %24 3 + %110 = OpTypePointer Uniform %79 + %113 = OpTypePointer Uniform %78 + %128 = OpVariable %26 UniformConstant + %130 = OpConstantComposite %32 %45 %45 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %136 = OpFunctionCall %2 %6 + %137 = OpFunctionCall %2 %8 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %12 = OpVariable %11 Function + %20 = OpVariable %11 Function + %44 = OpVariable %43 Function + %46 = OpVariable %43 Function + %56 = OpVariable %43 Function + %18 = OpAccessChain %17 %15 %16 + %19 = OpLoad %10 %18 + OpStore %12 %19 + %22 = OpAccessChain %17 %15 %21 + %23 = OpLoad %10 %22 + OpStore %20 %23 + %28 = OpLoad %25 %27 + %30 = OpLoad %13 %15 + %31 = OpVectorShuffle %29 %30 %30 0 1 + %33 = OpBitcast %32 %31 + %34 = OpLoad %10 %12 + %35 = OpLoad %10 %20 + %36 = OpIAdd %10 %34 %35 + %37 = OpBitcast %24 %36 + %39 = OpCompositeConstruct %38 %37 %37 %37 %37 + OpImageWrite %28 %33 %39 + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + OpStore %44 %45 + OpStore %46 %45 + OpBranch %47 + %47 = OpLabel + OpLoopMerge %49 %50 None + OpBranch %51 + %51 = OpLabel + %52 = OpLoad %24 %46 + %55 = OpSLessThan %54 %52 %53 + OpBranchConditional %55 %48 %49 + %48 = OpLabel + OpStore %56 %45 + OpBranch %57 + %57 = OpLabel + OpLoopMerge %59 %60 None + OpBranch %61 + %61 = OpLabel + %62 = OpLoad %24 %56 + %63 = OpSLessThan %54 %62 %53 + OpBranchConditional %63 %58 %59 + %58 = OpLabel + %64 = OpLoad %25 %27 + %65 = OpLoad %24 %46 + %66 = OpLoad %24 %56 + %67 = OpCompositeConstruct %32 %65 %66 + %68 = OpImageRead %38 %64 %67 + %69 = OpCompositeExtract %24 %68 0 + %70 = OpLoad %24 %44 + %71 = OpIMul %24 %70 %69 + OpStore %44 %71 + OpBranch %60 + %60 = OpLabel + %72 = OpLoad %24 %56 + %74 = OpIAdd %24 %72 %73 + OpStore %56 %74 + OpBranch %57 + %59 = OpLabel + OpBranch %50 + %50 = OpLabel + %75 = OpLoad %24 %46 + %76 = OpIAdd %24 %75 %73 + OpStore %46 %76 + OpBranch %47 + %49 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + %83 = OpLoad %24 %44 + %85 = OpAccessChain %84 %82 %53 + OpStore %85 %83 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %101 = OpVariable %43 Function + %92 = OpAccessChain %91 %90 %73 + %93 = OpLoad %87 %92 + %95 = OpAccessChain %94 %82 %45 + %96 = OpLoad %77 %95 + %97 = OpConvertUToF %86 %96 + %98 = OpMatrixTimesVector %86 %93 %97 + %99 = OpConvertFToU %77 %98 + %100 = OpAccessChain %94 %82 %45 + OpStore %100 %99 + OpStore %101 %45 + OpBranch %102 + %102 = OpLabel + OpLoopMerge %104 %105 None + OpBranch %106 + %106 = OpLabel + %107 = OpLoad %24 %101 + %109 = OpSLessThan %54 %107 %108 + OpBranchConditional %109 %103 %104 + %103 = OpLabel + %111 = OpAccessChain %110 %82 %73 + %112 = OpLoad %79 %111 + %114 = OpAccessChain %113 %90 %53 + %115 = OpLoad %78 %114 + %116 = OpVectorTimesScalar %79 %112 %115 + %117 = OpConvertFToU %13 %116 + %118 = OpCompositeExtract %10 %117 0 + %119 = OpCompositeExtract %10 %117 1 + %120 = OpCompositeExtract %10 %117 2 + %121 = OpCompositeConstruct %77 %118 %119 %120 %16 + %122 = OpAccessChain %94 %82 %45 + %123 = OpLoad %77 %122 + %124 = OpIAdd %77 %123 %121 + %125 = OpAccessChain %94 %82 %45 + OpStore %125 %124 + OpBranch %105 + %105 = OpLabel + %126 = OpLoad %24 %101 + %127 = OpIAdd %24 %126 %73 + OpStore %101 %127 + OpBranch %102 + %104 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + %129 = OpLoad %25 %128 + %131 = OpImageRead %38 %129 %130 + %132 = OpCompositeExtract %24 %131 0 + %133 = OpConvertSToF %78 %132 + %134 = OpCompositeConstruct %79 %133 %133 %133 + %135 = OpAccessChain %110 %82 %73 + OpStore %135 %134 + OpReturn + OpFunctionEnd + +)"; + constexpr char kDstNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" %15 %110 + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpDecorate %15 BuiltIn GlobalInvocationId + OpMemberDecorate %40 0 Offset 0 + OpMemberDecorate %40 1 Offset 16 + OpMemberDecorate %40 2 Offset 28 + OpDecorate %40 BufferBlock + OpDecorate %42 DescriptorSet 0 + OpDecorate %42 Binding 1 + OpDecorate %63 DescriptorSet 0 + OpDecorate %63 Binding 3 + OpDecorate %79 DescriptorSet 0 + OpDecorate %79 Binding 2 + OpDecorate %110 BuiltIn LocalInvocationId + OpMemberDecorate %127 0 Offset 0 + OpMemberDecorate %127 1 RowMajor + OpMemberDecorate %127 1 Offset 16 + OpMemberDecorate %127 1 MatrixStride 16 + OpMemberDecorate %127 2 Offset 80 + OpDecorate %127 Block + OpDecorate %129 DescriptorSet 0 + OpDecorate %129 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 0 + %11 = OpTypePointer Function %10 + %13 = OpTypeVector %10 3 + %14 = OpTypePointer Input %13 + %15 = OpVariable %14 Input + %16 = OpConstant %10 0 + %17 = OpTypePointer Input %10 + %21 = OpConstant %10 1 + %24 = OpTypeInt 32 1 + %25 = OpTypePointer Function %24 + %27 = OpConstant %24 0 + %34 = OpConstant %24 2 + %35 = OpTypeBool + %37 = OpTypeVector %10 4 + %38 = OpTypeFloat 32 + %39 = OpTypeVector %38 3 + %40 = OpTypeStruct %37 %39 %24 + %41 = OpTypePointer Uniform %40 + %42 = OpVariable %41 Uniform + %46 = OpTypeVector %10 2 + %48 = OpTypePointer Uniform %37 + %53 = OpTypePointer Uniform %10 + %59 = OpConstant %24 1 + %61 = OpTypeImage %24 2D 0 0 0 2 R32i + %62 = OpTypePointer UniformConstant %61 + %63 = OpVariable %62 UniformConstant + %69 = OpTypeVector %24 2 + %71 = OpTypeVector %24 4 + %74 = OpTypePointer Uniform %24 + %76 = OpConstant %10 2 + %77 = OpConstant %10 3400 + %78 = OpConstant %10 264 + %79 = OpVariable %62 UniformConstant + %96 = OpConstant %24 3 + %103 = OpConstantComposite %69 %27 %27 + %107 = OpTypePointer Uniform %38 + %110 = OpVariable %14 Input + %113 = OpTypeVector %38 2 + %125 = OpTypeVector %38 4 + %126 = OpTypeMatrix %125 4 + %127 = OpTypeStruct %10 %126 %38 + %128 = OpTypePointer Uniform %127 + %129 = OpVariable %128 Uniform + %4 = OpFunction %2 None %3 + %5 = OpLabel + %123 = OpFunctionCall %2 %8 + %124 = OpFunctionCall %2 %6 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %12 = OpVariable %11 Function + %20 = OpVariable %11 Function + %26 = OpVariable %25 Function + %18 = OpAccessChain %17 %15 %16 + %19 = OpLoad %10 %18 + OpStore %12 %19 + %22 = OpAccessChain %17 %15 %21 + %23 = OpLoad %10 %22 + OpStore %20 %23 + OpStore %26 %27 + OpBranch %28 + %28 = OpLabel + OpLoopMerge %30 %31 None + OpBranch %32 + %32 = OpLabel + %33 = OpLoad %24 %26 + %36 = OpSLessThan %35 %33 %34 + OpBranchConditional %36 %29 %30 + %29 = OpLabel + %43 = OpLoad %10 %12 + %44 = OpLoad %10 %20 + %45 = OpIAdd %10 %43 %44 + %47 = OpCompositeConstruct %46 %45 %45 + %49 = OpAccessChain %48 %42 %27 + %50 = OpLoad %37 %49 + %51 = OpVectorShuffle %46 %50 %50 0 1 + %52 = OpIAdd %46 %51 %47 + %54 = OpAccessChain %53 %42 %27 %16 + %55 = OpCompositeExtract %10 %52 0 + OpStore %54 %55 + %56 = OpAccessChain %53 %42 %27 %21 + %57 = OpCompositeExtract %10 %52 1 + OpStore %56 %57 + OpBranch %31 + %31 = OpLabel + %58 = OpLoad %24 %26 + %60 = OpIAdd %24 %58 %59 + OpStore %26 %60 + OpBranch %28 + %30 = OpLabel + %64 = OpLoad %61 %63 + %65 = OpLoad %10 %12 + %66 = OpBitcast %24 %65 + %67 = OpLoad %10 %20 + %68 = OpBitcast %24 %67 + %70 = OpCompositeConstruct %69 %66 %68 + %72 = OpImageRead %71 %64 %70 + %73 = OpCompositeExtract %24 %72 1 + %75 = OpAccessChain %74 %42 %34 + OpStore %75 %73 + OpMemoryBarrier %76 %77 + OpControlBarrier %76 %76 %78 + %80 = OpLoad %61 %79 + %81 = OpLoad %10 %20 + %82 = OpBitcast %24 %81 + %83 = OpLoad %10 %12 + %84 = OpBitcast %24 %83 + %85 = OpCompositeConstruct %69 %82 %84 + %86 = OpAccessChain %74 %42 %34 + %87 = OpLoad %24 %86 + %88 = OpCompositeConstruct %71 %87 %27 %27 %27 + OpImageWrite %80 %85 %88 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %89 = OpVariable %25 Function + OpStore %89 %27 + OpBranch %90 + %90 = OpLabel + OpLoopMerge %92 %93 None + OpBranch %94 + %94 = OpLabel + %95 = OpLoad %24 %89 + %97 = OpSLessThan %35 %95 %96 + OpBranchConditional %97 %91 %92 + %91 = OpLabel + %98 = OpLoad %24 %89 + %99 = OpIEqual %35 %98 %27 + OpSelectionMerge %101 None + OpBranchConditional %99 %100 %109 + %100 = OpLabel + %102 = OpLoad %61 %63 + %104 = OpImageRead %71 %102 %103 + %105 = OpCompositeExtract %24 %104 0 + %106 = OpConvertSToF %38 %105 + %108 = OpAccessChain %107 %42 %59 %16 + OpStore %108 %106 + OpBranch %101 + %109 = OpLabel + %111 = OpLoad %13 %110 + %112 = OpConvertUToF %39 %111 + %114 = OpCompositeExtract %38 %112 0 + %115 = OpCompositeExtract %38 %112 1 + %116 = OpCompositeConstruct %113 %114 %115 + %117 = OpAccessChain %107 %42 %59 %21 + %118 = OpCompositeExtract %38 %116 0 + OpStore %117 %118 + %119 = OpAccessChain %107 %42 %59 %76 + %120 = OpCompositeExtract %38 %116 1 + OpStore %119 %120 + OpBranch %101 + %101 = OpLabel + OpBranch %93 + %93 = OpLabel + %121 = OpLoad %24 %89 + %122 = OpIAdd %24 %121 %59 + OpStore %89 %122 + OpBranch %90 + %92 = OpLabel + OpReturn + OpFunctionEnd + +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 138 ++; Bound: 220 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 +-OpEntryPoint GLCompute %4 "main" %15 ++OpEntryPoint GLCompute %4 "main" %143 %15 + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 ++OpDecorate %143 BuiltIn GlobalInvocationId + OpDecorate %15 BuiltIn LocalInvocationId + OpDecorate %27 DescriptorSet 0 + OpDecorate %27 Binding 2 + OpMemberDecorate %80 0 Offset 0 + OpMemberDecorate %80 1 Offset 16 + OpMemberDecorate %80 2 Offset 28 + OpDecorate %80 BufferBlock + OpDecorate %82 DescriptorSet 0 + OpDecorate %82 Binding 1 + OpMemberDecorate %88 0 Offset 0 + OpMemberDecorate %88 1 RowMajor + OpMemberDecorate %88 1 Offset 16 + OpMemberDecorate %88 1 MatrixStride 16 + OpMemberDecorate %88 2 Offset 80 + OpDecorate %88 Block + OpDecorate %90 DescriptorSet 0 + OpDecorate %90 Binding 0 + OpDecorate %128 DescriptorSet 0 + OpDecorate %128 Binding 3 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 0 + %11 = OpTypePointer Function %10 + %13 = OpTypeVector %10 3 + %14 = OpTypePointer Input %13 ++%143 = OpVariable %14 Input + %15 = OpVariable %14 Input + %16 = OpConstant %10 0 + %17 = OpTypePointer Input %10 + %21 = OpConstant %10 1 + %24 = OpTypeInt 32 1 + %25 = OpTypeImage %24 2D 0 0 0 2 R32i + %26 = OpTypePointer UniformConstant %25 + %27 = OpVariable %26 UniformConstant + %29 = OpTypeVector %10 2 + %32 = OpTypeVector %24 2 + %38 = OpTypeVector %24 4 + %40 = OpConstant %10 2 + %41 = OpConstant %10 3400 + %42 = OpConstant %10 264 + %43 = OpTypePointer Function %24 + %45 = OpConstant %24 0 ++%165 = OpTypePointer Uniform %10 + %53 = OpConstant %24 2 + %54 = OpTypeBool + %73 = OpConstant %24 1 + %77 = OpTypeVector %10 4 + %78 = OpTypeFloat 32 + %79 = OpTypeVector %78 3 + %80 = OpTypeStruct %77 %79 %24 + %81 = OpTypePointer Uniform %80 + %82 = OpVariable %81 Uniform + %84 = OpTypePointer Uniform %24 + %86 = OpTypeVector %78 4 + %87 = OpTypeMatrix %86 4 + %88 = OpTypeStruct %10 %87 %78 + %89 = OpTypePointer Uniform %88 + %90 = OpVariable %89 Uniform +-%91 = OpTypePointer Uniform %87 ++%210 = OpTypeVector %78 2 + %94 = OpTypePointer Uniform %77 + %108 = OpConstant %24 3 +-%110 = OpTypePointer Uniform %79 + %113 = OpTypePointer Uniform %78 + %128 = OpVariable %26 UniformConstant + %130 = OpConstantComposite %32 %45 %45 + %4 = OpFunction %2 None %3 + %5 = OpLabel +-%136 = OpFunctionCall %2 %6 ++%136 = OpFunctionCall %2 %140 +-%137 = OpFunctionCall %2 %8 ++%137 = OpFunctionCall %2 %138 + OpReturn + OpFunctionEnd +-%6 = OpFunction %2 None %3 +-%7 = OpLabel +-%12 = OpVariable %11 Function +-%20 = OpVariable %11 Function +-%44 = OpVariable %43 Function +-%46 = OpVariable %43 Function +-%56 = OpVariable %43 Function +-%18 = OpAccessChain %17 %15 %16 +-%19 = OpLoad %10 %18 +-OpStore %12 %19 +-%22 = OpAccessChain %17 %15 %21 +-%23 = OpLoad %10 %22 +-OpStore %20 %23 +-%28 = OpLoad %25 %27 +-%30 = OpLoad %13 %15 +-%31 = OpVectorShuffle %29 %30 %30 0 1 +-%33 = OpBitcast %32 %31 +-%34 = OpLoad %10 %12 +-%35 = OpLoad %10 %20 +-%36 = OpIAdd %10 %34 %35 +-%37 = OpBitcast %24 %36 +-%39 = OpCompositeConstruct %38 %37 %37 %37 %37 +-OpImageWrite %28 %33 %39 +-OpMemoryBarrier %40 %41 +-OpControlBarrier %40 %40 %42 +-OpStore %44 %45 +-OpStore %46 %45 +-OpBranch %47 +-%47 = OpLabel +-OpLoopMerge %49 %50 None +-OpBranch %51 +-%51 = OpLabel +-%52 = OpLoad %24 %46 +-%55 = OpSLessThan %54 %52 %53 +-OpBranchConditional %55 %48 %49 +-%48 = OpLabel +-OpStore %56 %45 +-OpBranch %57 +-%57 = OpLabel +-OpLoopMerge %59 %60 None +-OpBranch %61 +-%61 = OpLabel +-%62 = OpLoad %24 %56 +-%63 = OpSLessThan %54 %62 %53 +-OpBranchConditional %63 %58 %59 +-%58 = OpLabel +-%64 = OpLoad %25 %27 +-%65 = OpLoad %24 %46 +-%66 = OpLoad %24 %56 +-%67 = OpCompositeConstruct %32 %65 %66 +-%68 = OpImageRead %38 %64 %67 +-%69 = OpCompositeExtract %24 %68 0 +-%70 = OpLoad %24 %44 +-%71 = OpIMul %24 %70 %69 +-OpStore %44 %71 +-OpBranch %60 +-%60 = OpLabel +-%72 = OpLoad %24 %56 +-%74 = OpIAdd %24 %72 %73 +-OpStore %56 %74 +-OpBranch %57 +-%59 = OpLabel +-OpBranch %50 +-%50 = OpLabel +-%75 = OpLoad %24 %46 +-%76 = OpIAdd %24 %75 %73 +-OpStore %46 %76 +-OpBranch %47 +-%49 = OpLabel +-OpMemoryBarrier %40 %41 +-OpControlBarrier %40 %40 %42 +-%83 = OpLoad %24 %44 +-%85 = OpAccessChain %84 %82 %53 +-OpStore %85 %83 +-OpReturn +-OpFunctionEnd +-%8 = OpFunction %2 None %3 +-%9 = OpLabel +-%101 = OpVariable %43 Function +-%92 = OpAccessChain %91 %90 %73 +-%93 = OpLoad %87 %92 +-%95 = OpAccessChain %94 %82 %45 +-%96 = OpLoad %77 %95 +-%97 = OpConvertUToF %86 %96 +-%98 = OpMatrixTimesVector %86 %93 %97 +-%99 = OpConvertFToU %77 %98 +-%100 = OpAccessChain %94 %82 %45 +-OpStore %100 %99 +-OpStore %101 %45 +-OpBranch %102 +-%102 = OpLabel +-OpLoopMerge %104 %105 None +-OpBranch %106 +-%106 = OpLabel +-%107 = OpLoad %24 %101 +-%109 = OpSLessThan %54 %107 %108 +-OpBranchConditional %109 %103 %104 +-%103 = OpLabel +-%111 = OpAccessChain %110 %82 %73 +-%112 = OpLoad %79 %111 +-%114 = OpAccessChain %113 %90 %53 +-%115 = OpLoad %78 %114 +-%116 = OpVectorTimesScalar %79 %112 %115 +-%117 = OpConvertFToU %13 %116 +-%118 = OpCompositeExtract %10 %117 0 +-%119 = OpCompositeExtract %10 %117 1 +-%120 = OpCompositeExtract %10 %117 2 +-%121 = OpCompositeConstruct %77 %118 %119 %120 %16 +-%122 = OpAccessChain %94 %82 %45 +-%123 = OpLoad %77 %122 +-%124 = OpIAdd %77 %123 %121 +-%125 = OpAccessChain %94 %82 %45 +-OpStore %125 %124 +-OpBranch %105 +-%105 = OpLabel +-%126 = OpLoad %24 %101 +-%127 = OpIAdd %24 %126 %73 +-OpStore %101 %127 +-OpBranch %102 +-%104 = OpLabel +-OpMemoryBarrier %40 %41 +-OpControlBarrier %40 %40 %42 +-%129 = OpLoad %25 %128 +-%131 = OpImageRead %38 %129 %130 +-%132 = OpCompositeExtract %24 %131 0 +-%133 = OpConvertSToF %78 %132 +-%134 = OpCompositeConstruct %79 %133 %133 %133 +-%135 = OpAccessChain %110 %82 %73 +-OpStore %135 %134 +-OpReturn +-OpFunctionEnd ++%138 = OpFunction %2 None %3 ++%139 = OpLabel ++%142 = OpVariable %11 Function ++%146 = OpVariable %11 Function ++%149 = OpVariable %43 Function ++%144 = OpAccessChain %17 %143 %16 ++%145 = OpLoad %10 %144 ++OpStore %142 %145 ++%147 = OpAccessChain %17 %143 %21 ++%148 = OpLoad %10 %147 ++OpStore %146 %148 ++OpStore %149 %45 ++OpBranch %150 ++%150 = OpLabel ++OpLoopMerge %152 %153 None ++OpBranch %154 ++%154 = OpLabel ++%155 = OpLoad %24 %149 ++%156 = OpSLessThan %54 %155 %53 ++OpBranchConditional %156 %151 %152 ++%151 = OpLabel ++%157 = OpLoad %10 %142 ++%158 = OpLoad %10 %146 ++%159 = OpIAdd %10 %157 %158 ++%160 = OpCompositeConstruct %29 %159 %159 ++%161 = OpAccessChain %94 %82 %45 ++%162 = OpLoad %77 %161 ++%163 = OpVectorShuffle %29 %162 %162 0 1 ++%164 = OpIAdd %29 %163 %160 ++%166 = OpAccessChain %165 %82 %45 %16 ++%167 = OpCompositeExtract %10 %164 0 ++OpStore %166 %167 ++%168 = OpAccessChain %165 %82 %45 %21 ++%169 = OpCompositeExtract %10 %164 1 ++OpStore %168 %169 ++OpBranch %153 ++%153 = OpLabel ++%170 = OpLoad %24 %149 ++%171 = OpIAdd %24 %170 %73 ++OpStore %149 %171 ++OpBranch %150 ++%152 = OpLabel ++%172 = OpLoad %25 %128 ++%173 = OpLoad %10 %142 ++%174 = OpBitcast %24 %173 ++%175 = OpLoad %10 %146 ++%176 = OpBitcast %24 %175 ++%177 = OpCompositeConstruct %32 %174 %176 ++%178 = OpImageRead %38 %172 %177 ++%179 = OpCompositeExtract %24 %178 1 ++%180 = OpAccessChain %84 %82 %53 ++OpStore %180 %179 ++OpMemoryBarrier %40 %41 ++OpControlBarrier %40 %40 %42 ++%181 = OpLoad %25 %27 ++%182 = OpLoad %10 %146 ++%183 = OpBitcast %24 %182 ++%184 = OpLoad %10 %142 ++%185 = OpBitcast %24 %184 ++%186 = OpCompositeConstruct %32 %183 %185 ++%187 = OpAccessChain %84 %82 %53 ++%188 = OpLoad %24 %187 ++%189 = OpCompositeConstruct %38 %188 %45 %45 %45 ++OpImageWrite %181 %186 %189 ++OpReturn ++OpFunctionEnd ++%140 = OpFunction %2 None %3 ++%141 = OpLabel ++%190 = OpVariable %43 Function ++OpStore %190 %45 ++OpBranch %191 ++%191 = OpLabel ++OpLoopMerge %193 %194 None ++OpBranch %195 ++%195 = OpLabel ++%196 = OpLoad %24 %190 ++%197 = OpSLessThan %54 %196 %108 ++OpBranchConditional %197 %192 %193 ++%192 = OpLabel ++%198 = OpLoad %24 %190 ++%199 = OpIEqual %54 %198 %45 ++OpSelectionMerge %201 None ++OpBranchConditional %199 %200 %207 ++%207 = OpLabel ++%208 = OpLoad %13 %15 ++%209 = OpConvertUToF %79 %208 ++%211 = OpCompositeExtract %78 %209 0 ++%212 = OpCompositeExtract %78 %209 1 ++%213 = OpCompositeConstruct %210 %211 %212 ++%214 = OpAccessChain %113 %82 %73 %21 ++%215 = OpCompositeExtract %78 %213 0 ++OpStore %214 %215 ++%216 = OpAccessChain %113 %82 %73 %40 ++%217 = OpCompositeExtract %78 %213 1 ++OpStore %216 %217 ++OpBranch %201 ++%200 = OpLabel ++%202 = OpLoad %25 %128 ++%203 = OpImageRead %38 %202 %130 ++%204 = OpCompositeExtract %24 %203 0 ++%205 = OpConvertSToF %78 %204 ++%206 = OpAccessChain %113 %82 %73 %16 ++OpStore %206 %205 ++OpBranch %201 ++%201 = OpLabel ++OpBranch %194 ++%194 = OpLabel ++%218 = OpLoad %24 %190 ++%219 = OpIAdd %24 %218 %73 ++OpStore %190 %219 ++OpBranch %191 ++%193 = OpLabel ++OpReturn ++OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/large_functions_large_diffs_dst.spvasm b/test/diff/diff_files/large_functions_large_diffs_dst.spvasm new file mode 100644 index 00000000..be7c1d5f --- /dev/null +++ b/test/diff/diff_files/large_functions_large_diffs_dst.spvasm @@ -0,0 +1,213 @@ + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" %15 %110 + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "f1(" + OpName %8 "f2(" + OpName %12 "x" + OpName %15 "gl_GlobalInvocationID" + OpName %20 "z" + OpName %26 "i" + OpName %40 "BufferOut" + OpMemberName %40 0 "o_uv4" + OpMemberName %40 1 "o_v3" + OpMemberName %40 2 "o_i" + OpName %42 "" + OpName %63 "image2" + OpName %79 "image" + OpName %89 "i" + OpName %110 "gl_LocalInvocationID" + OpName %127 "BufferIn" + OpMemberName %127 0 "i_u" + OpMemberName %127 1 "i_v4" + OpMemberName %127 2 "i_f" + OpName %129 "" + OpDecorate %15 BuiltIn GlobalInvocationId + OpMemberDecorate %40 0 Offset 0 + OpMemberDecorate %40 1 Offset 16 + OpMemberDecorate %40 2 Offset 28 + OpDecorate %40 BufferBlock + OpDecorate %42 DescriptorSet 0 + OpDecorate %42 Binding 1 + OpDecorate %63 DescriptorSet 0 + OpDecorate %63 Binding 3 + OpDecorate %79 DescriptorSet 0 + OpDecorate %79 Binding 2 + OpDecorate %110 BuiltIn LocalInvocationId + OpMemberDecorate %127 0 Offset 0 + OpMemberDecorate %127 1 RowMajor + OpMemberDecorate %127 1 Offset 16 + OpMemberDecorate %127 1 MatrixStride 16 + OpMemberDecorate %127 2 Offset 80 + OpDecorate %127 Block + OpDecorate %129 DescriptorSet 0 + OpDecorate %129 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 0 + %11 = OpTypePointer Function %10 + %13 = OpTypeVector %10 3 + %14 = OpTypePointer Input %13 + %15 = OpVariable %14 Input + %16 = OpConstant %10 0 + %17 = OpTypePointer Input %10 + %21 = OpConstant %10 1 + %24 = OpTypeInt 32 1 + %25 = OpTypePointer Function %24 + %27 = OpConstant %24 0 + %34 = OpConstant %24 2 + %35 = OpTypeBool + %37 = OpTypeVector %10 4 + %38 = OpTypeFloat 32 + %39 = OpTypeVector %38 3 + %40 = OpTypeStruct %37 %39 %24 + %41 = OpTypePointer Uniform %40 + %42 = OpVariable %41 Uniform + %46 = OpTypeVector %10 2 + %48 = OpTypePointer Uniform %37 + %53 = OpTypePointer Uniform %10 + %59 = OpConstant %24 1 + %61 = OpTypeImage %24 2D 0 0 0 2 R32i + %62 = OpTypePointer UniformConstant %61 + %63 = OpVariable %62 UniformConstant + %69 = OpTypeVector %24 2 + %71 = OpTypeVector %24 4 + %74 = OpTypePointer Uniform %24 + %76 = OpConstant %10 2 + %77 = OpConstant %10 3400 + %78 = OpConstant %10 264 + %79 = OpVariable %62 UniformConstant + %96 = OpConstant %24 3 + %103 = OpConstantComposite %69 %27 %27 + %107 = OpTypePointer Uniform %38 + %110 = OpVariable %14 Input + %113 = OpTypeVector %38 2 + %125 = OpTypeVector %38 4 + %126 = OpTypeMatrix %125 4 + %127 = OpTypeStruct %10 %126 %38 + %128 = OpTypePointer Uniform %127 + %129 = OpVariable %128 Uniform + %4 = OpFunction %2 None %3 + %5 = OpLabel + %123 = OpFunctionCall %2 %8 + %124 = OpFunctionCall %2 %6 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %12 = OpVariable %11 Function + %20 = OpVariable %11 Function + %26 = OpVariable %25 Function + %18 = OpAccessChain %17 %15 %16 + %19 = OpLoad %10 %18 + OpStore %12 %19 + %22 = OpAccessChain %17 %15 %21 + %23 = OpLoad %10 %22 + OpStore %20 %23 + OpStore %26 %27 + OpBranch %28 + %28 = OpLabel + OpLoopMerge %30 %31 None + OpBranch %32 + %32 = OpLabel + %33 = OpLoad %24 %26 + %36 = OpSLessThan %35 %33 %34 + OpBranchConditional %36 %29 %30 + %29 = OpLabel + %43 = OpLoad %10 %12 + %44 = OpLoad %10 %20 + %45 = OpIAdd %10 %43 %44 + %47 = OpCompositeConstruct %46 %45 %45 + %49 = OpAccessChain %48 %42 %27 + %50 = OpLoad %37 %49 + %51 = OpVectorShuffle %46 %50 %50 0 1 + %52 = OpIAdd %46 %51 %47 + %54 = OpAccessChain %53 %42 %27 %16 + %55 = OpCompositeExtract %10 %52 0 + OpStore %54 %55 + %56 = OpAccessChain %53 %42 %27 %21 + %57 = OpCompositeExtract %10 %52 1 + OpStore %56 %57 + OpBranch %31 + %31 = OpLabel + %58 = OpLoad %24 %26 + %60 = OpIAdd %24 %58 %59 + OpStore %26 %60 + OpBranch %28 + %30 = OpLabel + %64 = OpLoad %61 %63 + %65 = OpLoad %10 %12 + %66 = OpBitcast %24 %65 + %67 = OpLoad %10 %20 + %68 = OpBitcast %24 %67 + %70 = OpCompositeConstruct %69 %66 %68 + %72 = OpImageRead %71 %64 %70 + %73 = OpCompositeExtract %24 %72 1 + %75 = OpAccessChain %74 %42 %34 + OpStore %75 %73 + OpMemoryBarrier %76 %77 + OpControlBarrier %76 %76 %78 + %80 = OpLoad %61 %79 + %81 = OpLoad %10 %20 + %82 = OpBitcast %24 %81 + %83 = OpLoad %10 %12 + %84 = OpBitcast %24 %83 + %85 = OpCompositeConstruct %69 %82 %84 + %86 = OpAccessChain %74 %42 %34 + %87 = OpLoad %24 %86 + %88 = OpCompositeConstruct %71 %87 %27 %27 %27 + OpImageWrite %80 %85 %88 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %89 = OpVariable %25 Function + OpStore %89 %27 + OpBranch %90 + %90 = OpLabel + OpLoopMerge %92 %93 None + OpBranch %94 + %94 = OpLabel + %95 = OpLoad %24 %89 + %97 = OpSLessThan %35 %95 %96 + OpBranchConditional %97 %91 %92 + %91 = OpLabel + %98 = OpLoad %24 %89 + %99 = OpIEqual %35 %98 %27 + OpSelectionMerge %101 None + OpBranchConditional %99 %100 %109 + %100 = OpLabel + %102 = OpLoad %61 %63 + %104 = OpImageRead %71 %102 %103 + %105 = OpCompositeExtract %24 %104 0 + %106 = OpConvertSToF %38 %105 + %108 = OpAccessChain %107 %42 %59 %16 + OpStore %108 %106 + OpBranch %101 + %109 = OpLabel + %111 = OpLoad %13 %110 + %112 = OpConvertUToF %39 %111 + %114 = OpCompositeExtract %38 %112 0 + %115 = OpCompositeExtract %38 %112 1 + %116 = OpCompositeConstruct %113 %114 %115 + %117 = OpAccessChain %107 %42 %59 %21 + %118 = OpCompositeExtract %38 %116 0 + OpStore %117 %118 + %119 = OpAccessChain %107 %42 %59 %76 + %120 = OpCompositeExtract %38 %116 1 + OpStore %119 %120 + OpBranch %101 + %101 = OpLabel + OpBranch %93 + %93 = OpLabel + %121 = OpLoad %24 %89 + %122 = OpIAdd %24 %121 %59 + OpStore %89 %122 + OpBranch %90 + %92 = OpLabel + OpReturn + OpFunctionEnd + diff --git a/test/diff/diff_files/large_functions_large_diffs_src.spvasm b/test/diff/diff_files/large_functions_large_diffs_src.spvasm new file mode 100644 index 00000000..8f0aa614 --- /dev/null +++ b/test/diff/diff_files/large_functions_large_diffs_src.spvasm @@ -0,0 +1,230 @@ +;; Test where src and dst have a few large functions with large differences. + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" %15 + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "f1(" + OpName %8 "f2(" + OpName %12 "x" + OpName %15 "gl_LocalInvocationID" + OpName %20 "y" + OpName %27 "image" + OpName %44 "sum" + OpName %46 "i" + OpName %56 "j" + OpName %80 "BufferOut" + OpMemberName %80 0 "o_uv4" + OpMemberName %80 1 "o_v3" + OpMemberName %80 2 "o_i" + OpName %82 "" + OpName %88 "BufferIn" + OpMemberName %88 0 "i_u" + OpMemberName %88 1 "i_v4" + OpMemberName %88 2 "i_f" + OpName %90 "" + OpName %101 "i" + OpName %128 "image2" + OpDecorate %15 BuiltIn LocalInvocationId + OpDecorate %27 DescriptorSet 0 + OpDecorate %27 Binding 2 + OpMemberDecorate %80 0 Offset 0 + OpMemberDecorate %80 1 Offset 16 + OpMemberDecorate %80 2 Offset 28 + OpDecorate %80 BufferBlock + OpDecorate %82 DescriptorSet 0 + OpDecorate %82 Binding 1 + OpMemberDecorate %88 0 Offset 0 + OpMemberDecorate %88 1 RowMajor + OpMemberDecorate %88 1 Offset 16 + OpMemberDecorate %88 1 MatrixStride 16 + OpMemberDecorate %88 2 Offset 80 + OpDecorate %88 Block + OpDecorate %90 DescriptorSet 0 + OpDecorate %90 Binding 0 + OpDecorate %128 DescriptorSet 0 + OpDecorate %128 Binding 3 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 0 + %11 = OpTypePointer Function %10 + %13 = OpTypeVector %10 3 + %14 = OpTypePointer Input %13 + %15 = OpVariable %14 Input + %16 = OpConstant %10 0 + %17 = OpTypePointer Input %10 + %21 = OpConstant %10 1 + %24 = OpTypeInt 32 1 + %25 = OpTypeImage %24 2D 0 0 0 2 R32i + %26 = OpTypePointer UniformConstant %25 + %27 = OpVariable %26 UniformConstant + %29 = OpTypeVector %10 2 + %32 = OpTypeVector %24 2 + %38 = OpTypeVector %24 4 + %40 = OpConstant %10 2 + %41 = OpConstant %10 3400 + %42 = OpConstant %10 264 + %43 = OpTypePointer Function %24 + %45 = OpConstant %24 0 + %53 = OpConstant %24 2 + %54 = OpTypeBool + %73 = OpConstant %24 1 + %77 = OpTypeVector %10 4 + %78 = OpTypeFloat 32 + %79 = OpTypeVector %78 3 + %80 = OpTypeStruct %77 %79 %24 + %81 = OpTypePointer Uniform %80 + %82 = OpVariable %81 Uniform + %84 = OpTypePointer Uniform %24 + %86 = OpTypeVector %78 4 + %87 = OpTypeMatrix %86 4 + %88 = OpTypeStruct %10 %87 %78 + %89 = OpTypePointer Uniform %88 + %90 = OpVariable %89 Uniform + %91 = OpTypePointer Uniform %87 + %94 = OpTypePointer Uniform %77 + %108 = OpConstant %24 3 + %110 = OpTypePointer Uniform %79 + %113 = OpTypePointer Uniform %78 + %128 = OpVariable %26 UniformConstant + %130 = OpConstantComposite %32 %45 %45 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %136 = OpFunctionCall %2 %6 + %137 = OpFunctionCall %2 %8 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %12 = OpVariable %11 Function + %20 = OpVariable %11 Function + %44 = OpVariable %43 Function + %46 = OpVariable %43 Function + %56 = OpVariable %43 Function + %18 = OpAccessChain %17 %15 %16 + %19 = OpLoad %10 %18 + OpStore %12 %19 + %22 = OpAccessChain %17 %15 %21 + %23 = OpLoad %10 %22 + OpStore %20 %23 + %28 = OpLoad %25 %27 + %30 = OpLoad %13 %15 + %31 = OpVectorShuffle %29 %30 %30 0 1 + %33 = OpBitcast %32 %31 + %34 = OpLoad %10 %12 + %35 = OpLoad %10 %20 + %36 = OpIAdd %10 %34 %35 + %37 = OpBitcast %24 %36 + %39 = OpCompositeConstruct %38 %37 %37 %37 %37 + OpImageWrite %28 %33 %39 + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + OpStore %44 %45 + OpStore %46 %45 + OpBranch %47 + %47 = OpLabel + OpLoopMerge %49 %50 None + OpBranch %51 + %51 = OpLabel + %52 = OpLoad %24 %46 + %55 = OpSLessThan %54 %52 %53 + OpBranchConditional %55 %48 %49 + %48 = OpLabel + OpStore %56 %45 + OpBranch %57 + %57 = OpLabel + OpLoopMerge %59 %60 None + OpBranch %61 + %61 = OpLabel + %62 = OpLoad %24 %56 + %63 = OpSLessThan %54 %62 %53 + OpBranchConditional %63 %58 %59 + %58 = OpLabel + %64 = OpLoad %25 %27 + %65 = OpLoad %24 %46 + %66 = OpLoad %24 %56 + %67 = OpCompositeConstruct %32 %65 %66 + %68 = OpImageRead %38 %64 %67 + %69 = OpCompositeExtract %24 %68 0 + %70 = OpLoad %24 %44 + %71 = OpIMul %24 %70 %69 + OpStore %44 %71 + OpBranch %60 + %60 = OpLabel + %72 = OpLoad %24 %56 + %74 = OpIAdd %24 %72 %73 + OpStore %56 %74 + OpBranch %57 + %59 = OpLabel + OpBranch %50 + %50 = OpLabel + %75 = OpLoad %24 %46 + %76 = OpIAdd %24 %75 %73 + OpStore %46 %76 + OpBranch %47 + %49 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + %83 = OpLoad %24 %44 + %85 = OpAccessChain %84 %82 %53 + OpStore %85 %83 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %101 = OpVariable %43 Function + %92 = OpAccessChain %91 %90 %73 + %93 = OpLoad %87 %92 + %95 = OpAccessChain %94 %82 %45 + %96 = OpLoad %77 %95 + %97 = OpConvertUToF %86 %96 + %98 = OpMatrixTimesVector %86 %93 %97 + %99 = OpConvertFToU %77 %98 + %100 = OpAccessChain %94 %82 %45 + OpStore %100 %99 + OpStore %101 %45 + OpBranch %102 + %102 = OpLabel + OpLoopMerge %104 %105 None + OpBranch %106 + %106 = OpLabel + %107 = OpLoad %24 %101 + %109 = OpSLessThan %54 %107 %108 + OpBranchConditional %109 %103 %104 + %103 = OpLabel + %111 = OpAccessChain %110 %82 %73 + %112 = OpLoad %79 %111 + %114 = OpAccessChain %113 %90 %53 + %115 = OpLoad %78 %114 + %116 = OpVectorTimesScalar %79 %112 %115 + %117 = OpConvertFToU %13 %116 + %118 = OpCompositeExtract %10 %117 0 + %119 = OpCompositeExtract %10 %117 1 + %120 = OpCompositeExtract %10 %117 2 + %121 = OpCompositeConstruct %77 %118 %119 %120 %16 + %122 = OpAccessChain %94 %82 %45 + %123 = OpLoad %77 %122 + %124 = OpIAdd %77 %123 %121 + %125 = OpAccessChain %94 %82 %45 + OpStore %125 %124 + OpBranch %105 + %105 = OpLabel + %126 = OpLoad %24 %101 + %127 = OpIAdd %24 %126 %73 + OpStore %101 %127 + OpBranch %102 + %104 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + %129 = OpLoad %25 %128 + %131 = OpImageRead %38 %129 %130 + %132 = OpCompositeExtract %24 %131 0 + %133 = OpConvertSToF %78 %132 + %134 = OpCompositeConstruct %79 %133 %133 %133 + %135 = OpAccessChain %110 %82 %73 + OpStore %135 %134 + OpReturn + OpFunctionEnd + diff --git a/test/diff/diff_files/large_functions_small_diffs_autogen.cpp b/test/diff/diff_files/large_functions_small_diffs_autogen.cpp new file mode 100644 index 00000000..02838d9a --- /dev/null +++ b/test/diff/diff_files/large_functions_small_diffs_autogen.cpp @@ -0,0 +1,1364 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Test where src and dst have a few large functions with small differences. +constexpr char kSrc[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" %15 + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "f1(" + OpName %8 "f2(" + OpName %12 "x" + OpName %15 "gl_LocalInvocationID" + OpName %20 "y" + OpName %27 "image" + OpName %44 "sum" + OpName %46 "i" + OpName %56 "j" + OpName %80 "BufferOut" + OpMemberName %80 0 "o_uv4" + OpMemberName %80 1 "o_v3" + OpMemberName %80 2 "o_i" + OpName %82 "" + OpName %88 "BufferIn" + OpMemberName %88 0 "i_u" + OpMemberName %88 1 "i_v4" + OpMemberName %88 2 "i_f" + OpName %90 "" + OpName %101 "i" + OpDecorate %15 BuiltIn LocalInvocationId + OpDecorate %27 DescriptorSet 0 + OpDecorate %27 Binding 2 + OpMemberDecorate %80 0 Offset 0 + OpMemberDecorate %80 1 Offset 16 + OpMemberDecorate %80 2 Offset 28 + OpDecorate %80 BufferBlock + OpDecorate %82 DescriptorSet 0 + OpDecorate %82 Binding 1 + OpMemberDecorate %88 0 Offset 0 + OpMemberDecorate %88 1 RowMajor + OpMemberDecorate %88 1 Offset 16 + OpMemberDecorate %88 1 MatrixStride 16 + OpMemberDecorate %88 2 Offset 80 + OpDecorate %88 Block + OpDecorate %90 DescriptorSet 0 + OpDecorate %90 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 0 + %11 = OpTypePointer Function %10 + %13 = OpTypeVector %10 3 + %14 = OpTypePointer Input %13 + %15 = OpVariable %14 Input + %16 = OpConstant %10 0 + %17 = OpTypePointer Input %10 + %21 = OpConstant %10 1 + %24 = OpTypeInt 32 1 + %25 = OpTypeImage %24 2D 0 0 0 2 R32i + %26 = OpTypePointer UniformConstant %25 + %27 = OpVariable %26 UniformConstant + %29 = OpTypeVector %10 2 + %32 = OpTypeVector %24 2 + %38 = OpTypeVector %24 4 + %40 = OpConstant %10 2 + %41 = OpConstant %10 3400 + %42 = OpConstant %10 264 + %43 = OpTypePointer Function %24 + %45 = OpConstant %24 0 + %53 = OpConstant %24 2 + %54 = OpTypeBool + %73 = OpConstant %24 1 + %77 = OpTypeVector %10 4 + %78 = OpTypeFloat 32 + %79 = OpTypeVector %78 3 + %80 = OpTypeStruct %77 %79 %24 + %81 = OpTypePointer Uniform %80 + %82 = OpVariable %81 Uniform + %84 = OpTypePointer Uniform %24 + %86 = OpTypeVector %78 4 + %87 = OpTypeMatrix %86 4 + %88 = OpTypeStruct %10 %87 %78 + %89 = OpTypePointer Uniform %88 + %90 = OpVariable %89 Uniform + %91 = OpTypePointer Uniform %87 + %94 = OpTypePointer Uniform %77 + %108 = OpConstant %24 3 + %110 = OpTypePointer Uniform %79 + %113 = OpTypePointer Uniform %78 + %129 = OpConstantComposite %32 %45 %45 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %135 = OpFunctionCall %2 %6 + %136 = OpFunctionCall %2 %8 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %12 = OpVariable %11 Function + %20 = OpVariable %11 Function + %44 = OpVariable %43 Function + %46 = OpVariable %43 Function + %56 = OpVariable %43 Function + %18 = OpAccessChain %17 %15 %16 + %19 = OpLoad %10 %18 + OpStore %12 %19 + %22 = OpAccessChain %17 %15 %21 + %23 = OpLoad %10 %22 + OpStore %20 %23 + %28 = OpLoad %25 %27 + %30 = OpLoad %13 %15 + %31 = OpVectorShuffle %29 %30 %30 0 1 + %33 = OpBitcast %32 %31 + %34 = OpLoad %10 %12 + %35 = OpLoad %10 %20 + %36 = OpIAdd %10 %34 %35 + %37 = OpBitcast %24 %36 + %39 = OpCompositeConstruct %38 %37 %37 %37 %37 + OpImageWrite %28 %33 %39 + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + OpStore %44 %45 + OpStore %46 %45 + OpBranch %47 + %47 = OpLabel + OpLoopMerge %49 %50 None + OpBranch %51 + %51 = OpLabel + %52 = OpLoad %24 %46 + %55 = OpSLessThan %54 %52 %53 + OpBranchConditional %55 %48 %49 + %48 = OpLabel + OpStore %56 %45 + OpBranch %57 + %57 = OpLabel + OpLoopMerge %59 %60 None + OpBranch %61 + %61 = OpLabel + %62 = OpLoad %24 %56 + %63 = OpSLessThan %54 %62 %53 + OpBranchConditional %63 %58 %59 + %58 = OpLabel + %64 = OpLoad %25 %27 + %65 = OpLoad %24 %46 + %66 = OpLoad %24 %56 + %67 = OpCompositeConstruct %32 %65 %66 + %68 = OpImageRead %38 %64 %67 + %69 = OpCompositeExtract %24 %68 0 + %70 = OpLoad %24 %44 + %71 = OpIAdd %24 %70 %69 + OpStore %44 %71 + OpBranch %60 + %60 = OpLabel + %72 = OpLoad %24 %56 + %74 = OpIAdd %24 %72 %73 + OpStore %56 %74 + OpBranch %57 + %59 = OpLabel + OpBranch %50 + %50 = OpLabel + %75 = OpLoad %24 %46 + %76 = OpIAdd %24 %75 %73 + OpStore %46 %76 + OpBranch %47 + %49 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + %83 = OpLoad %24 %44 + %85 = OpAccessChain %84 %82 %53 + OpStore %85 %83 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %101 = OpVariable %43 Function + %92 = OpAccessChain %91 %90 %73 + %93 = OpLoad %87 %92 + %95 = OpAccessChain %94 %82 %45 + %96 = OpLoad %77 %95 + %97 = OpConvertUToF %86 %96 + %98 = OpMatrixTimesVector %86 %93 %97 + %99 = OpConvertFToU %77 %98 + %100 = OpAccessChain %94 %82 %45 + OpStore %100 %99 + OpStore %101 %45 + OpBranch %102 + %102 = OpLabel + OpLoopMerge %104 %105 None + OpBranch %106 + %106 = OpLabel + %107 = OpLoad %24 %101 + %109 = OpSLessThan %54 %107 %108 + OpBranchConditional %109 %103 %104 + %103 = OpLabel + %111 = OpAccessChain %110 %82 %73 + %112 = OpLoad %79 %111 + %114 = OpAccessChain %113 %90 %53 + %115 = OpLoad %78 %114 + %116 = OpVectorTimesScalar %79 %112 %115 + %117 = OpConvertFToU %13 %116 + %118 = OpCompositeExtract %10 %117 0 + %119 = OpCompositeExtract %10 %117 1 + %120 = OpCompositeExtract %10 %117 2 + %121 = OpCompositeConstruct %77 %118 %119 %120 %16 + %122 = OpAccessChain %94 %82 %45 + %123 = OpLoad %77 %122 + %124 = OpIAdd %77 %123 %121 + %125 = OpAccessChain %94 %82 %45 + OpStore %125 %124 + OpBranch %105 + %105 = OpLabel + %126 = OpLoad %24 %101 + %127 = OpIAdd %24 %126 %73 + OpStore %101 %127 + OpBranch %102 + %104 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + %128 = OpLoad %25 %27 + %130 = OpImageRead %38 %128 %129 + %131 = OpCompositeExtract %24 %130 0 + %132 = OpConvertSToF %78 %131 + %133 = OpCompositeConstruct %79 %132 %132 %132 + %134 = OpAccessChain %110 %82 %73 + OpStore %134 %133 + OpReturn + OpFunctionEnd +)"; +constexpr char kDst[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" %15 + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "f1(" + OpName %8 "f2(" + OpName %12 "x" + OpName %15 "gl_LocalInvocationID" + OpName %20 "y" + OpName %27 "image" + OpName %44 "sum" + OpName %46 "i" + OpName %56 "j" + OpName %80 "BufferOut" + OpMemberName %80 0 "o_uv4" + OpMemberName %80 1 "o_v3" + OpMemberName %80 2 "o_i" + OpName %82 "" + OpName %88 "BufferIn" + OpMemberName %88 0 "i_u" + OpMemberName %88 1 "i_v4" + OpMemberName %88 2 "i_f" + OpName %90 "" + OpName %101 "i" + OpName %128 "image2" + OpDecorate %15 BuiltIn LocalInvocationId + OpDecorate %27 DescriptorSet 0 + OpDecorate %27 Binding 2 + OpMemberDecorate %80 0 Offset 0 + OpMemberDecorate %80 1 Offset 16 + OpMemberDecorate %80 2 Offset 28 + OpDecorate %80 BufferBlock + OpDecorate %82 DescriptorSet 0 + OpDecorate %82 Binding 1 + OpMemberDecorate %88 0 Offset 0 + OpMemberDecorate %88 1 RowMajor + OpMemberDecorate %88 1 Offset 16 + OpMemberDecorate %88 1 MatrixStride 16 + OpMemberDecorate %88 2 Offset 80 + OpDecorate %88 Block + OpDecorate %90 DescriptorSet 0 + OpDecorate %90 Binding 0 + OpDecorate %128 DescriptorSet 0 + OpDecorate %128 Binding 3 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 0 + %11 = OpTypePointer Function %10 + %13 = OpTypeVector %10 3 + %14 = OpTypePointer Input %13 + %15 = OpVariable %14 Input + %16 = OpConstant %10 0 + %17 = OpTypePointer Input %10 + %21 = OpConstant %10 1 + %24 = OpTypeInt 32 1 + %25 = OpTypeImage %24 2D 0 0 0 2 R32i + %26 = OpTypePointer UniformConstant %25 + %27 = OpVariable %26 UniformConstant + %29 = OpTypeVector %10 2 + %32 = OpTypeVector %24 2 + %38 = OpTypeVector %24 4 + %40 = OpConstant %10 2 + %41 = OpConstant %10 3400 + %42 = OpConstant %10 264 + %43 = OpTypePointer Function %24 + %45 = OpConstant %24 0 + %53 = OpConstant %24 2 + %54 = OpTypeBool + %73 = OpConstant %24 1 + %77 = OpTypeVector %10 4 + %78 = OpTypeFloat 32 + %79 = OpTypeVector %78 3 + %80 = OpTypeStruct %77 %79 %24 + %81 = OpTypePointer Uniform %80 + %82 = OpVariable %81 Uniform + %84 = OpTypePointer Uniform %24 + %86 = OpTypeVector %78 4 + %87 = OpTypeMatrix %86 4 + %88 = OpTypeStruct %10 %87 %78 + %89 = OpTypePointer Uniform %88 + %90 = OpVariable %89 Uniform + %91 = OpTypePointer Uniform %87 + %94 = OpTypePointer Uniform %77 + %108 = OpConstant %24 3 + %110 = OpTypePointer Uniform %79 + %113 = OpTypePointer Uniform %78 + %128 = OpVariable %26 UniformConstant + %130 = OpConstantComposite %32 %45 %45 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %136 = OpFunctionCall %2 %6 + %137 = OpFunctionCall %2 %8 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %12 = OpVariable %11 Function + %20 = OpVariable %11 Function + %44 = OpVariable %43 Function + %46 = OpVariable %43 Function + %56 = OpVariable %43 Function + %18 = OpAccessChain %17 %15 %16 + %19 = OpLoad %10 %18 + OpStore %12 %19 + %22 = OpAccessChain %17 %15 %21 + %23 = OpLoad %10 %22 + OpStore %20 %23 + %28 = OpLoad %25 %27 + %30 = OpLoad %13 %15 + %31 = OpVectorShuffle %29 %30 %30 0 1 + %33 = OpBitcast %32 %31 + %34 = OpLoad %10 %12 + %35 = OpLoad %10 %20 + %36 = OpIAdd %10 %34 %35 + %37 = OpBitcast %24 %36 + %39 = OpCompositeConstruct %38 %37 %37 %37 %37 + OpImageWrite %28 %33 %39 + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + OpStore %44 %45 + OpStore %46 %45 + OpBranch %47 + %47 = OpLabel + OpLoopMerge %49 %50 None + OpBranch %51 + %51 = OpLabel + %52 = OpLoad %24 %46 + %55 = OpSLessThan %54 %52 %53 + OpBranchConditional %55 %48 %49 + %48 = OpLabel + OpStore %56 %45 + OpBranch %57 + %57 = OpLabel + OpLoopMerge %59 %60 None + OpBranch %61 + %61 = OpLabel + %62 = OpLoad %24 %56 + %63 = OpSLessThan %54 %62 %53 + OpBranchConditional %63 %58 %59 + %58 = OpLabel + %64 = OpLoad %25 %27 + %65 = OpLoad %24 %46 + %66 = OpLoad %24 %56 + %67 = OpCompositeConstruct %32 %65 %66 + %68 = OpImageRead %38 %64 %67 + %69 = OpCompositeExtract %24 %68 0 + %70 = OpLoad %24 %44 + %71 = OpIMul %24 %70 %69 + OpStore %44 %71 + OpBranch %60 + %60 = OpLabel + %72 = OpLoad %24 %56 + %74 = OpIAdd %24 %72 %73 + OpStore %56 %74 + OpBranch %57 + %59 = OpLabel + OpBranch %50 + %50 = OpLabel + %75 = OpLoad %24 %46 + %76 = OpIAdd %24 %75 %73 + OpStore %46 %76 + OpBranch %47 + %49 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + %83 = OpLoad %24 %44 + %85 = OpAccessChain %84 %82 %53 + OpStore %85 %83 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %101 = OpVariable %43 Function + %92 = OpAccessChain %91 %90 %73 + %93 = OpLoad %87 %92 + %95 = OpAccessChain %94 %82 %45 + %96 = OpLoad %77 %95 + %97 = OpConvertUToF %86 %96 + %98 = OpMatrixTimesVector %86 %93 %97 + %99 = OpConvertFToU %77 %98 + %100 = OpAccessChain %94 %82 %45 + OpStore %100 %99 + OpStore %101 %45 + OpBranch %102 + %102 = OpLabel + OpLoopMerge %104 %105 None + OpBranch %106 + %106 = OpLabel + %107 = OpLoad %24 %101 + %109 = OpSLessThan %54 %107 %108 + OpBranchConditional %109 %103 %104 + %103 = OpLabel + %111 = OpAccessChain %110 %82 %73 + %112 = OpLoad %79 %111 + %114 = OpAccessChain %113 %90 %53 + %115 = OpLoad %78 %114 + %116 = OpVectorTimesScalar %79 %112 %115 + %117 = OpConvertFToU %13 %116 + %118 = OpCompositeExtract %10 %117 0 + %119 = OpCompositeExtract %10 %117 1 + %120 = OpCompositeExtract %10 %117 2 + %121 = OpCompositeConstruct %77 %118 %119 %120 %16 + %122 = OpAccessChain %94 %82 %45 + %123 = OpLoad %77 %122 + %124 = OpIAdd %77 %123 %121 + %125 = OpAccessChain %94 %82 %45 + OpStore %125 %124 + OpBranch %105 + %105 = OpLabel + %126 = OpLoad %24 %101 + %127 = OpIAdd %24 %126 %73 + OpStore %101 %127 + OpBranch %102 + %104 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + %129 = OpLoad %25 %128 + %131 = OpImageRead %38 %129 %130 + %132 = OpCompositeExtract %24 %131 0 + %133 = OpConvertSToF %78 %132 + %134 = OpCompositeConstruct %79 %133 %133 %133 + %135 = OpAccessChain %110 %82 %73 + OpStore %135 %134 + OpReturn + OpFunctionEnd + +)"; + +TEST(DiffTest, LargeFunctionsSmallDiffs) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 137 ++; Bound: 140 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" %15 + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "f1(" + OpName %8 "f2(" + OpName %12 "x" + OpName %15 "gl_LocalInvocationID" + OpName %20 "y" + OpName %27 "image" + OpName %44 "sum" + OpName %46 "i" + OpName %56 "j" + OpName %80 "BufferOut" + OpMemberName %80 0 "o_uv4" + OpMemberName %80 1 "o_v3" + OpMemberName %80 2 "o_i" + OpName %82 "" + OpName %88 "BufferIn" + OpMemberName %88 0 "i_u" + OpMemberName %88 1 "i_v4" + OpMemberName %88 2 "i_f" + OpName %90 "" + OpName %101 "i" ++OpName %138 "image2" + OpDecorate %15 BuiltIn LocalInvocationId + OpDecorate %27 DescriptorSet 0 + OpDecorate %27 Binding 2 + OpMemberDecorate %80 0 Offset 0 + OpMemberDecorate %80 1 Offset 16 + OpMemberDecorate %80 2 Offset 28 + OpDecorate %80 BufferBlock + OpDecorate %82 DescriptorSet 0 + OpDecorate %82 Binding 1 + OpMemberDecorate %88 0 Offset 0 + OpMemberDecorate %88 1 RowMajor + OpMemberDecorate %88 1 Offset 16 + OpMemberDecorate %88 1 MatrixStride 16 + OpMemberDecorate %88 2 Offset 80 + OpDecorate %88 Block + OpDecorate %90 DescriptorSet 0 + OpDecorate %90 Binding 0 ++OpDecorate %138 DescriptorSet 0 ++OpDecorate %138 Binding 3 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 0 + %11 = OpTypePointer Function %10 + %13 = OpTypeVector %10 3 + %14 = OpTypePointer Input %13 + %15 = OpVariable %14 Input + %16 = OpConstant %10 0 + %17 = OpTypePointer Input %10 + %21 = OpConstant %10 1 + %24 = OpTypeInt 32 1 + %25 = OpTypeImage %24 2D 0 0 0 2 R32i + %26 = OpTypePointer UniformConstant %25 + %27 = OpVariable %26 UniformConstant + %29 = OpTypeVector %10 2 + %32 = OpTypeVector %24 2 + %38 = OpTypeVector %24 4 + %40 = OpConstant %10 2 + %41 = OpConstant %10 3400 + %42 = OpConstant %10 264 + %43 = OpTypePointer Function %24 + %45 = OpConstant %24 0 + %53 = OpConstant %24 2 + %54 = OpTypeBool + %73 = OpConstant %24 1 + %77 = OpTypeVector %10 4 + %78 = OpTypeFloat 32 + %79 = OpTypeVector %78 3 + %80 = OpTypeStruct %77 %79 %24 + %81 = OpTypePointer Uniform %80 + %82 = OpVariable %81 Uniform + %84 = OpTypePointer Uniform %24 + %86 = OpTypeVector %78 4 + %87 = OpTypeMatrix %86 4 + %88 = OpTypeStruct %10 %87 %78 + %89 = OpTypePointer Uniform %88 + %90 = OpVariable %89 Uniform + %91 = OpTypePointer Uniform %87 + %94 = OpTypePointer Uniform %77 + %108 = OpConstant %24 3 + %110 = OpTypePointer Uniform %79 + %113 = OpTypePointer Uniform %78 ++%138 = OpVariable %26 UniformConstant + %129 = OpConstantComposite %32 %45 %45 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %135 = OpFunctionCall %2 %6 + %136 = OpFunctionCall %2 %8 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %12 = OpVariable %11 Function + %20 = OpVariable %11 Function + %44 = OpVariable %43 Function + %46 = OpVariable %43 Function + %56 = OpVariable %43 Function + %18 = OpAccessChain %17 %15 %16 + %19 = OpLoad %10 %18 + OpStore %12 %19 + %22 = OpAccessChain %17 %15 %21 + %23 = OpLoad %10 %22 + OpStore %20 %23 + %28 = OpLoad %25 %27 + %30 = OpLoad %13 %15 + %31 = OpVectorShuffle %29 %30 %30 0 1 + %33 = OpBitcast %32 %31 + %34 = OpLoad %10 %12 + %35 = OpLoad %10 %20 + %36 = OpIAdd %10 %34 %35 + %37 = OpBitcast %24 %36 + %39 = OpCompositeConstruct %38 %37 %37 %37 %37 + OpImageWrite %28 %33 %39 + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + OpStore %44 %45 + OpStore %46 %45 + OpBranch %47 + %47 = OpLabel + OpLoopMerge %49 %50 None + OpBranch %51 + %51 = OpLabel + %52 = OpLoad %24 %46 + %55 = OpSLessThan %54 %52 %53 + OpBranchConditional %55 %48 %49 + %48 = OpLabel + OpStore %56 %45 + OpBranch %57 + %57 = OpLabel + OpLoopMerge %59 %60 None + OpBranch %61 + %61 = OpLabel + %62 = OpLoad %24 %56 + %63 = OpSLessThan %54 %62 %53 + OpBranchConditional %63 %58 %59 + %58 = OpLabel + %64 = OpLoad %25 %27 + %65 = OpLoad %24 %46 + %66 = OpLoad %24 %56 + %67 = OpCompositeConstruct %32 %65 %66 + %68 = OpImageRead %38 %64 %67 + %69 = OpCompositeExtract %24 %68 0 + %70 = OpLoad %24 %44 +-%71 = OpIAdd %24 %70 %69 ++%137 = OpIMul %24 %70 %69 +-OpStore %44 %71 ++OpStore %44 %137 + OpBranch %60 + %60 = OpLabel + %72 = OpLoad %24 %56 + %74 = OpIAdd %24 %72 %73 + OpStore %56 %74 + OpBranch %57 + %59 = OpLabel + OpBranch %50 + %50 = OpLabel + %75 = OpLoad %24 %46 + %76 = OpIAdd %24 %75 %73 + OpStore %46 %76 + OpBranch %47 + %49 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + %83 = OpLoad %24 %44 + %85 = OpAccessChain %84 %82 %53 + OpStore %85 %83 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %101 = OpVariable %43 Function + %92 = OpAccessChain %91 %90 %73 + %93 = OpLoad %87 %92 + %95 = OpAccessChain %94 %82 %45 + %96 = OpLoad %77 %95 + %97 = OpConvertUToF %86 %96 + %98 = OpMatrixTimesVector %86 %93 %97 + %99 = OpConvertFToU %77 %98 + %100 = OpAccessChain %94 %82 %45 + OpStore %100 %99 + OpStore %101 %45 + OpBranch %102 + %102 = OpLabel + OpLoopMerge %104 %105 None + OpBranch %106 + %106 = OpLabel + %107 = OpLoad %24 %101 + %109 = OpSLessThan %54 %107 %108 + OpBranchConditional %109 %103 %104 + %103 = OpLabel + %111 = OpAccessChain %110 %82 %73 + %112 = OpLoad %79 %111 + %114 = OpAccessChain %113 %90 %53 + %115 = OpLoad %78 %114 + %116 = OpVectorTimesScalar %79 %112 %115 + %117 = OpConvertFToU %13 %116 + %118 = OpCompositeExtract %10 %117 0 + %119 = OpCompositeExtract %10 %117 1 + %120 = OpCompositeExtract %10 %117 2 + %121 = OpCompositeConstruct %77 %118 %119 %120 %16 + %122 = OpAccessChain %94 %82 %45 + %123 = OpLoad %77 %122 + %124 = OpIAdd %77 %123 %121 + %125 = OpAccessChain %94 %82 %45 + OpStore %125 %124 + OpBranch %105 + %105 = OpLabel + %126 = OpLoad %24 %101 + %127 = OpIAdd %24 %126 %73 + OpStore %101 %127 + OpBranch %102 + %104 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 +-%128 = OpLoad %25 %27 ++%139 = OpLoad %25 %138 +-%130 = OpImageRead %38 %128 %129 ++%130 = OpImageRead %38 %139 %129 + %131 = OpCompositeExtract %24 %130 0 + %132 = OpConvertSToF %78 %131 + %133 = OpCompositeConstruct %79 %132 %132 %132 + %134 = OpAccessChain %110 %82 %73 + OpStore %134 %133 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, LargeFunctionsSmallDiffsNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" %15 + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpDecorate %15 BuiltIn LocalInvocationId + OpDecorate %27 DescriptorSet 0 + OpDecorate %27 Binding 2 + OpMemberDecorate %80 0 Offset 0 + OpMemberDecorate %80 1 Offset 16 + OpMemberDecorate %80 2 Offset 28 + OpDecorate %80 BufferBlock + OpDecorate %82 DescriptorSet 0 + OpDecorate %82 Binding 1 + OpMemberDecorate %88 0 Offset 0 + OpMemberDecorate %88 1 RowMajor + OpMemberDecorate %88 1 Offset 16 + OpMemberDecorate %88 1 MatrixStride 16 + OpMemberDecorate %88 2 Offset 80 + OpDecorate %88 Block + OpDecorate %90 DescriptorSet 0 + OpDecorate %90 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 0 + %11 = OpTypePointer Function %10 + %13 = OpTypeVector %10 3 + %14 = OpTypePointer Input %13 + %15 = OpVariable %14 Input + %16 = OpConstant %10 0 + %17 = OpTypePointer Input %10 + %21 = OpConstant %10 1 + %24 = OpTypeInt 32 1 + %25 = OpTypeImage %24 2D 0 0 0 2 R32i + %26 = OpTypePointer UniformConstant %25 + %27 = OpVariable %26 UniformConstant + %29 = OpTypeVector %10 2 + %32 = OpTypeVector %24 2 + %38 = OpTypeVector %24 4 + %40 = OpConstant %10 2 + %41 = OpConstant %10 3400 + %42 = OpConstant %10 264 + %43 = OpTypePointer Function %24 + %45 = OpConstant %24 0 + %53 = OpConstant %24 2 + %54 = OpTypeBool + %73 = OpConstant %24 1 + %77 = OpTypeVector %10 4 + %78 = OpTypeFloat 32 + %79 = OpTypeVector %78 3 + %80 = OpTypeStruct %77 %79 %24 + %81 = OpTypePointer Uniform %80 + %82 = OpVariable %81 Uniform + %84 = OpTypePointer Uniform %24 + %86 = OpTypeVector %78 4 + %87 = OpTypeMatrix %86 4 + %88 = OpTypeStruct %10 %87 %78 + %89 = OpTypePointer Uniform %88 + %90 = OpVariable %89 Uniform + %91 = OpTypePointer Uniform %87 + %94 = OpTypePointer Uniform %77 + %108 = OpConstant %24 3 + %110 = OpTypePointer Uniform %79 + %113 = OpTypePointer Uniform %78 + %129 = OpConstantComposite %32 %45 %45 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %135 = OpFunctionCall %2 %6 + %136 = OpFunctionCall %2 %8 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %12 = OpVariable %11 Function + %20 = OpVariable %11 Function + %44 = OpVariable %43 Function + %46 = OpVariable %43 Function + %56 = OpVariable %43 Function + %18 = OpAccessChain %17 %15 %16 + %19 = OpLoad %10 %18 + OpStore %12 %19 + %22 = OpAccessChain %17 %15 %21 + %23 = OpLoad %10 %22 + OpStore %20 %23 + %28 = OpLoad %25 %27 + %30 = OpLoad %13 %15 + %31 = OpVectorShuffle %29 %30 %30 0 1 + %33 = OpBitcast %32 %31 + %34 = OpLoad %10 %12 + %35 = OpLoad %10 %20 + %36 = OpIAdd %10 %34 %35 + %37 = OpBitcast %24 %36 + %39 = OpCompositeConstruct %38 %37 %37 %37 %37 + OpImageWrite %28 %33 %39 + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + OpStore %44 %45 + OpStore %46 %45 + OpBranch %47 + %47 = OpLabel + OpLoopMerge %49 %50 None + OpBranch %51 + %51 = OpLabel + %52 = OpLoad %24 %46 + %55 = OpSLessThan %54 %52 %53 + OpBranchConditional %55 %48 %49 + %48 = OpLabel + OpStore %56 %45 + OpBranch %57 + %57 = OpLabel + OpLoopMerge %59 %60 None + OpBranch %61 + %61 = OpLabel + %62 = OpLoad %24 %56 + %63 = OpSLessThan %54 %62 %53 + OpBranchConditional %63 %58 %59 + %58 = OpLabel + %64 = OpLoad %25 %27 + %65 = OpLoad %24 %46 + %66 = OpLoad %24 %56 + %67 = OpCompositeConstruct %32 %65 %66 + %68 = OpImageRead %38 %64 %67 + %69 = OpCompositeExtract %24 %68 0 + %70 = OpLoad %24 %44 + %71 = OpIAdd %24 %70 %69 + OpStore %44 %71 + OpBranch %60 + %60 = OpLabel + %72 = OpLoad %24 %56 + %74 = OpIAdd %24 %72 %73 + OpStore %56 %74 + OpBranch %57 + %59 = OpLabel + OpBranch %50 + %50 = OpLabel + %75 = OpLoad %24 %46 + %76 = OpIAdd %24 %75 %73 + OpStore %46 %76 + OpBranch %47 + %49 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + %83 = OpLoad %24 %44 + %85 = OpAccessChain %84 %82 %53 + OpStore %85 %83 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %101 = OpVariable %43 Function + %92 = OpAccessChain %91 %90 %73 + %93 = OpLoad %87 %92 + %95 = OpAccessChain %94 %82 %45 + %96 = OpLoad %77 %95 + %97 = OpConvertUToF %86 %96 + %98 = OpMatrixTimesVector %86 %93 %97 + %99 = OpConvertFToU %77 %98 + %100 = OpAccessChain %94 %82 %45 + OpStore %100 %99 + OpStore %101 %45 + OpBranch %102 + %102 = OpLabel + OpLoopMerge %104 %105 None + OpBranch %106 + %106 = OpLabel + %107 = OpLoad %24 %101 + %109 = OpSLessThan %54 %107 %108 + OpBranchConditional %109 %103 %104 + %103 = OpLabel + %111 = OpAccessChain %110 %82 %73 + %112 = OpLoad %79 %111 + %114 = OpAccessChain %113 %90 %53 + %115 = OpLoad %78 %114 + %116 = OpVectorTimesScalar %79 %112 %115 + %117 = OpConvertFToU %13 %116 + %118 = OpCompositeExtract %10 %117 0 + %119 = OpCompositeExtract %10 %117 1 + %120 = OpCompositeExtract %10 %117 2 + %121 = OpCompositeConstruct %77 %118 %119 %120 %16 + %122 = OpAccessChain %94 %82 %45 + %123 = OpLoad %77 %122 + %124 = OpIAdd %77 %123 %121 + %125 = OpAccessChain %94 %82 %45 + OpStore %125 %124 + OpBranch %105 + %105 = OpLabel + %126 = OpLoad %24 %101 + %127 = OpIAdd %24 %126 %73 + OpStore %101 %127 + OpBranch %102 + %104 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + %128 = OpLoad %25 %27 + %130 = OpImageRead %38 %128 %129 + %131 = OpCompositeExtract %24 %130 0 + %132 = OpConvertSToF %78 %131 + %133 = OpCompositeConstruct %79 %132 %132 %132 + %134 = OpAccessChain %110 %82 %73 + OpStore %134 %133 + OpReturn + OpFunctionEnd + +)"; + constexpr char kDstNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" %15 + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpDecorate %15 BuiltIn LocalInvocationId + OpDecorate %27 DescriptorSet 0 + OpDecorate %27 Binding 2 + OpMemberDecorate %80 0 Offset 0 + OpMemberDecorate %80 1 Offset 16 + OpMemberDecorate %80 2 Offset 28 + OpDecorate %80 BufferBlock + OpDecorate %82 DescriptorSet 0 + OpDecorate %82 Binding 1 + OpMemberDecorate %88 0 Offset 0 + OpMemberDecorate %88 1 RowMajor + OpMemberDecorate %88 1 Offset 16 + OpMemberDecorate %88 1 MatrixStride 16 + OpMemberDecorate %88 2 Offset 80 + OpDecorate %88 Block + OpDecorate %90 DescriptorSet 0 + OpDecorate %90 Binding 0 + OpDecorate %128 DescriptorSet 0 + OpDecorate %128 Binding 3 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 0 + %11 = OpTypePointer Function %10 + %13 = OpTypeVector %10 3 + %14 = OpTypePointer Input %13 + %15 = OpVariable %14 Input + %16 = OpConstant %10 0 + %17 = OpTypePointer Input %10 + %21 = OpConstant %10 1 + %24 = OpTypeInt 32 1 + %25 = OpTypeImage %24 2D 0 0 0 2 R32i + %26 = OpTypePointer UniformConstant %25 + %27 = OpVariable %26 UniformConstant + %29 = OpTypeVector %10 2 + %32 = OpTypeVector %24 2 + %38 = OpTypeVector %24 4 + %40 = OpConstant %10 2 + %41 = OpConstant %10 3400 + %42 = OpConstant %10 264 + %43 = OpTypePointer Function %24 + %45 = OpConstant %24 0 + %53 = OpConstant %24 2 + %54 = OpTypeBool + %73 = OpConstant %24 1 + %77 = OpTypeVector %10 4 + %78 = OpTypeFloat 32 + %79 = OpTypeVector %78 3 + %80 = OpTypeStruct %77 %79 %24 + %81 = OpTypePointer Uniform %80 + %82 = OpVariable %81 Uniform + %84 = OpTypePointer Uniform %24 + %86 = OpTypeVector %78 4 + %87 = OpTypeMatrix %86 4 + %88 = OpTypeStruct %10 %87 %78 + %89 = OpTypePointer Uniform %88 + %90 = OpVariable %89 Uniform + %91 = OpTypePointer Uniform %87 + %94 = OpTypePointer Uniform %77 + %108 = OpConstant %24 3 + %110 = OpTypePointer Uniform %79 + %113 = OpTypePointer Uniform %78 + %128 = OpVariable %26 UniformConstant + %130 = OpConstantComposite %32 %45 %45 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %136 = OpFunctionCall %2 %6 + %137 = OpFunctionCall %2 %8 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %12 = OpVariable %11 Function + %20 = OpVariable %11 Function + %44 = OpVariable %43 Function + %46 = OpVariable %43 Function + %56 = OpVariable %43 Function + %18 = OpAccessChain %17 %15 %16 + %19 = OpLoad %10 %18 + OpStore %12 %19 + %22 = OpAccessChain %17 %15 %21 + %23 = OpLoad %10 %22 + OpStore %20 %23 + %28 = OpLoad %25 %27 + %30 = OpLoad %13 %15 + %31 = OpVectorShuffle %29 %30 %30 0 1 + %33 = OpBitcast %32 %31 + %34 = OpLoad %10 %12 + %35 = OpLoad %10 %20 + %36 = OpIAdd %10 %34 %35 + %37 = OpBitcast %24 %36 + %39 = OpCompositeConstruct %38 %37 %37 %37 %37 + OpImageWrite %28 %33 %39 + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + OpStore %44 %45 + OpStore %46 %45 + OpBranch %47 + %47 = OpLabel + OpLoopMerge %49 %50 None + OpBranch %51 + %51 = OpLabel + %52 = OpLoad %24 %46 + %55 = OpSLessThan %54 %52 %53 + OpBranchConditional %55 %48 %49 + %48 = OpLabel + OpStore %56 %45 + OpBranch %57 + %57 = OpLabel + OpLoopMerge %59 %60 None + OpBranch %61 + %61 = OpLabel + %62 = OpLoad %24 %56 + %63 = OpSLessThan %54 %62 %53 + OpBranchConditional %63 %58 %59 + %58 = OpLabel + %64 = OpLoad %25 %27 + %65 = OpLoad %24 %46 + %66 = OpLoad %24 %56 + %67 = OpCompositeConstruct %32 %65 %66 + %68 = OpImageRead %38 %64 %67 + %69 = OpCompositeExtract %24 %68 0 + %70 = OpLoad %24 %44 + %71 = OpIMul %24 %70 %69 + OpStore %44 %71 + OpBranch %60 + %60 = OpLabel + %72 = OpLoad %24 %56 + %74 = OpIAdd %24 %72 %73 + OpStore %56 %74 + OpBranch %57 + %59 = OpLabel + OpBranch %50 + %50 = OpLabel + %75 = OpLoad %24 %46 + %76 = OpIAdd %24 %75 %73 + OpStore %46 %76 + OpBranch %47 + %49 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + %83 = OpLoad %24 %44 + %85 = OpAccessChain %84 %82 %53 + OpStore %85 %83 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %101 = OpVariable %43 Function + %92 = OpAccessChain %91 %90 %73 + %93 = OpLoad %87 %92 + %95 = OpAccessChain %94 %82 %45 + %96 = OpLoad %77 %95 + %97 = OpConvertUToF %86 %96 + %98 = OpMatrixTimesVector %86 %93 %97 + %99 = OpConvertFToU %77 %98 + %100 = OpAccessChain %94 %82 %45 + OpStore %100 %99 + OpStore %101 %45 + OpBranch %102 + %102 = OpLabel + OpLoopMerge %104 %105 None + OpBranch %106 + %106 = OpLabel + %107 = OpLoad %24 %101 + %109 = OpSLessThan %54 %107 %108 + OpBranchConditional %109 %103 %104 + %103 = OpLabel + %111 = OpAccessChain %110 %82 %73 + %112 = OpLoad %79 %111 + %114 = OpAccessChain %113 %90 %53 + %115 = OpLoad %78 %114 + %116 = OpVectorTimesScalar %79 %112 %115 + %117 = OpConvertFToU %13 %116 + %118 = OpCompositeExtract %10 %117 0 + %119 = OpCompositeExtract %10 %117 1 + %120 = OpCompositeExtract %10 %117 2 + %121 = OpCompositeConstruct %77 %118 %119 %120 %16 + %122 = OpAccessChain %94 %82 %45 + %123 = OpLoad %77 %122 + %124 = OpIAdd %77 %123 %121 + %125 = OpAccessChain %94 %82 %45 + OpStore %125 %124 + OpBranch %105 + %105 = OpLabel + %126 = OpLoad %24 %101 + %127 = OpIAdd %24 %126 %73 + OpStore %101 %127 + OpBranch %102 + %104 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + %129 = OpLoad %25 %128 + %131 = OpImageRead %38 %129 %130 + %132 = OpCompositeExtract %24 %131 0 + %133 = OpConvertSToF %78 %132 + %134 = OpCompositeConstruct %79 %133 %133 %133 + %135 = OpAccessChain %110 %82 %73 + OpStore %135 %134 + OpReturn + OpFunctionEnd + +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 137 ++; Bound: 140 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" %15 + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpDecorate %15 BuiltIn LocalInvocationId + OpDecorate %27 DescriptorSet 0 + OpDecorate %27 Binding 2 + OpMemberDecorate %80 0 Offset 0 + OpMemberDecorate %80 1 Offset 16 + OpMemberDecorate %80 2 Offset 28 + OpDecorate %80 BufferBlock + OpDecorate %82 DescriptorSet 0 + OpDecorate %82 Binding 1 + OpMemberDecorate %88 0 Offset 0 + OpMemberDecorate %88 1 RowMajor + OpMemberDecorate %88 1 Offset 16 + OpMemberDecorate %88 1 MatrixStride 16 + OpMemberDecorate %88 2 Offset 80 + OpDecorate %88 Block + OpDecorate %90 DescriptorSet 0 + OpDecorate %90 Binding 0 ++OpDecorate %138 DescriptorSet 0 ++OpDecorate %138 Binding 3 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 0 + %11 = OpTypePointer Function %10 + %13 = OpTypeVector %10 3 + %14 = OpTypePointer Input %13 + %15 = OpVariable %14 Input + %16 = OpConstant %10 0 + %17 = OpTypePointer Input %10 + %21 = OpConstant %10 1 + %24 = OpTypeInt 32 1 + %25 = OpTypeImage %24 2D 0 0 0 2 R32i + %26 = OpTypePointer UniformConstant %25 + %27 = OpVariable %26 UniformConstant + %29 = OpTypeVector %10 2 + %32 = OpTypeVector %24 2 + %38 = OpTypeVector %24 4 + %40 = OpConstant %10 2 + %41 = OpConstant %10 3400 + %42 = OpConstant %10 264 + %43 = OpTypePointer Function %24 + %45 = OpConstant %24 0 + %53 = OpConstant %24 2 + %54 = OpTypeBool + %73 = OpConstant %24 1 + %77 = OpTypeVector %10 4 + %78 = OpTypeFloat 32 + %79 = OpTypeVector %78 3 + %80 = OpTypeStruct %77 %79 %24 + %81 = OpTypePointer Uniform %80 + %82 = OpVariable %81 Uniform + %84 = OpTypePointer Uniform %24 + %86 = OpTypeVector %78 4 + %87 = OpTypeMatrix %86 4 + %88 = OpTypeStruct %10 %87 %78 + %89 = OpTypePointer Uniform %88 + %90 = OpVariable %89 Uniform + %91 = OpTypePointer Uniform %87 + %94 = OpTypePointer Uniform %77 + %108 = OpConstant %24 3 + %110 = OpTypePointer Uniform %79 + %113 = OpTypePointer Uniform %78 ++%138 = OpVariable %26 UniformConstant + %129 = OpConstantComposite %32 %45 %45 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %135 = OpFunctionCall %2 %6 + %136 = OpFunctionCall %2 %8 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %12 = OpVariable %11 Function + %20 = OpVariable %11 Function + %44 = OpVariable %43 Function + %46 = OpVariable %43 Function + %56 = OpVariable %43 Function + %18 = OpAccessChain %17 %15 %16 + %19 = OpLoad %10 %18 + OpStore %12 %19 + %22 = OpAccessChain %17 %15 %21 + %23 = OpLoad %10 %22 + OpStore %20 %23 + %28 = OpLoad %25 %27 + %30 = OpLoad %13 %15 + %31 = OpVectorShuffle %29 %30 %30 0 1 + %33 = OpBitcast %32 %31 + %34 = OpLoad %10 %12 + %35 = OpLoad %10 %20 + %36 = OpIAdd %10 %34 %35 + %37 = OpBitcast %24 %36 + %39 = OpCompositeConstruct %38 %37 %37 %37 %37 + OpImageWrite %28 %33 %39 + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + OpStore %44 %45 + OpStore %46 %45 + OpBranch %47 + %47 = OpLabel + OpLoopMerge %49 %50 None + OpBranch %51 + %51 = OpLabel + %52 = OpLoad %24 %46 + %55 = OpSLessThan %54 %52 %53 + OpBranchConditional %55 %48 %49 + %48 = OpLabel + OpStore %56 %45 + OpBranch %57 + %57 = OpLabel + OpLoopMerge %59 %60 None + OpBranch %61 + %61 = OpLabel + %62 = OpLoad %24 %56 + %63 = OpSLessThan %54 %62 %53 + OpBranchConditional %63 %58 %59 + %58 = OpLabel + %64 = OpLoad %25 %27 + %65 = OpLoad %24 %46 + %66 = OpLoad %24 %56 + %67 = OpCompositeConstruct %32 %65 %66 + %68 = OpImageRead %38 %64 %67 + %69 = OpCompositeExtract %24 %68 0 + %70 = OpLoad %24 %44 +-%71 = OpIAdd %24 %70 %69 ++%137 = OpIMul %24 %70 %69 +-OpStore %44 %71 ++OpStore %44 %137 + OpBranch %60 + %60 = OpLabel + %72 = OpLoad %24 %56 + %74 = OpIAdd %24 %72 %73 + OpStore %56 %74 + OpBranch %57 + %59 = OpLabel + OpBranch %50 + %50 = OpLabel + %75 = OpLoad %24 %46 + %76 = OpIAdd %24 %75 %73 + OpStore %46 %76 + OpBranch %47 + %49 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + %83 = OpLoad %24 %44 + %85 = OpAccessChain %84 %82 %53 + OpStore %85 %83 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %101 = OpVariable %43 Function + %92 = OpAccessChain %91 %90 %73 + %93 = OpLoad %87 %92 + %95 = OpAccessChain %94 %82 %45 + %96 = OpLoad %77 %95 + %97 = OpConvertUToF %86 %96 + %98 = OpMatrixTimesVector %86 %93 %97 + %99 = OpConvertFToU %77 %98 + %100 = OpAccessChain %94 %82 %45 + OpStore %100 %99 + OpStore %101 %45 + OpBranch %102 + %102 = OpLabel + OpLoopMerge %104 %105 None + OpBranch %106 + %106 = OpLabel + %107 = OpLoad %24 %101 + %109 = OpSLessThan %54 %107 %108 + OpBranchConditional %109 %103 %104 + %103 = OpLabel + %111 = OpAccessChain %110 %82 %73 + %112 = OpLoad %79 %111 + %114 = OpAccessChain %113 %90 %53 + %115 = OpLoad %78 %114 + %116 = OpVectorTimesScalar %79 %112 %115 + %117 = OpConvertFToU %13 %116 + %118 = OpCompositeExtract %10 %117 0 + %119 = OpCompositeExtract %10 %117 1 + %120 = OpCompositeExtract %10 %117 2 + %121 = OpCompositeConstruct %77 %118 %119 %120 %16 + %122 = OpAccessChain %94 %82 %45 + %123 = OpLoad %77 %122 + %124 = OpIAdd %77 %123 %121 + %125 = OpAccessChain %94 %82 %45 + OpStore %125 %124 + OpBranch %105 + %105 = OpLabel + %126 = OpLoad %24 %101 + %127 = OpIAdd %24 %126 %73 + OpStore %101 %127 + OpBranch %102 + %104 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 +-%128 = OpLoad %25 %27 ++%139 = OpLoad %25 %138 +-%130 = OpImageRead %38 %128 %129 ++%130 = OpImageRead %38 %139 %129 + %131 = OpCompositeExtract %24 %130 0 + %132 = OpConvertSToF %78 %131 + %133 = OpCompositeConstruct %79 %132 %132 %132 + %134 = OpAccessChain %110 %82 %73 + OpStore %134 %133 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/large_functions_small_diffs_dst.spvasm b/test/diff/diff_files/large_functions_small_diffs_dst.spvasm new file mode 100644 index 00000000..f788e0b6 --- /dev/null +++ b/test/diff/diff_files/large_functions_small_diffs_dst.spvasm @@ -0,0 +1,229 @@ + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" %15 + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "f1(" + OpName %8 "f2(" + OpName %12 "x" + OpName %15 "gl_LocalInvocationID" + OpName %20 "y" + OpName %27 "image" + OpName %44 "sum" + OpName %46 "i" + OpName %56 "j" + OpName %80 "BufferOut" + OpMemberName %80 0 "o_uv4" + OpMemberName %80 1 "o_v3" + OpMemberName %80 2 "o_i" + OpName %82 "" + OpName %88 "BufferIn" + OpMemberName %88 0 "i_u" + OpMemberName %88 1 "i_v4" + OpMemberName %88 2 "i_f" + OpName %90 "" + OpName %101 "i" + OpName %128 "image2" + OpDecorate %15 BuiltIn LocalInvocationId + OpDecorate %27 DescriptorSet 0 + OpDecorate %27 Binding 2 + OpMemberDecorate %80 0 Offset 0 + OpMemberDecorate %80 1 Offset 16 + OpMemberDecorate %80 2 Offset 28 + OpDecorate %80 BufferBlock + OpDecorate %82 DescriptorSet 0 + OpDecorate %82 Binding 1 + OpMemberDecorate %88 0 Offset 0 + OpMemberDecorate %88 1 RowMajor + OpMemberDecorate %88 1 Offset 16 + OpMemberDecorate %88 1 MatrixStride 16 + OpMemberDecorate %88 2 Offset 80 + OpDecorate %88 Block + OpDecorate %90 DescriptorSet 0 + OpDecorate %90 Binding 0 + OpDecorate %128 DescriptorSet 0 + OpDecorate %128 Binding 3 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 0 + %11 = OpTypePointer Function %10 + %13 = OpTypeVector %10 3 + %14 = OpTypePointer Input %13 + %15 = OpVariable %14 Input + %16 = OpConstant %10 0 + %17 = OpTypePointer Input %10 + %21 = OpConstant %10 1 + %24 = OpTypeInt 32 1 + %25 = OpTypeImage %24 2D 0 0 0 2 R32i + %26 = OpTypePointer UniformConstant %25 + %27 = OpVariable %26 UniformConstant + %29 = OpTypeVector %10 2 + %32 = OpTypeVector %24 2 + %38 = OpTypeVector %24 4 + %40 = OpConstant %10 2 + %41 = OpConstant %10 3400 + %42 = OpConstant %10 264 + %43 = OpTypePointer Function %24 + %45 = OpConstant %24 0 + %53 = OpConstant %24 2 + %54 = OpTypeBool + %73 = OpConstant %24 1 + %77 = OpTypeVector %10 4 + %78 = OpTypeFloat 32 + %79 = OpTypeVector %78 3 + %80 = OpTypeStruct %77 %79 %24 + %81 = OpTypePointer Uniform %80 + %82 = OpVariable %81 Uniform + %84 = OpTypePointer Uniform %24 + %86 = OpTypeVector %78 4 + %87 = OpTypeMatrix %86 4 + %88 = OpTypeStruct %10 %87 %78 + %89 = OpTypePointer Uniform %88 + %90 = OpVariable %89 Uniform + %91 = OpTypePointer Uniform %87 + %94 = OpTypePointer Uniform %77 + %108 = OpConstant %24 3 + %110 = OpTypePointer Uniform %79 + %113 = OpTypePointer Uniform %78 + %128 = OpVariable %26 UniformConstant + %130 = OpConstantComposite %32 %45 %45 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %136 = OpFunctionCall %2 %6 + %137 = OpFunctionCall %2 %8 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %12 = OpVariable %11 Function + %20 = OpVariable %11 Function + %44 = OpVariable %43 Function + %46 = OpVariable %43 Function + %56 = OpVariable %43 Function + %18 = OpAccessChain %17 %15 %16 + %19 = OpLoad %10 %18 + OpStore %12 %19 + %22 = OpAccessChain %17 %15 %21 + %23 = OpLoad %10 %22 + OpStore %20 %23 + %28 = OpLoad %25 %27 + %30 = OpLoad %13 %15 + %31 = OpVectorShuffle %29 %30 %30 0 1 + %33 = OpBitcast %32 %31 + %34 = OpLoad %10 %12 + %35 = OpLoad %10 %20 + %36 = OpIAdd %10 %34 %35 + %37 = OpBitcast %24 %36 + %39 = OpCompositeConstruct %38 %37 %37 %37 %37 + OpImageWrite %28 %33 %39 + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + OpStore %44 %45 + OpStore %46 %45 + OpBranch %47 + %47 = OpLabel + OpLoopMerge %49 %50 None + OpBranch %51 + %51 = OpLabel + %52 = OpLoad %24 %46 + %55 = OpSLessThan %54 %52 %53 + OpBranchConditional %55 %48 %49 + %48 = OpLabel + OpStore %56 %45 + OpBranch %57 + %57 = OpLabel + OpLoopMerge %59 %60 None + OpBranch %61 + %61 = OpLabel + %62 = OpLoad %24 %56 + %63 = OpSLessThan %54 %62 %53 + OpBranchConditional %63 %58 %59 + %58 = OpLabel + %64 = OpLoad %25 %27 + %65 = OpLoad %24 %46 + %66 = OpLoad %24 %56 + %67 = OpCompositeConstruct %32 %65 %66 + %68 = OpImageRead %38 %64 %67 + %69 = OpCompositeExtract %24 %68 0 + %70 = OpLoad %24 %44 + %71 = OpIMul %24 %70 %69 + OpStore %44 %71 + OpBranch %60 + %60 = OpLabel + %72 = OpLoad %24 %56 + %74 = OpIAdd %24 %72 %73 + OpStore %56 %74 + OpBranch %57 + %59 = OpLabel + OpBranch %50 + %50 = OpLabel + %75 = OpLoad %24 %46 + %76 = OpIAdd %24 %75 %73 + OpStore %46 %76 + OpBranch %47 + %49 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + %83 = OpLoad %24 %44 + %85 = OpAccessChain %84 %82 %53 + OpStore %85 %83 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %101 = OpVariable %43 Function + %92 = OpAccessChain %91 %90 %73 + %93 = OpLoad %87 %92 + %95 = OpAccessChain %94 %82 %45 + %96 = OpLoad %77 %95 + %97 = OpConvertUToF %86 %96 + %98 = OpMatrixTimesVector %86 %93 %97 + %99 = OpConvertFToU %77 %98 + %100 = OpAccessChain %94 %82 %45 + OpStore %100 %99 + OpStore %101 %45 + OpBranch %102 + %102 = OpLabel + OpLoopMerge %104 %105 None + OpBranch %106 + %106 = OpLabel + %107 = OpLoad %24 %101 + %109 = OpSLessThan %54 %107 %108 + OpBranchConditional %109 %103 %104 + %103 = OpLabel + %111 = OpAccessChain %110 %82 %73 + %112 = OpLoad %79 %111 + %114 = OpAccessChain %113 %90 %53 + %115 = OpLoad %78 %114 + %116 = OpVectorTimesScalar %79 %112 %115 + %117 = OpConvertFToU %13 %116 + %118 = OpCompositeExtract %10 %117 0 + %119 = OpCompositeExtract %10 %117 1 + %120 = OpCompositeExtract %10 %117 2 + %121 = OpCompositeConstruct %77 %118 %119 %120 %16 + %122 = OpAccessChain %94 %82 %45 + %123 = OpLoad %77 %122 + %124 = OpIAdd %77 %123 %121 + %125 = OpAccessChain %94 %82 %45 + OpStore %125 %124 + OpBranch %105 + %105 = OpLabel + %126 = OpLoad %24 %101 + %127 = OpIAdd %24 %126 %73 + OpStore %101 %127 + OpBranch %102 + %104 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + %129 = OpLoad %25 %128 + %131 = OpImageRead %38 %129 %130 + %132 = OpCompositeExtract %24 %131 0 + %133 = OpConvertSToF %78 %132 + %134 = OpCompositeConstruct %79 %133 %133 %133 + %135 = OpAccessChain %110 %82 %73 + OpStore %135 %134 + OpReturn + OpFunctionEnd + diff --git a/test/diff/diff_files/large_functions_small_diffs_src.spvasm b/test/diff/diff_files/large_functions_small_diffs_src.spvasm new file mode 100644 index 00000000..78a92785 --- /dev/null +++ b/test/diff/diff_files/large_functions_small_diffs_src.spvasm @@ -0,0 +1,226 @@ +;; Test where src and dst have a few large functions with small differences. + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" %15 + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "f1(" + OpName %8 "f2(" + OpName %12 "x" + OpName %15 "gl_LocalInvocationID" + OpName %20 "y" + OpName %27 "image" + OpName %44 "sum" + OpName %46 "i" + OpName %56 "j" + OpName %80 "BufferOut" + OpMemberName %80 0 "o_uv4" + OpMemberName %80 1 "o_v3" + OpMemberName %80 2 "o_i" + OpName %82 "" + OpName %88 "BufferIn" + OpMemberName %88 0 "i_u" + OpMemberName %88 1 "i_v4" + OpMemberName %88 2 "i_f" + OpName %90 "" + OpName %101 "i" + OpDecorate %15 BuiltIn LocalInvocationId + OpDecorate %27 DescriptorSet 0 + OpDecorate %27 Binding 2 + OpMemberDecorate %80 0 Offset 0 + OpMemberDecorate %80 1 Offset 16 + OpMemberDecorate %80 2 Offset 28 + OpDecorate %80 BufferBlock + OpDecorate %82 DescriptorSet 0 + OpDecorate %82 Binding 1 + OpMemberDecorate %88 0 Offset 0 + OpMemberDecorate %88 1 RowMajor + OpMemberDecorate %88 1 Offset 16 + OpMemberDecorate %88 1 MatrixStride 16 + OpMemberDecorate %88 2 Offset 80 + OpDecorate %88 Block + OpDecorate %90 DescriptorSet 0 + OpDecorate %90 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 0 + %11 = OpTypePointer Function %10 + %13 = OpTypeVector %10 3 + %14 = OpTypePointer Input %13 + %15 = OpVariable %14 Input + %16 = OpConstant %10 0 + %17 = OpTypePointer Input %10 + %21 = OpConstant %10 1 + %24 = OpTypeInt 32 1 + %25 = OpTypeImage %24 2D 0 0 0 2 R32i + %26 = OpTypePointer UniformConstant %25 + %27 = OpVariable %26 UniformConstant + %29 = OpTypeVector %10 2 + %32 = OpTypeVector %24 2 + %38 = OpTypeVector %24 4 + %40 = OpConstant %10 2 + %41 = OpConstant %10 3400 + %42 = OpConstant %10 264 + %43 = OpTypePointer Function %24 + %45 = OpConstant %24 0 + %53 = OpConstant %24 2 + %54 = OpTypeBool + %73 = OpConstant %24 1 + %77 = OpTypeVector %10 4 + %78 = OpTypeFloat 32 + %79 = OpTypeVector %78 3 + %80 = OpTypeStruct %77 %79 %24 + %81 = OpTypePointer Uniform %80 + %82 = OpVariable %81 Uniform + %84 = OpTypePointer Uniform %24 + %86 = OpTypeVector %78 4 + %87 = OpTypeMatrix %86 4 + %88 = OpTypeStruct %10 %87 %78 + %89 = OpTypePointer Uniform %88 + %90 = OpVariable %89 Uniform + %91 = OpTypePointer Uniform %87 + %94 = OpTypePointer Uniform %77 + %108 = OpConstant %24 3 + %110 = OpTypePointer Uniform %79 + %113 = OpTypePointer Uniform %78 + %129 = OpConstantComposite %32 %45 %45 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %135 = OpFunctionCall %2 %6 + %136 = OpFunctionCall %2 %8 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %12 = OpVariable %11 Function + %20 = OpVariable %11 Function + %44 = OpVariable %43 Function + %46 = OpVariable %43 Function + %56 = OpVariable %43 Function + %18 = OpAccessChain %17 %15 %16 + %19 = OpLoad %10 %18 + OpStore %12 %19 + %22 = OpAccessChain %17 %15 %21 + %23 = OpLoad %10 %22 + OpStore %20 %23 + %28 = OpLoad %25 %27 + %30 = OpLoad %13 %15 + %31 = OpVectorShuffle %29 %30 %30 0 1 + %33 = OpBitcast %32 %31 + %34 = OpLoad %10 %12 + %35 = OpLoad %10 %20 + %36 = OpIAdd %10 %34 %35 + %37 = OpBitcast %24 %36 + %39 = OpCompositeConstruct %38 %37 %37 %37 %37 + OpImageWrite %28 %33 %39 + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + OpStore %44 %45 + OpStore %46 %45 + OpBranch %47 + %47 = OpLabel + OpLoopMerge %49 %50 None + OpBranch %51 + %51 = OpLabel + %52 = OpLoad %24 %46 + %55 = OpSLessThan %54 %52 %53 + OpBranchConditional %55 %48 %49 + %48 = OpLabel + OpStore %56 %45 + OpBranch %57 + %57 = OpLabel + OpLoopMerge %59 %60 None + OpBranch %61 + %61 = OpLabel + %62 = OpLoad %24 %56 + %63 = OpSLessThan %54 %62 %53 + OpBranchConditional %63 %58 %59 + %58 = OpLabel + %64 = OpLoad %25 %27 + %65 = OpLoad %24 %46 + %66 = OpLoad %24 %56 + %67 = OpCompositeConstruct %32 %65 %66 + %68 = OpImageRead %38 %64 %67 + %69 = OpCompositeExtract %24 %68 0 + %70 = OpLoad %24 %44 + %71 = OpIAdd %24 %70 %69 + OpStore %44 %71 + OpBranch %60 + %60 = OpLabel + %72 = OpLoad %24 %56 + %74 = OpIAdd %24 %72 %73 + OpStore %56 %74 + OpBranch %57 + %59 = OpLabel + OpBranch %50 + %50 = OpLabel + %75 = OpLoad %24 %46 + %76 = OpIAdd %24 %75 %73 + OpStore %46 %76 + OpBranch %47 + %49 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + %83 = OpLoad %24 %44 + %85 = OpAccessChain %84 %82 %53 + OpStore %85 %83 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %101 = OpVariable %43 Function + %92 = OpAccessChain %91 %90 %73 + %93 = OpLoad %87 %92 + %95 = OpAccessChain %94 %82 %45 + %96 = OpLoad %77 %95 + %97 = OpConvertUToF %86 %96 + %98 = OpMatrixTimesVector %86 %93 %97 + %99 = OpConvertFToU %77 %98 + %100 = OpAccessChain %94 %82 %45 + OpStore %100 %99 + OpStore %101 %45 + OpBranch %102 + %102 = OpLabel + OpLoopMerge %104 %105 None + OpBranch %106 + %106 = OpLabel + %107 = OpLoad %24 %101 + %109 = OpSLessThan %54 %107 %108 + OpBranchConditional %109 %103 %104 + %103 = OpLabel + %111 = OpAccessChain %110 %82 %73 + %112 = OpLoad %79 %111 + %114 = OpAccessChain %113 %90 %53 + %115 = OpLoad %78 %114 + %116 = OpVectorTimesScalar %79 %112 %115 + %117 = OpConvertFToU %13 %116 + %118 = OpCompositeExtract %10 %117 0 + %119 = OpCompositeExtract %10 %117 1 + %120 = OpCompositeExtract %10 %117 2 + %121 = OpCompositeConstruct %77 %118 %119 %120 %16 + %122 = OpAccessChain %94 %82 %45 + %123 = OpLoad %77 %122 + %124 = OpIAdd %77 %123 %121 + %125 = OpAccessChain %94 %82 %45 + OpStore %125 %124 + OpBranch %105 + %105 = OpLabel + %126 = OpLoad %24 %101 + %127 = OpIAdd %24 %126 %73 + OpStore %101 %127 + OpBranch %102 + %104 = OpLabel + OpMemoryBarrier %40 %41 + OpControlBarrier %40 %40 %42 + %128 = OpLoad %25 %27 + %130 = OpImageRead %38 %128 %129 + %131 = OpCompositeExtract %24 %130 0 + %132 = OpConvertSToF %78 %131 + %133 = OpCompositeConstruct %79 %132 %132 %132 + %134 = OpAccessChain %110 %82 %73 + OpStore %134 %133 + OpReturn + OpFunctionEnd + diff --git a/test/diff/diff_files/multiple_different_entry_points_autogen.cpp b/test/diff/diff_files/multiple_different_entry_points_autogen.cpp new file mode 100644 index 00000000..29d4b1d8 --- /dev/null +++ b/test/diff/diff_files/multiple_different_entry_points_autogen.cpp @@ -0,0 +1,330 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Basic test for multiple entry points. The entry points have different +// execution models and so can be trivially matched. +constexpr char kSrc[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %mainv "mainv" %vo %a + OpEntryPoint Fragment %mainf "mainf" %color %vi + OpExecutionMode %mainf OriginUpperLeft + OpSource ESSL 310 + OpName %mainv "mainv" + OpName %mainf "mainf" + OpName %a "a" + OpName %vo "v" + OpName %vi "v" + OpName %color "color" + OpDecorate %a Location 0 + OpDecorate %vo Location 0 + OpDecorate %vi Location 0 + OpDecorate %color Location 0 + OpDecorate %color RelaxedPrecision + OpDecorate %vi RelaxedPrecision + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + +%_ptr_Input_float = OpTypePointer Input %float + %a = OpVariable %_ptr_Input_float Input +%_ptr_Output_float = OpTypePointer Output %float + %vo = OpVariable %_ptr_Output_float Output + %vi = OpVariable %_ptr_Input_float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float + %color = OpVariable %_ptr_Output_v4float Output + + %mainv = OpFunction %void None %3 + %5 = OpLabel + %11 = OpLoad %float %a + OpStore %vo %11 + OpReturn + OpFunctionEnd + + %mainf = OpFunction %void None %3 + %6 = OpLabel + %12 = OpLoad %float %vi + %13 = OpCompositeConstruct %v4float %12 %12 %12 %12 + OpStore %color %13 + OpReturn + OpFunctionEnd)"; +constexpr char kDst[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %frag "frag" %vi %color + OpEntryPoint Vertex %vert "vert" %a %vo + OpExecutionMode %frag OriginUpperLeft + OpSource ESSL 310 + OpName %frag "frag" + OpName %vert "vert" + OpName %vo "v" + OpName %a "a" + OpName %color "color" + OpName %vi "v" + OpDecorate %vi Location 0 + OpDecorate %color Location 0 + OpDecorate %a Location 0 + OpDecorate %vo Location 0 + OpDecorate %color RelaxedPrecision + OpDecorate %vi RelaxedPrecision + OpDecorate %14 RelaxedPrecision + OpDecorate %17 RelaxedPrecision + + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %void = OpTypeVoid + %3 = OpTypeFunction %void + +%_ptr_Output_float = OpTypePointer Output %float + %vo = OpVariable %_ptr_Output_float Output +%_ptr_Input_float = OpTypePointer Input %float + %a = OpVariable %_ptr_Input_float Input + %vi = OpVariable %_ptr_Input_float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float + %color = OpVariable %_ptr_Output_v4float Output + + %frag = OpFunction %void None %3 + %7 = OpLabel + %14 = OpLoad %float %vi + %17 = OpCompositeConstruct %v4float %14 %14 %14 %14 + OpStore %color %17 + OpReturn + OpFunctionEnd + + %vert = OpFunction %void None %3 + %8 = OpLabel + %13 = OpLoad %float %a + OpStore %vo %13 + OpReturn + OpFunctionEnd +)"; + +TEST(DiffTest, MultipleDifferentEntryPoints) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 + ; Bound: 20 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 +-OpEntryPoint Vertex %2 "mainv" %4 %7 ++OpEntryPoint Vertex %2 "vert" %7 %4 +-OpEntryPoint Fragment %8 "mainf" %9 %10 ++OpEntryPoint Fragment %8 "frag" %10 %9 + OpExecutionMode %8 OriginUpperLeft + OpSource ESSL 310 +-OpName %2 "mainv" ++OpName %2 "vert" +-OpName %8 "mainf" ++OpName %8 "frag" + OpName %7 "a" + OpName %4 "v" + OpName %10 "v" + OpName %9 "color" + OpDecorate %7 Location 0 + OpDecorate %4 Location 0 + OpDecorate %10 Location 0 + OpDecorate %9 Location 0 + OpDecorate %9 RelaxedPrecision + OpDecorate %10 RelaxedPrecision + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + %14 = OpTypeVoid + %3 = OpTypeFunction %14 + %15 = OpTypeFloat 32 + %16 = OpTypeVector %15 4 + %17 = OpTypePointer Input %15 + %7 = OpVariable %17 Input + %18 = OpTypePointer Output %15 + %4 = OpVariable %18 Output + %10 = OpVariable %17 Input + %19 = OpTypePointer Output %16 + %9 = OpVariable %19 Output + %2 = OpFunction %14 None %3 + %5 = OpLabel + %11 = OpLoad %15 %7 + OpStore %4 %11 + OpReturn + OpFunctionEnd + %8 = OpFunction %14 None %3 + %6 = OpLabel + %12 = OpLoad %15 %10 + %13 = OpCompositeConstruct %16 %12 %12 %12 %12 + OpStore %9 %13 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, MultipleDifferentEntryPointsNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %mainv "mainv" %vo %a + OpEntryPoint Fragment %mainf "mainf" %color %vi + OpExecutionMode %mainf OriginUpperLeft + OpSource ESSL 310 + OpDecorate %a Location 0 + OpDecorate %vo Location 0 + OpDecorate %vi Location 0 + OpDecorate %color Location 0 + OpDecorate %color RelaxedPrecision + OpDecorate %vi RelaxedPrecision + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + +%_ptr_Input_float = OpTypePointer Input %float + %a = OpVariable %_ptr_Input_float Input +%_ptr_Output_float = OpTypePointer Output %float + %vo = OpVariable %_ptr_Output_float Output + %vi = OpVariable %_ptr_Input_float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float + %color = OpVariable %_ptr_Output_v4float Output + + %mainv = OpFunction %void None %3 + %5 = OpLabel + %11 = OpLoad %float %a + OpStore %vo %11 + OpReturn + OpFunctionEnd + + %mainf = OpFunction %void None %3 + %6 = OpLabel + %12 = OpLoad %float %vi + %13 = OpCompositeConstruct %v4float %12 %12 %12 %12 + OpStore %color %13 + OpReturn + OpFunctionEnd +)"; + constexpr char kDstNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %frag "frag" %vi %color + OpEntryPoint Vertex %vert "vert" %a %vo + OpExecutionMode %frag OriginUpperLeft + OpSource ESSL 310 + OpDecorate %vi Location 0 + OpDecorate %color Location 0 + OpDecorate %a Location 0 + OpDecorate %vo Location 0 + OpDecorate %color RelaxedPrecision + OpDecorate %vi RelaxedPrecision + OpDecorate %14 RelaxedPrecision + OpDecorate %17 RelaxedPrecision + + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %void = OpTypeVoid + %3 = OpTypeFunction %void + +%_ptr_Output_float = OpTypePointer Output %float + %vo = OpVariable %_ptr_Output_float Output +%_ptr_Input_float = OpTypePointer Input %float + %a = OpVariable %_ptr_Input_float Input + %vi = OpVariable %_ptr_Input_float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float + %color = OpVariable %_ptr_Output_v4float Output + + %frag = OpFunction %void None %3 + %7 = OpLabel + %14 = OpLoad %float %vi + %17 = OpCompositeConstruct %v4float %14 %14 %14 %14 + OpStore %color %17 + OpReturn + OpFunctionEnd + + %vert = OpFunction %void None %3 + %8 = OpLabel + %13 = OpLoad %float %a + OpStore %vo %13 + OpReturn + OpFunctionEnd +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 + ; Bound: 20 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 +-OpEntryPoint Vertex %2 "mainv" %4 %7 ++OpEntryPoint Vertex %2 "vert" %7 %4 +-OpEntryPoint Fragment %8 "mainf" %9 %10 ++OpEntryPoint Fragment %8 "frag" %10 %9 + OpExecutionMode %8 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %7 Location 0 + OpDecorate %4 Location 0 + OpDecorate %10 Location 0 + OpDecorate %9 Location 0 + OpDecorate %9 RelaxedPrecision + OpDecorate %10 RelaxedPrecision + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + %14 = OpTypeVoid + %3 = OpTypeFunction %14 + %15 = OpTypeFloat 32 + %16 = OpTypeVector %15 4 + %17 = OpTypePointer Input %15 + %7 = OpVariable %17 Input + %18 = OpTypePointer Output %15 + %4 = OpVariable %18 Output + %10 = OpVariable %17 Input + %19 = OpTypePointer Output %16 + %9 = OpVariable %19 Output + %2 = OpFunction %14 None %3 + %5 = OpLabel + %11 = OpLoad %15 %7 + OpStore %4 %11 + OpReturn + OpFunctionEnd + %8 = OpFunction %14 None %3 + %6 = OpLabel + %12 = OpLoad %15 %10 + %13 = OpCompositeConstruct %16 %12 %12 %12 %12 + OpStore %9 %13 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/multiple_different_entry_points_dst.spvasm b/test/diff/diff_files/multiple_different_entry_points_dst.spvasm new file mode 100644 index 00000000..72cfe283 --- /dev/null +++ b/test/diff/diff_files/multiple_different_entry_points_dst.spvasm @@ -0,0 +1,49 @@ + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %frag "frag" %vi %color + OpEntryPoint Vertex %vert "vert" %a %vo + OpExecutionMode %frag OriginUpperLeft + OpSource ESSL 310 + OpName %frag "frag" + OpName %vert "vert" + OpName %vo "v" + OpName %a "a" + OpName %color "color" + OpName %vi "v" + OpDecorate %vi Location 0 + OpDecorate %color Location 0 + OpDecorate %a Location 0 + OpDecorate %vo Location 0 + OpDecorate %color RelaxedPrecision + OpDecorate %vi RelaxedPrecision + OpDecorate %14 RelaxedPrecision + OpDecorate %17 RelaxedPrecision + + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %void = OpTypeVoid + %3 = OpTypeFunction %void + +%_ptr_Output_float = OpTypePointer Output %float + %vo = OpVariable %_ptr_Output_float Output +%_ptr_Input_float = OpTypePointer Input %float + %a = OpVariable %_ptr_Input_float Input + %vi = OpVariable %_ptr_Input_float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float + %color = OpVariable %_ptr_Output_v4float Output + + %frag = OpFunction %void None %3 + %7 = OpLabel + %14 = OpLoad %float %vi + %17 = OpCompositeConstruct %v4float %14 %14 %14 %14 + OpStore %color %17 + OpReturn + OpFunctionEnd + + %vert = OpFunction %void None %3 + %8 = OpLabel + %13 = OpLoad %float %a + OpStore %vo %13 + OpReturn + OpFunctionEnd diff --git a/test/diff/diff_files/multiple_different_entry_points_src.spvasm b/test/diff/diff_files/multiple_different_entry_points_src.spvasm new file mode 100644 index 00000000..2119aa73 --- /dev/null +++ b/test/diff/diff_files/multiple_different_entry_points_src.spvasm @@ -0,0 +1,51 @@ +;; Basic test for multiple entry points. The entry points have different +;; execution models and so can be trivially matched. + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %mainv "mainv" %vo %a + OpEntryPoint Fragment %mainf "mainf" %color %vi + OpExecutionMode %mainf OriginUpperLeft + OpSource ESSL 310 + OpName %mainv "mainv" + OpName %mainf "mainf" + OpName %a "a" + OpName %vo "v" + OpName %vi "v" + OpName %color "color" + OpDecorate %a Location 0 + OpDecorate %vo Location 0 + OpDecorate %vi Location 0 + OpDecorate %color Location 0 + OpDecorate %color RelaxedPrecision + OpDecorate %vi RelaxedPrecision + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + +%_ptr_Input_float = OpTypePointer Input %float + %a = OpVariable %_ptr_Input_float Input +%_ptr_Output_float = OpTypePointer Output %float + %vo = OpVariable %_ptr_Output_float Output + %vi = OpVariable %_ptr_Input_float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float + %color = OpVariable %_ptr_Output_v4float Output + + %mainv = OpFunction %void None %3 + %5 = OpLabel + %11 = OpLoad %float %a + OpStore %vo %11 + OpReturn + OpFunctionEnd + + %mainf = OpFunction %void None %3 + %6 = OpLabel + %12 = OpLoad %float %vi + %13 = OpCompositeConstruct %v4float %12 %12 %12 %12 + OpStore %color %13 + OpReturn + OpFunctionEnd diff --git a/test/diff/diff_files/multiple_same_entry_points_autogen.cpp b/test/diff/diff_files/multiple_same_entry_points_autogen.cpp new file mode 100644 index 00000000..9d011661 --- /dev/null +++ b/test/diff/diff_files/multiple_same_entry_points_autogen.cpp @@ -0,0 +1,375 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Test for multiple entry points with the same execution model. +constexpr char kSrc[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %4 "main1" %8 %10 + OpEntryPoint Vertex %12 "main2" %13 %14 %15 + OpSource ESSL 310 + OpName %4 "main1" + OpName %12 "main2" + OpName %8 "v" + OpName %10 "a" + OpName %13 "v" + OpName %14 "a" + OpName %15 "b" + OpDecorate %8 Location 0 + OpDecorate %10 Location 0 + OpDecorate %13 Location 0 + OpDecorate %14 Location 0 + OpDecorate %15 Location 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + + %7 = OpTypePointer Output %6 + %9 = OpTypePointer Input %6 + %8 = OpVariable %7 Output + %10 = OpVariable %9 Input + %13 = OpVariable %7 Output + %14 = OpVariable %9 Input + %15 = OpVariable %9 Input + + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpLoad %6 %10 + OpStore %8 %11 + OpReturn + OpFunctionEnd + + %12 = OpFunction %2 None %3 + %16 = OpLabel + %17 = OpLoad %6 %14 + %18 = OpLoad %6 %15 + %19 = OpFAdd %6 %17 %18 + OpStore %13 %19 + OpReturn + OpFunctionEnd)"; +constexpr char kDst[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %4 "main2" %13 %14 %15 + OpEntryPoint Vertex %12 "main1" %8 %10 + OpSource ESSL 310 + OpName %12 "main1" + OpName %4 "main2" + OpName %8 "v" + OpName %10 "a" + OpName %13 "v" + OpName %14 "a" + OpName %15 "b" + OpDecorate %8 Location 0 + OpDecorate %10 Location 0 + OpDecorate %13 Location 0 + OpDecorate %14 Location 0 + OpDecorate %15 Location 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + + %7 = OpTypePointer Output %6 + %9 = OpTypePointer Input %6 + %8 = OpVariable %7 Output + %10 = OpVariable %9 Input + %13 = OpVariable %7 Output + %14 = OpVariable %9 Input + %15 = OpVariable %9 Input + + %4 = OpFunction %2 None %3 + %16 = OpLabel + %17 = OpLoad %6 %14 + %18 = OpLoad %6 %15 + %19 = OpFAdd %6 %17 %18 + OpStore %13 %19 + OpReturn + OpFunctionEnd + + %12 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpLoad %6 %10 + OpStore %8 %11 + OpReturn + OpFunctionEnd +)"; + +TEST(DiffTest, MultipleSameEntryPoints) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 + ; Bound: 20 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 ++OpEntryPoint Vertex %12 "main2" %13 %14 %15 + OpEntryPoint Vertex %4 "main1" %8 %10 +-OpEntryPoint Vertex %12 "main2" %13 %14 %15 + OpSource ESSL 310 + OpName %4 "main1" + OpName %12 "main2" + OpName %8 "v" + OpName %10 "a" + OpName %13 "v" + OpName %14 "a" + OpName %15 "b" + OpDecorate %8 Location 0 + OpDecorate %10 Location 0 + OpDecorate %13 Location 0 + OpDecorate %14 Location 0 + OpDecorate %15 Location 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Output %6 + %9 = OpTypePointer Input %6 + %8 = OpVariable %7 Output + %10 = OpVariable %9 Input + %13 = OpVariable %7 Output + %14 = OpVariable %9 Input + %15 = OpVariable %9 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpLoad %6 %10 + OpStore %8 %11 + OpReturn + OpFunctionEnd + %12 = OpFunction %2 None %3 + %16 = OpLabel + %17 = OpLoad %6 %14 + %18 = OpLoad %6 %15 + %19 = OpFAdd %6 %17 %18 + OpStore %13 %19 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, MultipleSameEntryPointsNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %4 "main1" %8 %10 + OpEntryPoint Vertex %12 "main2" %13 %14 %15 + OpSource ESSL 310 + OpDecorate %8 Location 0 + OpDecorate %10 Location 0 + OpDecorate %13 Location 0 + OpDecorate %14 Location 0 + OpDecorate %15 Location 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + + %7 = OpTypePointer Output %6 + %9 = OpTypePointer Input %6 + %8 = OpVariable %7 Output + %10 = OpVariable %9 Input + %13 = OpVariable %7 Output + %14 = OpVariable %9 Input + %15 = OpVariable %9 Input + + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpLoad %6 %10 + OpStore %8 %11 + OpReturn + OpFunctionEnd + + %12 = OpFunction %2 None %3 + %16 = OpLabel + %17 = OpLoad %6 %14 + %18 = OpLoad %6 %15 + %19 = OpFAdd %6 %17 %18 + OpStore %13 %19 + OpReturn + OpFunctionEnd +)"; + constexpr char kDstNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %4 "main2" %13 %14 %15 + OpEntryPoint Vertex %12 "main1" %8 %10 + OpSource ESSL 310 + OpDecorate %8 Location 0 + OpDecorate %10 Location 0 + OpDecorate %13 Location 0 + OpDecorate %14 Location 0 + OpDecorate %15 Location 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + + %7 = OpTypePointer Output %6 + %9 = OpTypePointer Input %6 + %8 = OpVariable %7 Output + %10 = OpVariable %9 Input + %13 = OpVariable %7 Output + %14 = OpVariable %9 Input + %15 = OpVariable %9 Input + + %4 = OpFunction %2 None %3 + %16 = OpLabel + %17 = OpLoad %6 %14 + %18 = OpLoad %6 %15 + %19 = OpFAdd %6 %17 %18 + OpStore %13 %19 + OpReturn + OpFunctionEnd + + %12 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpLoad %6 %10 + OpStore %8 %11 + OpReturn + OpFunctionEnd +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 + ; Bound: 20 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 ++OpEntryPoint Vertex %12 "main2" %13 %14 %15 + OpEntryPoint Vertex %4 "main1" %8 %10 +-OpEntryPoint Vertex %12 "main2" %13 %14 %15 + OpSource ESSL 310 + OpDecorate %8 Location 0 + OpDecorate %10 Location 0 + OpDecorate %13 Location 0 + OpDecorate %14 Location 0 + OpDecorate %15 Location 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Output %6 + %9 = OpTypePointer Input %6 + %8 = OpVariable %7 Output + %10 = OpVariable %9 Input + %13 = OpVariable %7 Output + %14 = OpVariable %9 Input + %15 = OpVariable %9 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpLoad %6 %10 + OpStore %8 %11 + OpReturn + OpFunctionEnd + %12 = OpFunction %2 None %3 + %16 = OpLabel + %17 = OpLoad %6 %14 + %18 = OpLoad %6 %15 + %19 = OpFAdd %6 %17 %18 + OpStore %13 %19 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +TEST(DiffTest, MultipleSameEntryPointsDumpIds) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 + ; Bound: 20 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 ++OpEntryPoint Vertex %12 "main2" %13 %14 %15 + OpEntryPoint Vertex %4 "main1" %8 %10 +-OpEntryPoint Vertex %12 "main2" %13 %14 %15 + OpSource ESSL 310 + OpName %4 "main1" + OpName %12 "main2" + OpName %8 "v" + OpName %10 "a" + OpName %13 "v" + OpName %14 "a" + OpName %15 "b" + OpDecorate %8 Location 0 + OpDecorate %10 Location 0 + OpDecorate %13 Location 0 + OpDecorate %14 Location 0 + OpDecorate %15 Location 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Output %6 + %9 = OpTypePointer Input %6 + %8 = OpVariable %7 Output + %10 = OpVariable %9 Input + %13 = OpVariable %7 Output + %14 = OpVariable %9 Input + %15 = OpVariable %9 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpLoad %6 %10 + OpStore %8 %11 + OpReturn + OpFunctionEnd + %12 = OpFunction %2 None %3 + %16 = OpLabel + %17 = OpLoad %6 %14 + %18 = OpLoad %6 %15 + %19 = OpFAdd %6 %17 %18 + OpStore %13 %19 + OpReturn + OpFunctionEnd + Src -> Dst + 1 -> 1 [ExtInstImport] + 2 -> 2 [TypeVoid] + 3 -> 3 [TypeFunction] + 4 -> 12 [Function] + 5 -> 5 [Label] + 6 -> 6 [TypeFloat] + 7 -> 7 [TypePointer] + 8 -> 8 [Variable] + 9 -> 9 [TypePointer] + 10 -> 10 [Variable] + 11 -> 11 [Load] + 12 -> 4 [Function] + 13 -> 13 [Variable] + 14 -> 14 [Variable] + 15 -> 15 [Variable] + 16 -> 16 [Label] + 17 -> 17 [Load] + 18 -> 18 [Load] + 19 -> 19 [FAdd] +)"; + Options options; + options.dump_id_map = true; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/multiple_same_entry_points_dst.spvasm b/test/diff/diff_files/multiple_same_entry_points_dst.spvasm new file mode 100644 index 00000000..e2007220 --- /dev/null +++ b/test/diff/diff_files/multiple_same_entry_points_dst.spvasm @@ -0,0 +1,45 @@ + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %4 "main2" %13 %14 %15 + OpEntryPoint Vertex %12 "main1" %8 %10 + OpSource ESSL 310 + OpName %12 "main1" + OpName %4 "main2" + OpName %8 "v" + OpName %10 "a" + OpName %13 "v" + OpName %14 "a" + OpName %15 "b" + OpDecorate %8 Location 0 + OpDecorate %10 Location 0 + OpDecorate %13 Location 0 + OpDecorate %14 Location 0 + OpDecorate %15 Location 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + + %7 = OpTypePointer Output %6 + %9 = OpTypePointer Input %6 + %8 = OpVariable %7 Output + %10 = OpVariable %9 Input + %13 = OpVariable %7 Output + %14 = OpVariable %9 Input + %15 = OpVariable %9 Input + + %4 = OpFunction %2 None %3 + %16 = OpLabel + %17 = OpLoad %6 %14 + %18 = OpLoad %6 %15 + %19 = OpFAdd %6 %17 %18 + OpStore %13 %19 + OpReturn + OpFunctionEnd + + %12 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpLoad %6 %10 + OpStore %8 %11 + OpReturn + OpFunctionEnd diff --git a/test/diff/diff_files/multiple_same_entry_points_src.spvasm b/test/diff/diff_files/multiple_same_entry_points_src.spvasm new file mode 100644 index 00000000..17001b57 --- /dev/null +++ b/test/diff/diff_files/multiple_same_entry_points_src.spvasm @@ -0,0 +1,46 @@ +;; Test for multiple entry points with the same execution model. + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %4 "main1" %8 %10 + OpEntryPoint Vertex %12 "main2" %13 %14 %15 + OpSource ESSL 310 + OpName %4 "main1" + OpName %12 "main2" + OpName %8 "v" + OpName %10 "a" + OpName %13 "v" + OpName %14 "a" + OpName %15 "b" + OpDecorate %8 Location 0 + OpDecorate %10 Location 0 + OpDecorate %13 Location 0 + OpDecorate %14 Location 0 + OpDecorate %15 Location 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + + %7 = OpTypePointer Output %6 + %9 = OpTypePointer Input %6 + %8 = OpVariable %7 Output + %10 = OpVariable %9 Input + %13 = OpVariable %7 Output + %14 = OpVariable %9 Input + %15 = OpVariable %9 Input + + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpLoad %6 %10 + OpStore %8 %11 + OpReturn + OpFunctionEnd + + %12 = OpFunction %2 None %3 + %16 = OpLabel + %17 = OpLoad %6 %14 + %18 = OpLoad %6 %15 + %19 = OpFAdd %6 %17 %18 + OpStore %13 %19 + OpReturn + OpFunctionEnd diff --git a/test/diff/diff_files/reordered_if_blocks_autogen.cpp b/test/diff/diff_files/reordered_if_blocks_autogen.cpp new file mode 100644 index 00000000..0788199f --- /dev/null +++ b/test/diff/diff_files/reordered_if_blocks_autogen.cpp @@ -0,0 +1,568 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Test where src and dst have the true and false blocks of an if reordered. +constexpr char kSrc[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %8 %44 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "v" + OpName %44 "color" + OpDecorate %8 RelaxedPrecision + OpDecorate %8 Location 0 + OpDecorate %9 RelaxedPrecision + OpDecorate %18 RelaxedPrecision + OpDecorate %19 RelaxedPrecision + OpDecorate %20 RelaxedPrecision + OpDecorate %23 RelaxedPrecision + OpDecorate %24 RelaxedPrecision + OpDecorate %25 RelaxedPrecision + OpDecorate %26 RelaxedPrecision + OpDecorate %27 RelaxedPrecision + OpDecorate %28 RelaxedPrecision + OpDecorate %29 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %31 RelaxedPrecision + OpDecorate %33 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %44 RelaxedPrecision + OpDecorate %44 Location 0 + OpDecorate %45 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Input %6 + %8 = OpVariable %7 Input + %10 = OpConstant %6 0 + %11 = OpTypeBool + %15 = OpTypeVector %6 4 + %16 = OpTypePointer Function %15 + %21 = OpConstant %6 -0.5 + %22 = OpConstant %6 -0.300000012 + %38 = OpConstant %6 0.5 + %43 = OpTypePointer Output %15 + %44 = OpVariable %43 Output + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpLoad %6 %8 + %12 = OpFOrdLessThanEqual %11 %9 %10 + OpSelectionMerge %14 None + OpBranchConditional %12 %13 %32 + %13 = OpLabel + %18 = OpLoad %6 %8 + %19 = OpExtInst %6 %1 Log %18 + %20 = OpLoad %6 %8 + %23 = OpExtInst %6 %1 FClamp %20 %21 %22 + %24 = OpFMul %6 %19 %23 + %25 = OpLoad %6 %8 + %26 = OpExtInst %6 %1 Sin %25 + %27 = OpLoad %6 %8 + %28 = OpExtInst %6 %1 Cos %27 + %29 = OpLoad %6 %8 + %30 = OpExtInst %6 %1 Exp %29 + %31 = OpCompositeConstruct %15 %24 %26 %28 %30 + OpBranch %14 + %32 = OpLabel + %33 = OpLoad %6 %8 + %34 = OpExtInst %6 %1 Sqrt %33 + %35 = OpLoad %6 %8 + %36 = OpExtInst %6 %1 FSign %35 + %37 = OpLoad %6 %8 + %39 = OpExtInst %6 %1 FMax %37 %38 + %40 = OpLoad %6 %8 + %41 = OpExtInst %6 %1 Floor %40 + %42 = OpCompositeConstruct %15 %34 %36 %39 %41 + OpBranch %14 + %14 = OpLabel + %45 = OpPhi %15 %31 %13 %42 %32 + OpStore %44 %45 + OpReturn + OpFunctionEnd)"; +constexpr char kDst[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %8 %44 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "v" + OpName %44 "color" + OpDecorate %8 RelaxedPrecision + OpDecorate %8 Location 0 + OpDecorate %9 RelaxedPrecision + OpDecorate %18 RelaxedPrecision + OpDecorate %19 RelaxedPrecision + OpDecorate %20 RelaxedPrecision + OpDecorate %21 RelaxedPrecision + OpDecorate %22 RelaxedPrecision + OpDecorate %24 RelaxedPrecision + OpDecorate %25 RelaxedPrecision + OpDecorate %26 RelaxedPrecision + OpDecorate %27 RelaxedPrecision + OpDecorate %29 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %31 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %38 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %44 RelaxedPrecision + OpDecorate %44 Location 0 + OpDecorate %45 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Input %6 + %8 = OpVariable %7 Input + %10 = OpConstant %6 0 + %11 = OpTypeBool + %15 = OpTypeVector %6 4 + %16 = OpTypePointer Function %15 + %23 = OpConstant %6 0.5 + %32 = OpConstant %6 -0.5 + %33 = OpConstant %6 -0.300000012 + %43 = OpTypePointer Output %15 + %44 = OpVariable %43 Output + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpLoad %6 %8 + %12 = OpFOrdLessThanEqual %11 %9 %10 + OpSelectionMerge %14 None + OpBranchConditional %12 %28 %13 + %13 = OpLabel + %18 = OpLoad %6 %8 + %19 = OpExtInst %6 %1 Sqrt %18 + %20 = OpLoad %6 %8 + %21 = OpExtInst %6 %1 FSign %20 + %22 = OpLoad %6 %8 + %24 = OpExtInst %6 %1 FMax %22 %23 + %25 = OpLoad %6 %8 + %26 = OpExtInst %6 %1 Floor %25 + %27 = OpCompositeConstruct %15 %19 %21 %24 %26 + OpBranch %14 + %28 = OpLabel + %29 = OpLoad %6 %8 + %30 = OpExtInst %6 %1 Log %29 + %31 = OpLoad %6 %8 + %34 = OpExtInst %6 %1 FClamp %31 %32 %33 + %35 = OpFMul %6 %30 %34 + %36 = OpLoad %6 %8 + %37 = OpExtInst %6 %1 Sin %36 + %38 = OpLoad %6 %8 + %39 = OpExtInst %6 %1 Cos %38 + %40 = OpLoad %6 %8 + %41 = OpExtInst %6 %1 Exp %40 + %42 = OpCompositeConstruct %15 %35 %37 %39 %41 + OpBranch %14 + %14 = OpLabel + %45 = OpPhi %15 %27 %13 %42 %28 + OpStore %44 %45 + OpReturn + OpFunctionEnd + +)"; + +TEST(DiffTest, ReorderedIfBlocks) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 46 ++; Bound: 47 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %8 %44 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "v" + OpName %44 "color" + OpDecorate %8 RelaxedPrecision + OpDecorate %8 Location 0 + OpDecorate %9 RelaxedPrecision + OpDecorate %18 RelaxedPrecision + OpDecorate %19 RelaxedPrecision + OpDecorate %20 RelaxedPrecision + OpDecorate %23 RelaxedPrecision + OpDecorate %24 RelaxedPrecision + OpDecorate %25 RelaxedPrecision + OpDecorate %26 RelaxedPrecision + OpDecorate %27 RelaxedPrecision + OpDecorate %28 RelaxedPrecision + OpDecorate %29 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %31 RelaxedPrecision + OpDecorate %33 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %44 RelaxedPrecision + OpDecorate %44 Location 0 + OpDecorate %45 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Input %6 + %8 = OpVariable %7 Input + %10 = OpConstant %6 0 + %11 = OpTypeBool + %15 = OpTypeVector %6 4 + %16 = OpTypePointer Function %15 + %21 = OpConstant %6 -0.5 + %22 = OpConstant %6 -0.300000012 + %38 = OpConstant %6 0.5 + %43 = OpTypePointer Output %15 + %44 = OpVariable %43 Output + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpLoad %6 %8 + %12 = OpFOrdLessThanEqual %11 %9 %10 + OpSelectionMerge %14 None + OpBranchConditional %12 %13 %32 + %32 = OpLabel + %33 = OpLoad %6 %8 + %34 = OpExtInst %6 %1 Sqrt %33 + %35 = OpLoad %6 %8 + %36 = OpExtInst %6 %1 FSign %35 + %37 = OpLoad %6 %8 + %39 = OpExtInst %6 %1 FMax %37 %38 + %40 = OpLoad %6 %8 + %41 = OpExtInst %6 %1 Floor %40 + %42 = OpCompositeConstruct %15 %34 %36 %39 %41 + OpBranch %14 + %13 = OpLabel + %18 = OpLoad %6 %8 + %19 = OpExtInst %6 %1 Log %18 + %20 = OpLoad %6 %8 + %23 = OpExtInst %6 %1 FClamp %20 %21 %22 + %24 = OpFMul %6 %19 %23 + %25 = OpLoad %6 %8 + %26 = OpExtInst %6 %1 Sin %25 + %27 = OpLoad %6 %8 + %28 = OpExtInst %6 %1 Cos %27 + %29 = OpLoad %6 %8 + %30 = OpExtInst %6 %1 Exp %29 + %31 = OpCompositeConstruct %15 %24 %26 %28 %30 + OpBranch %14 + %14 = OpLabel +-%45 = OpPhi %15 %31 %13 %42 %32 ++%45 = OpPhi %15 %42 %32 %31 %13 + OpStore %44 %45 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, ReorderedIfBlocksNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %8 %44 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %8 RelaxedPrecision + OpDecorate %8 Location 0 + OpDecorate %9 RelaxedPrecision + OpDecorate %18 RelaxedPrecision + OpDecorate %19 RelaxedPrecision + OpDecorate %20 RelaxedPrecision + OpDecorate %23 RelaxedPrecision + OpDecorate %24 RelaxedPrecision + OpDecorate %25 RelaxedPrecision + OpDecorate %26 RelaxedPrecision + OpDecorate %27 RelaxedPrecision + OpDecorate %28 RelaxedPrecision + OpDecorate %29 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %31 RelaxedPrecision + OpDecorate %33 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %44 RelaxedPrecision + OpDecorate %44 Location 0 + OpDecorate %45 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Input %6 + %8 = OpVariable %7 Input + %10 = OpConstant %6 0 + %11 = OpTypeBool + %15 = OpTypeVector %6 4 + %16 = OpTypePointer Function %15 + %21 = OpConstant %6 -0.5 + %22 = OpConstant %6 -0.300000012 + %38 = OpConstant %6 0.5 + %43 = OpTypePointer Output %15 + %44 = OpVariable %43 Output + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpLoad %6 %8 + %12 = OpFOrdLessThanEqual %11 %9 %10 + OpSelectionMerge %14 None + OpBranchConditional %12 %13 %32 + %13 = OpLabel + %18 = OpLoad %6 %8 + %19 = OpExtInst %6 %1 Log %18 + %20 = OpLoad %6 %8 + %23 = OpExtInst %6 %1 FClamp %20 %21 %22 + %24 = OpFMul %6 %19 %23 + %25 = OpLoad %6 %8 + %26 = OpExtInst %6 %1 Sin %25 + %27 = OpLoad %6 %8 + %28 = OpExtInst %6 %1 Cos %27 + %29 = OpLoad %6 %8 + %30 = OpExtInst %6 %1 Exp %29 + %31 = OpCompositeConstruct %15 %24 %26 %28 %30 + OpBranch %14 + %32 = OpLabel + %33 = OpLoad %6 %8 + %34 = OpExtInst %6 %1 Sqrt %33 + %35 = OpLoad %6 %8 + %36 = OpExtInst %6 %1 FSign %35 + %37 = OpLoad %6 %8 + %39 = OpExtInst %6 %1 FMax %37 %38 + %40 = OpLoad %6 %8 + %41 = OpExtInst %6 %1 Floor %40 + %42 = OpCompositeConstruct %15 %34 %36 %39 %41 + OpBranch %14 + %14 = OpLabel + %45 = OpPhi %15 %31 %13 %42 %32 + OpStore %44 %45 + OpReturn + OpFunctionEnd +)"; + constexpr char kDstNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %8 %44 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %8 RelaxedPrecision + OpDecorate %8 Location 0 + OpDecorate %9 RelaxedPrecision + OpDecorate %18 RelaxedPrecision + OpDecorate %19 RelaxedPrecision + OpDecorate %20 RelaxedPrecision + OpDecorate %21 RelaxedPrecision + OpDecorate %22 RelaxedPrecision + OpDecorate %24 RelaxedPrecision + OpDecorate %25 RelaxedPrecision + OpDecorate %26 RelaxedPrecision + OpDecorate %27 RelaxedPrecision + OpDecorate %29 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %31 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %38 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %44 RelaxedPrecision + OpDecorate %44 Location 0 + OpDecorate %45 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Input %6 + %8 = OpVariable %7 Input + %10 = OpConstant %6 0 + %11 = OpTypeBool + %15 = OpTypeVector %6 4 + %16 = OpTypePointer Function %15 + %23 = OpConstant %6 0.5 + %32 = OpConstant %6 -0.5 + %33 = OpConstant %6 -0.300000012 + %43 = OpTypePointer Output %15 + %44 = OpVariable %43 Output + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpLoad %6 %8 + %12 = OpFOrdLessThanEqual %11 %9 %10 + OpSelectionMerge %14 None + OpBranchConditional %12 %28 %13 + %13 = OpLabel + %18 = OpLoad %6 %8 + %19 = OpExtInst %6 %1 Sqrt %18 + %20 = OpLoad %6 %8 + %21 = OpExtInst %6 %1 FSign %20 + %22 = OpLoad %6 %8 + %24 = OpExtInst %6 %1 FMax %22 %23 + %25 = OpLoad %6 %8 + %26 = OpExtInst %6 %1 Floor %25 + %27 = OpCompositeConstruct %15 %19 %21 %24 %26 + OpBranch %14 + %28 = OpLabel + %29 = OpLoad %6 %8 + %30 = OpExtInst %6 %1 Log %29 + %31 = OpLoad %6 %8 + %34 = OpExtInst %6 %1 FClamp %31 %32 %33 + %35 = OpFMul %6 %30 %34 + %36 = OpLoad %6 %8 + %37 = OpExtInst %6 %1 Sin %36 + %38 = OpLoad %6 %8 + %39 = OpExtInst %6 %1 Cos %38 + %40 = OpLoad %6 %8 + %41 = OpExtInst %6 %1 Exp %40 + %42 = OpCompositeConstruct %15 %35 %37 %39 %41 + OpBranch %14 + %14 = OpLabel + %45 = OpPhi %15 %27 %13 %42 %28 + OpStore %44 %45 + OpReturn + OpFunctionEnd + +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 46 ++; Bound: 47 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %8 %44 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %8 RelaxedPrecision + OpDecorate %8 Location 0 + OpDecorate %9 RelaxedPrecision + OpDecorate %18 RelaxedPrecision + OpDecorate %19 RelaxedPrecision + OpDecorate %20 RelaxedPrecision + OpDecorate %23 RelaxedPrecision + OpDecorate %24 RelaxedPrecision + OpDecorate %25 RelaxedPrecision + OpDecorate %26 RelaxedPrecision + OpDecorate %27 RelaxedPrecision + OpDecorate %28 RelaxedPrecision + OpDecorate %29 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %31 RelaxedPrecision + OpDecorate %33 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %44 RelaxedPrecision + OpDecorate %44 Location 0 + OpDecorate %45 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Input %6 + %8 = OpVariable %7 Input + %10 = OpConstant %6 0 + %11 = OpTypeBool + %15 = OpTypeVector %6 4 + %16 = OpTypePointer Function %15 + %21 = OpConstant %6 -0.5 + %22 = OpConstant %6 -0.300000012 + %38 = OpConstant %6 0.5 + %43 = OpTypePointer Output %15 + %44 = OpVariable %43 Output + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpLoad %6 %8 + %12 = OpFOrdLessThanEqual %11 %9 %10 + OpSelectionMerge %14 None + OpBranchConditional %12 %13 %32 + %32 = OpLabel + %33 = OpLoad %6 %8 + %34 = OpExtInst %6 %1 Sqrt %33 + %35 = OpLoad %6 %8 + %36 = OpExtInst %6 %1 FSign %35 + %37 = OpLoad %6 %8 + %39 = OpExtInst %6 %1 FMax %37 %38 + %40 = OpLoad %6 %8 + %41 = OpExtInst %6 %1 Floor %40 + %42 = OpCompositeConstruct %15 %34 %36 %39 %41 + OpBranch %14 + %13 = OpLabel + %18 = OpLoad %6 %8 + %19 = OpExtInst %6 %1 Log %18 + %20 = OpLoad %6 %8 + %23 = OpExtInst %6 %1 FClamp %20 %21 %22 + %24 = OpFMul %6 %19 %23 + %25 = OpLoad %6 %8 + %26 = OpExtInst %6 %1 Sin %25 + %27 = OpLoad %6 %8 + %28 = OpExtInst %6 %1 Cos %27 + %29 = OpLoad %6 %8 + %30 = OpExtInst %6 %1 Exp %29 + %31 = OpCompositeConstruct %15 %24 %26 %28 %30 + OpBranch %14 + %14 = OpLabel +-%45 = OpPhi %15 %31 %13 %42 %32 ++%45 = OpPhi %15 %42 %32 %31 %13 + OpStore %44 %45 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/reordered_if_blocks_dst.spvasm b/test/diff/diff_files/reordered_if_blocks_dst.spvasm new file mode 100644 index 00000000..cd1d6d52 --- /dev/null +++ b/test/diff/diff_files/reordered_if_blocks_dst.spvasm @@ -0,0 +1,87 @@ + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %8 %44 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "v" + OpName %44 "color" + OpDecorate %8 RelaxedPrecision + OpDecorate %8 Location 0 + OpDecorate %9 RelaxedPrecision + OpDecorate %18 RelaxedPrecision + OpDecorate %19 RelaxedPrecision + OpDecorate %20 RelaxedPrecision + OpDecorate %21 RelaxedPrecision + OpDecorate %22 RelaxedPrecision + OpDecorate %24 RelaxedPrecision + OpDecorate %25 RelaxedPrecision + OpDecorate %26 RelaxedPrecision + OpDecorate %27 RelaxedPrecision + OpDecorate %29 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %31 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %38 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %44 RelaxedPrecision + OpDecorate %44 Location 0 + OpDecorate %45 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Input %6 + %8 = OpVariable %7 Input + %10 = OpConstant %6 0 + %11 = OpTypeBool + %15 = OpTypeVector %6 4 + %16 = OpTypePointer Function %15 + %23 = OpConstant %6 0.5 + %32 = OpConstant %6 -0.5 + %33 = OpConstant %6 -0.300000012 + %43 = OpTypePointer Output %15 + %44 = OpVariable %43 Output + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpLoad %6 %8 + %12 = OpFOrdLessThanEqual %11 %9 %10 + OpSelectionMerge %14 None + OpBranchConditional %12 %28 %13 + %13 = OpLabel + %18 = OpLoad %6 %8 + %19 = OpExtInst %6 %1 Sqrt %18 + %20 = OpLoad %6 %8 + %21 = OpExtInst %6 %1 FSign %20 + %22 = OpLoad %6 %8 + %24 = OpExtInst %6 %1 FMax %22 %23 + %25 = OpLoad %6 %8 + %26 = OpExtInst %6 %1 Floor %25 + %27 = OpCompositeConstruct %15 %19 %21 %24 %26 + OpBranch %14 + %28 = OpLabel + %29 = OpLoad %6 %8 + %30 = OpExtInst %6 %1 Log %29 + %31 = OpLoad %6 %8 + %34 = OpExtInst %6 %1 FClamp %31 %32 %33 + %35 = OpFMul %6 %30 %34 + %36 = OpLoad %6 %8 + %37 = OpExtInst %6 %1 Sin %36 + %38 = OpLoad %6 %8 + %39 = OpExtInst %6 %1 Cos %38 + %40 = OpLoad %6 %8 + %41 = OpExtInst %6 %1 Exp %40 + %42 = OpCompositeConstruct %15 %35 %37 %39 %41 + OpBranch %14 + %14 = OpLabel + %45 = OpPhi %15 %27 %13 %42 %28 + OpStore %44 %45 + OpReturn + OpFunctionEnd + diff --git a/test/diff/diff_files/reordered_if_blocks_src.spvasm b/test/diff/diff_files/reordered_if_blocks_src.spvasm new file mode 100644 index 00000000..209cb45b --- /dev/null +++ b/test/diff/diff_files/reordered_if_blocks_src.spvasm @@ -0,0 +1,87 @@ +;; Test where src and dst have the true and false blocks of an if reordered. + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %8 %44 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "v" + OpName %44 "color" + OpDecorate %8 RelaxedPrecision + OpDecorate %8 Location 0 + OpDecorate %9 RelaxedPrecision + OpDecorate %18 RelaxedPrecision + OpDecorate %19 RelaxedPrecision + OpDecorate %20 RelaxedPrecision + OpDecorate %23 RelaxedPrecision + OpDecorate %24 RelaxedPrecision + OpDecorate %25 RelaxedPrecision + OpDecorate %26 RelaxedPrecision + OpDecorate %27 RelaxedPrecision + OpDecorate %28 RelaxedPrecision + OpDecorate %29 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %31 RelaxedPrecision + OpDecorate %33 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %44 RelaxedPrecision + OpDecorate %44 Location 0 + OpDecorate %45 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Input %6 + %8 = OpVariable %7 Input + %10 = OpConstant %6 0 + %11 = OpTypeBool + %15 = OpTypeVector %6 4 + %16 = OpTypePointer Function %15 + %21 = OpConstant %6 -0.5 + %22 = OpConstant %6 -0.300000012 + %38 = OpConstant %6 0.5 + %43 = OpTypePointer Output %15 + %44 = OpVariable %43 Output + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpLoad %6 %8 + %12 = OpFOrdLessThanEqual %11 %9 %10 + OpSelectionMerge %14 None + OpBranchConditional %12 %13 %32 + %13 = OpLabel + %18 = OpLoad %6 %8 + %19 = OpExtInst %6 %1 Log %18 + %20 = OpLoad %6 %8 + %23 = OpExtInst %6 %1 FClamp %20 %21 %22 + %24 = OpFMul %6 %19 %23 + %25 = OpLoad %6 %8 + %26 = OpExtInst %6 %1 Sin %25 + %27 = OpLoad %6 %8 + %28 = OpExtInst %6 %1 Cos %27 + %29 = OpLoad %6 %8 + %30 = OpExtInst %6 %1 Exp %29 + %31 = OpCompositeConstruct %15 %24 %26 %28 %30 + OpBranch %14 + %32 = OpLabel + %33 = OpLoad %6 %8 + %34 = OpExtInst %6 %1 Sqrt %33 + %35 = OpLoad %6 %8 + %36 = OpExtInst %6 %1 FSign %35 + %37 = OpLoad %6 %8 + %39 = OpExtInst %6 %1 FMax %37 %38 + %40 = OpLoad %6 %8 + %41 = OpExtInst %6 %1 Floor %40 + %42 = OpCompositeConstruct %15 %34 %36 %39 %41 + OpBranch %14 + %14 = OpLabel + %45 = OpPhi %15 %31 %13 %42 %32 + OpStore %44 %45 + OpReturn + OpFunctionEnd diff --git a/test/diff/diff_files/reordered_switch_blocks_autogen.cpp b/test/diff/diff_files/reordered_switch_blocks_autogen.cpp new file mode 100644 index 00000000..c0ba48d1 --- /dev/null +++ b/test/diff/diff_files/reordered_switch_blocks_autogen.cpp @@ -0,0 +1,582 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Test where src and dst have cases of a switch in different order. +constexpr char kSrc[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %7 "BufferIn" + OpMemberName %7 0 "i" + OpName %9 "" + OpName %23 "BufferOut" + OpMemberName %23 0 "o" + OpName %25 "" + OpMemberDecorate %7 0 Offset 0 + OpDecorate %7 Block + OpDecorate %9 DescriptorSet 0 + OpDecorate %9 Binding 0 + OpMemberDecorate %23 0 Offset 0 + OpDecorate %23 BufferBlock + OpDecorate %25 DescriptorSet 0 + OpDecorate %25 Binding 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpTypeStruct %6 + %8 = OpTypePointer Uniform %7 + %9 = OpVariable %8 Uniform + %10 = OpTypeInt 32 1 + %11 = OpConstant %10 0 + %12 = OpTypePointer Uniform %6 + %23 = OpTypeStruct %6 + %24 = OpTypePointer Uniform %23 + %25 = OpVariable %24 Uniform + %28 = OpConstant %10 1 + %34 = OpConstant %6 2 + %52 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %13 = OpAccessChain %12 %9 %11 + %14 = OpLoad %6 %13 + OpSelectionMerge %22 None + OpSwitch %14 %21 0 %15 1 %16 2 %17 3 %18 4 %19 5 %20 + %21 = OpLabel + %54 = OpAccessChain %12 %25 %11 + %55 = OpLoad %6 %54 + %56 = OpIAdd %6 %55 %34 + %57 = OpAccessChain %12 %25 %11 + OpStore %57 %56 + OpBranch %22 + %15 = OpLabel + %26 = OpAccessChain %12 %25 %11 + %27 = OpLoad %6 %26 + %29 = OpIAdd %6 %27 %28 + OpStore %26 %29 + OpBranch %22 + %16 = OpLabel + %31 = OpAccessChain %12 %25 %11 + %32 = OpLoad %6 %31 + %33 = OpISub %6 %32 %28 + OpStore %31 %33 + OpBranch %17 + %17 = OpLabel + %35 = OpAccessChain %12 %25 %11 + %36 = OpLoad %6 %35 + %37 = OpIMul %6 %36 %34 + %38 = OpAccessChain %12 %25 %11 + OpStore %38 %37 + OpBranch %22 + %18 = OpLabel + %40 = OpAccessChain %12 %25 %11 + %41 = OpLoad %6 %40 + %42 = OpUDiv %6 %41 %34 + %43 = OpAccessChain %12 %25 %11 + OpStore %43 %42 + OpBranch %22 + %19 = OpLabel + %45 = OpAccessChain %12 %25 %11 + %46 = OpLoad %6 %45 + %47 = OpAccessChain %12 %25 %11 + %48 = OpLoad %6 %47 + %49 = OpIMul %6 %46 %48 + %50 = OpAccessChain %12 %25 %11 + OpStore %50 %49 + OpBranch %22 + %20 = OpLabel + %53 = OpAccessChain %12 %25 %11 + OpStore %53 %52 + OpBranch %21 + %22 = OpLabel + OpReturn + OpFunctionEnd)"; +constexpr char kDst[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %7 "BufferIn" + OpMemberName %7 0 "i" + OpName %9 "" + OpName %23 "BufferOut" + OpMemberName %23 0 "o" + OpName %25 "" + OpMemberDecorate %7 0 Offset 0 + OpDecorate %7 Block + OpDecorate %9 DescriptorSet 0 + OpDecorate %9 Binding 0 + OpMemberDecorate %23 0 Offset 0 + OpDecorate %23 BufferBlock + OpDecorate %25 DescriptorSet 0 + OpDecorate %25 Binding 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpTypeStruct %6 + %8 = OpTypePointer Uniform %7 + %9 = OpVariable %8 Uniform + %10 = OpTypeInt 32 1 + %11 = OpConstant %10 0 + %12 = OpTypePointer Uniform %6 + %23 = OpTypeStruct %6 + %24 = OpTypePointer Uniform %23 + %25 = OpVariable %24 Uniform + %28 = OpConstant %10 1 + %34 = OpConstant %6 2 + %52 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %13 = OpAccessChain %12 %9 %11 + %14 = OpLoad %6 %13 + OpSelectionMerge %22 None + OpSwitch %14 %21 0 %15 1 %16 2 %17 3 %18 4 %19 5 %20 + %17 = OpLabel + %35 = OpAccessChain %12 %25 %11 + %36 = OpLoad %6 %35 + %37 = OpIMul %6 %36 %34 + %38 = OpAccessChain %12 %25 %11 + OpStore %38 %37 + OpBranch %22 + %18 = OpLabel + %40 = OpAccessChain %12 %25 %11 + %41 = OpLoad %6 %40 + %42 = OpUDiv %6 %41 %34 + %43 = OpAccessChain %12 %25 %11 + OpStore %43 %42 + OpBranch %22 + %21 = OpLabel + %54 = OpAccessChain %12 %25 %11 + %55 = OpLoad %6 %54 + %56 = OpIAdd %6 %55 %34 + %57 = OpAccessChain %12 %25 %11 + OpStore %57 %56 + OpBranch %22 + %20 = OpLabel + %53 = OpAccessChain %12 %25 %11 + OpStore %53 %52 + OpBranch %21 + %15 = OpLabel + %26 = OpAccessChain %12 %25 %11 + %27 = OpLoad %6 %26 + %29 = OpIAdd %6 %27 %28 + OpStore %26 %29 + OpBranch %22 + %19 = OpLabel + %45 = OpAccessChain %12 %25 %11 + %46 = OpLoad %6 %45 + %47 = OpAccessChain %12 %25 %11 + %48 = OpLoad %6 %47 + %49 = OpIMul %6 %46 %48 + %50 = OpAccessChain %12 %25 %11 + OpStore %50 %49 + OpBranch %22 + %16 = OpLabel + %31 = OpAccessChain %12 %25 %11 + %32 = OpLoad %6 %31 + %33 = OpISub %6 %32 %28 + OpStore %31 %33 + OpBranch %17 + %22 = OpLabel + OpReturn + OpFunctionEnd +)"; + +TEST(DiffTest, ReorderedSwitchBlocks) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 58 ++; Bound: 62 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %7 "BufferIn" + OpMemberName %7 0 "i" + OpName %9 "" + OpName %23 "BufferOut" + OpMemberName %23 0 "o" + OpName %25 "" + OpMemberDecorate %7 0 Offset 0 + OpDecorate %7 Block + OpDecorate %9 DescriptorSet 0 + OpDecorate %9 Binding 0 + OpMemberDecorate %23 0 Offset 0 + OpDecorate %23 BufferBlock + OpDecorate %25 DescriptorSet 0 + OpDecorate %25 Binding 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpTypeStruct %6 + %8 = OpTypePointer Uniform %7 + %9 = OpVariable %8 Uniform + %10 = OpTypeInt 32 1 + %11 = OpConstant %10 0 + %12 = OpTypePointer Uniform %6 + %23 = OpTypeStruct %6 + %24 = OpTypePointer Uniform %23 + %25 = OpVariable %24 Uniform + %28 = OpConstant %10 1 + %34 = OpConstant %6 2 + %52 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %13 = OpAccessChain %12 %9 %11 + %14 = OpLoad %6 %13 + OpSelectionMerge %22 None + OpSwitch %14 %21 0 %15 1 %16 2 %17 3 %18 4 %19 5 %20 + %20 = OpLabel + %53 = OpAccessChain %12 %25 %11 + OpStore %53 %52 + OpBranch %21 + %19 = OpLabel + %45 = OpAccessChain %12 %25 %11 + %46 = OpLoad %6 %45 + %47 = OpAccessChain %12 %25 %11 + %48 = OpLoad %6 %47 + %49 = OpIMul %6 %46 %48 + %50 = OpAccessChain %12 %25 %11 + OpStore %50 %49 + OpBranch %22 + %18 = OpLabel + %40 = OpAccessChain %12 %25 %11 + %41 = OpLoad %6 %40 + %42 = OpUDiv %6 %41 %34 + %43 = OpAccessChain %12 %25 %11 + OpStore %43 %42 + OpBranch %22 + %16 = OpLabel + %31 = OpAccessChain %12 %25 %11 + %32 = OpLoad %6 %31 + %33 = OpISub %6 %32 %28 + OpStore %31 %33 + OpBranch %17 + %17 = OpLabel + %35 = OpAccessChain %12 %25 %11 + %36 = OpLoad %6 %35 + %37 = OpIMul %6 %36 %34 + %38 = OpAccessChain %12 %25 %11 + OpStore %38 %37 + OpBranch %22 + %15 = OpLabel + %26 = OpAccessChain %12 %25 %11 + %27 = OpLoad %6 %26 + %29 = OpIAdd %6 %27 %28 + OpStore %26 %29 + OpBranch %22 + %21 = OpLabel + %54 = OpAccessChain %12 %25 %11 + %55 = OpLoad %6 %54 + %56 = OpIAdd %6 %55 %34 + %57 = OpAccessChain %12 %25 %11 + OpStore %57 %56 + OpBranch %22 + %22 = OpLabel + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, ReorderedSwitchBlocksNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpMemberDecorate %7 0 Offset 0 + OpDecorate %7 Block + OpDecorate %9 DescriptorSet 0 + OpDecorate %9 Binding 0 + OpMemberDecorate %23 0 Offset 0 + OpDecorate %23 BufferBlock + OpDecorate %25 DescriptorSet 0 + OpDecorate %25 Binding 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpTypeStruct %6 + %8 = OpTypePointer Uniform %7 + %9 = OpVariable %8 Uniform + %10 = OpTypeInt 32 1 + %11 = OpConstant %10 0 + %12 = OpTypePointer Uniform %6 + %23 = OpTypeStruct %6 + %24 = OpTypePointer Uniform %23 + %25 = OpVariable %24 Uniform + %28 = OpConstant %10 1 + %34 = OpConstant %6 2 + %52 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %13 = OpAccessChain %12 %9 %11 + %14 = OpLoad %6 %13 + OpSelectionMerge %22 None + OpSwitch %14 %21 0 %15 1 %16 2 %17 3 %18 4 %19 5 %20 + %21 = OpLabel + %54 = OpAccessChain %12 %25 %11 + %55 = OpLoad %6 %54 + %56 = OpIAdd %6 %55 %34 + %57 = OpAccessChain %12 %25 %11 + OpStore %57 %56 + OpBranch %22 + %15 = OpLabel + %26 = OpAccessChain %12 %25 %11 + %27 = OpLoad %6 %26 + %29 = OpIAdd %6 %27 %28 + OpStore %26 %29 + OpBranch %22 + %16 = OpLabel + %31 = OpAccessChain %12 %25 %11 + %32 = OpLoad %6 %31 + %33 = OpISub %6 %32 %28 + OpStore %31 %33 + OpBranch %17 + %17 = OpLabel + %35 = OpAccessChain %12 %25 %11 + %36 = OpLoad %6 %35 + %37 = OpIMul %6 %36 %34 + %38 = OpAccessChain %12 %25 %11 + OpStore %38 %37 + OpBranch %22 + %18 = OpLabel + %40 = OpAccessChain %12 %25 %11 + %41 = OpLoad %6 %40 + %42 = OpUDiv %6 %41 %34 + %43 = OpAccessChain %12 %25 %11 + OpStore %43 %42 + OpBranch %22 + %19 = OpLabel + %45 = OpAccessChain %12 %25 %11 + %46 = OpLoad %6 %45 + %47 = OpAccessChain %12 %25 %11 + %48 = OpLoad %6 %47 + %49 = OpIMul %6 %46 %48 + %50 = OpAccessChain %12 %25 %11 + OpStore %50 %49 + OpBranch %22 + %20 = OpLabel + %53 = OpAccessChain %12 %25 %11 + OpStore %53 %52 + OpBranch %21 + %22 = OpLabel + OpReturn + OpFunctionEnd +)"; + constexpr char kDstNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpMemberDecorate %7 0 Offset 0 + OpDecorate %7 Block + OpDecorate %9 DescriptorSet 0 + OpDecorate %9 Binding 0 + OpMemberDecorate %23 0 Offset 0 + OpDecorate %23 BufferBlock + OpDecorate %25 DescriptorSet 0 + OpDecorate %25 Binding 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpTypeStruct %6 + %8 = OpTypePointer Uniform %7 + %9 = OpVariable %8 Uniform + %10 = OpTypeInt 32 1 + %11 = OpConstant %10 0 + %12 = OpTypePointer Uniform %6 + %23 = OpTypeStruct %6 + %24 = OpTypePointer Uniform %23 + %25 = OpVariable %24 Uniform + %28 = OpConstant %10 1 + %34 = OpConstant %6 2 + %52 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %13 = OpAccessChain %12 %9 %11 + %14 = OpLoad %6 %13 + OpSelectionMerge %22 None + OpSwitch %14 %21 0 %15 1 %16 2 %17 3 %18 4 %19 5 %20 + %17 = OpLabel + %35 = OpAccessChain %12 %25 %11 + %36 = OpLoad %6 %35 + %37 = OpIMul %6 %36 %34 + %38 = OpAccessChain %12 %25 %11 + OpStore %38 %37 + OpBranch %22 + %18 = OpLabel + %40 = OpAccessChain %12 %25 %11 + %41 = OpLoad %6 %40 + %42 = OpUDiv %6 %41 %34 + %43 = OpAccessChain %12 %25 %11 + OpStore %43 %42 + OpBranch %22 + %21 = OpLabel + %54 = OpAccessChain %12 %25 %11 + %55 = OpLoad %6 %54 + %56 = OpIAdd %6 %55 %34 + %57 = OpAccessChain %12 %25 %11 + OpStore %57 %56 + OpBranch %22 + %20 = OpLabel + %53 = OpAccessChain %12 %25 %11 + OpStore %53 %52 + OpBranch %21 + %15 = OpLabel + %26 = OpAccessChain %12 %25 %11 + %27 = OpLoad %6 %26 + %29 = OpIAdd %6 %27 %28 + OpStore %26 %29 + OpBranch %22 + %19 = OpLabel + %45 = OpAccessChain %12 %25 %11 + %46 = OpLoad %6 %45 + %47 = OpAccessChain %12 %25 %11 + %48 = OpLoad %6 %47 + %49 = OpIMul %6 %46 %48 + %50 = OpAccessChain %12 %25 %11 + OpStore %50 %49 + OpBranch %22 + %16 = OpLabel + %31 = OpAccessChain %12 %25 %11 + %32 = OpLoad %6 %31 + %33 = OpISub %6 %32 %28 + OpStore %31 %33 + OpBranch %17 + %22 = OpLabel + OpReturn + OpFunctionEnd +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 58 ++; Bound: 62 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpMemberDecorate %7 0 Offset 0 + OpDecorate %7 Block + OpDecorate %9 DescriptorSet 0 + OpDecorate %9 Binding 0 + OpMemberDecorate %23 0 Offset 0 + OpDecorate %23 BufferBlock + OpDecorate %25 DescriptorSet 0 + OpDecorate %25 Binding 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpTypeStruct %6 + %8 = OpTypePointer Uniform %7 + %9 = OpVariable %8 Uniform + %10 = OpTypeInt 32 1 + %11 = OpConstant %10 0 + %12 = OpTypePointer Uniform %6 + %23 = OpTypeStruct %6 + %24 = OpTypePointer Uniform %23 + %25 = OpVariable %24 Uniform + %28 = OpConstant %10 1 + %34 = OpConstant %6 2 + %52 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %13 = OpAccessChain %12 %9 %11 + %14 = OpLoad %6 %13 + OpSelectionMerge %22 None + OpSwitch %14 %21 0 %15 1 %16 2 %17 3 %18 4 %19 5 %20 + %20 = OpLabel + %53 = OpAccessChain %12 %25 %11 + OpStore %53 %52 + OpBranch %21 + %19 = OpLabel + %45 = OpAccessChain %12 %25 %11 + %46 = OpLoad %6 %45 + %47 = OpAccessChain %12 %25 %11 + %48 = OpLoad %6 %47 + %49 = OpIMul %6 %46 %48 + %50 = OpAccessChain %12 %25 %11 + OpStore %50 %49 + OpBranch %22 + %18 = OpLabel + %40 = OpAccessChain %12 %25 %11 + %41 = OpLoad %6 %40 + %42 = OpUDiv %6 %41 %34 + %43 = OpAccessChain %12 %25 %11 + OpStore %43 %42 + OpBranch %22 + %16 = OpLabel + %31 = OpAccessChain %12 %25 %11 + %32 = OpLoad %6 %31 + %33 = OpISub %6 %32 %28 + OpStore %31 %33 + OpBranch %17 + %17 = OpLabel + %35 = OpAccessChain %12 %25 %11 + %36 = OpLoad %6 %35 + %37 = OpIMul %6 %36 %34 + %38 = OpAccessChain %12 %25 %11 + OpStore %38 %37 + OpBranch %22 + %15 = OpLabel + %26 = OpAccessChain %12 %25 %11 + %27 = OpLoad %6 %26 + %29 = OpIAdd %6 %27 %28 + OpStore %26 %29 + OpBranch %22 + %21 = OpLabel + %54 = OpAccessChain %12 %25 %11 + %55 = OpLoad %6 %54 + %56 = OpIAdd %6 %55 %34 + %57 = OpAccessChain %12 %25 %11 + OpStore %57 %56 + OpBranch %22 + %22 = OpLabel + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/reordered_switch_blocks_dst.spvasm b/test/diff/diff_files/reordered_switch_blocks_dst.spvasm new file mode 100644 index 00000000..8eabb4ef --- /dev/null +++ b/test/diff/diff_files/reordered_switch_blocks_dst.spvasm @@ -0,0 +1,91 @@ + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %7 "BufferIn" + OpMemberName %7 0 "i" + OpName %9 "" + OpName %23 "BufferOut" + OpMemberName %23 0 "o" + OpName %25 "" + OpMemberDecorate %7 0 Offset 0 + OpDecorate %7 Block + OpDecorate %9 DescriptorSet 0 + OpDecorate %9 Binding 0 + OpMemberDecorate %23 0 Offset 0 + OpDecorate %23 BufferBlock + OpDecorate %25 DescriptorSet 0 + OpDecorate %25 Binding 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpTypeStruct %6 + %8 = OpTypePointer Uniform %7 + %9 = OpVariable %8 Uniform + %10 = OpTypeInt 32 1 + %11 = OpConstant %10 0 + %12 = OpTypePointer Uniform %6 + %23 = OpTypeStruct %6 + %24 = OpTypePointer Uniform %23 + %25 = OpVariable %24 Uniform + %28 = OpConstant %10 1 + %34 = OpConstant %6 2 + %52 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %13 = OpAccessChain %12 %9 %11 + %14 = OpLoad %6 %13 + OpSelectionMerge %22 None + OpSwitch %14 %21 0 %15 1 %16 2 %17 3 %18 4 %19 5 %20 + %17 = OpLabel + %35 = OpAccessChain %12 %25 %11 + %36 = OpLoad %6 %35 + %37 = OpIMul %6 %36 %34 + %38 = OpAccessChain %12 %25 %11 + OpStore %38 %37 + OpBranch %22 + %18 = OpLabel + %40 = OpAccessChain %12 %25 %11 + %41 = OpLoad %6 %40 + %42 = OpUDiv %6 %41 %34 + %43 = OpAccessChain %12 %25 %11 + OpStore %43 %42 + OpBranch %22 + %21 = OpLabel + %54 = OpAccessChain %12 %25 %11 + %55 = OpLoad %6 %54 + %56 = OpIAdd %6 %55 %34 + %57 = OpAccessChain %12 %25 %11 + OpStore %57 %56 + OpBranch %22 + %20 = OpLabel + %53 = OpAccessChain %12 %25 %11 + OpStore %53 %52 + OpBranch %21 + %15 = OpLabel + %26 = OpAccessChain %12 %25 %11 + %27 = OpLoad %6 %26 + %29 = OpIAdd %6 %27 %28 + OpStore %26 %29 + OpBranch %22 + %19 = OpLabel + %45 = OpAccessChain %12 %25 %11 + %46 = OpLoad %6 %45 + %47 = OpAccessChain %12 %25 %11 + %48 = OpLoad %6 %47 + %49 = OpIMul %6 %46 %48 + %50 = OpAccessChain %12 %25 %11 + OpStore %50 %49 + OpBranch %22 + %16 = OpLabel + %31 = OpAccessChain %12 %25 %11 + %32 = OpLoad %6 %31 + %33 = OpISub %6 %32 %28 + OpStore %31 %33 + OpBranch %17 + %22 = OpLabel + OpReturn + OpFunctionEnd diff --git a/test/diff/diff_files/reordered_switch_blocks_src.spvasm b/test/diff/diff_files/reordered_switch_blocks_src.spvasm new file mode 100644 index 00000000..e3772282 --- /dev/null +++ b/test/diff/diff_files/reordered_switch_blocks_src.spvasm @@ -0,0 +1,92 @@ +;; Test where src and dst have cases of a switch in different order. + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %7 "BufferIn" + OpMemberName %7 0 "i" + OpName %9 "" + OpName %23 "BufferOut" + OpMemberName %23 0 "o" + OpName %25 "" + OpMemberDecorate %7 0 Offset 0 + OpDecorate %7 Block + OpDecorate %9 DescriptorSet 0 + OpDecorate %9 Binding 0 + OpMemberDecorate %23 0 Offset 0 + OpDecorate %23 BufferBlock + OpDecorate %25 DescriptorSet 0 + OpDecorate %25 Binding 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpTypeStruct %6 + %8 = OpTypePointer Uniform %7 + %9 = OpVariable %8 Uniform + %10 = OpTypeInt 32 1 + %11 = OpConstant %10 0 + %12 = OpTypePointer Uniform %6 + %23 = OpTypeStruct %6 + %24 = OpTypePointer Uniform %23 + %25 = OpVariable %24 Uniform + %28 = OpConstant %10 1 + %34 = OpConstant %6 2 + %52 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %13 = OpAccessChain %12 %9 %11 + %14 = OpLoad %6 %13 + OpSelectionMerge %22 None + OpSwitch %14 %21 0 %15 1 %16 2 %17 3 %18 4 %19 5 %20 + %21 = OpLabel + %54 = OpAccessChain %12 %25 %11 + %55 = OpLoad %6 %54 + %56 = OpIAdd %6 %55 %34 + %57 = OpAccessChain %12 %25 %11 + OpStore %57 %56 + OpBranch %22 + %15 = OpLabel + %26 = OpAccessChain %12 %25 %11 + %27 = OpLoad %6 %26 + %29 = OpIAdd %6 %27 %28 + OpStore %26 %29 + OpBranch %22 + %16 = OpLabel + %31 = OpAccessChain %12 %25 %11 + %32 = OpLoad %6 %31 + %33 = OpISub %6 %32 %28 + OpStore %31 %33 + OpBranch %17 + %17 = OpLabel + %35 = OpAccessChain %12 %25 %11 + %36 = OpLoad %6 %35 + %37 = OpIMul %6 %36 %34 + %38 = OpAccessChain %12 %25 %11 + OpStore %38 %37 + OpBranch %22 + %18 = OpLabel + %40 = OpAccessChain %12 %25 %11 + %41 = OpLoad %6 %40 + %42 = OpUDiv %6 %41 %34 + %43 = OpAccessChain %12 %25 %11 + OpStore %43 %42 + OpBranch %22 + %19 = OpLabel + %45 = OpAccessChain %12 %25 %11 + %46 = OpLoad %6 %45 + %47 = OpAccessChain %12 %25 %11 + %48 = OpLoad %6 %47 + %49 = OpIMul %6 %46 %48 + %50 = OpAccessChain %12 %25 %11 + OpStore %50 %49 + OpBranch %22 + %20 = OpLabel + %53 = OpAccessChain %12 %25 %11 + OpStore %53 %52 + OpBranch %21 + %22 = OpLabel + OpReturn + OpFunctionEnd diff --git a/test/diff/diff_files/small_functions_small_diffs_autogen.cpp b/test/diff/diff_files/small_functions_small_diffs_autogen.cpp new file mode 100644 index 00000000..c1a91009 --- /dev/null +++ b/test/diff/diff_files/small_functions_small_diffs_autogen.cpp @@ -0,0 +1,747 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Test where src and dst have many small functions with small differences. +constexpr char kSrc[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "f1(" + OpName %8 "f2(" + OpName %10 "f3(" + OpName %12 "f4(" + OpName %14 "f5(" + OpName %17 "BufferOut" + OpMemberName %17 0 "o" + OpName %19 "" + OpName %22 "BufferIn" + OpMemberName %22 0 "i" + OpName %24 "" + OpMemberDecorate %17 0 Offset 0 + OpDecorate %17 BufferBlock + OpDecorate %19 DescriptorSet 0 + OpDecorate %19 Binding 1 + OpMemberDecorate %22 0 Offset 0 + OpDecorate %22 Block + OpDecorate %24 DescriptorSet 0 + OpDecorate %24 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %16 = OpTypeInt 32 0 + %17 = OpTypeStruct %16 + %18 = OpTypePointer Uniform %17 + %19 = OpVariable %18 Uniform + %20 = OpTypeInt 32 1 + %21 = OpConstant %20 0 + %22 = OpTypeStruct %16 + %23 = OpTypePointer Uniform %22 + %24 = OpVariable %23 Uniform + %25 = OpTypePointer Uniform %16 + %31 = OpConstant %20 1 + %36 = OpConstant %16 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %45 = OpFunctionCall %2 %6 + %46 = OpFunctionCall %2 %8 + %47 = OpFunctionCall %2 %10 + %48 = OpFunctionCall %2 %12 + %49 = OpFunctionCall %2 %14 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %26 = OpAccessChain %25 %24 %21 + %27 = OpLoad %16 %26 + %28 = OpAccessChain %25 %19 %21 + OpStore %28 %27 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %29 = OpAccessChain %25 %19 %21 + %30 = OpLoad %16 %29 + %32 = OpIAdd %16 %30 %31 + OpStore %29 %32 + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %3 + %11 = OpLabel + %33 = OpAccessChain %25 %19 %21 + %34 = OpLoad %16 %33 + %35 = OpISub %16 %34 %31 + OpStore %33 %35 + OpReturn + OpFunctionEnd + %12 = OpFunction %2 None %3 + %13 = OpLabel + %37 = OpAccessChain %25 %19 %21 + %38 = OpLoad %16 %37 + %39 = OpIMul %16 %38 %36 + %40 = OpAccessChain %25 %19 %21 + OpStore %40 %39 + OpReturn + OpFunctionEnd + %14 = OpFunction %2 None %3 + %15 = OpLabel + %41 = OpAccessChain %25 %19 %21 + %42 = OpLoad %16 %41 + %43 = OpUDiv %16 %42 %36 + %44 = OpAccessChain %25 %19 %21 + OpStore %44 %43 + OpReturn + OpFunctionEnd +)"; +constexpr char kDst[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "f1(" + OpName %8 "f2(" + OpName %10 "f3(" + OpName %12 "f4(" + OpName %14 "f5(" + OpName %17 "BufferOut" + OpMemberName %17 0 "o" + OpName %19 "" + OpName %22 "BufferIn" + OpMemberName %22 0 "i" + OpName %24 "" + OpMemberDecorate %17 0 Offset 0 + OpDecorate %17 BufferBlock + OpDecorate %19 DescriptorSet 0 + OpDecorate %19 Binding 1 + OpMemberDecorate %22 0 Offset 0 + OpDecorate %22 Block + OpDecorate %24 DescriptorSet 0 + OpDecorate %24 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %16 = OpTypeInt 32 0 + %17 = OpTypeStruct %16 + %18 = OpTypePointer Uniform %17 + %19 = OpVariable %18 Uniform + %20 = OpTypeInt 32 1 + %21 = OpConstant %20 0 + %22 = OpTypeStruct %16 + %23 = OpTypePointer Uniform %22 + %24 = OpVariable %23 Uniform + %25 = OpTypePointer Uniform %16 + %31 = OpConstant %20 1 + %36 = OpConstant %16 2 + %6 = OpFunction %2 None %3 + %7 = OpLabel + %26 = OpAccessChain %25 %24 %21 + %27 = OpLoad %16 %26 + %28 = OpAccessChain %25 %19 %21 + OpStore %28 %27 + OpReturn + OpFunctionEnd + %14 = OpFunction %2 None %3 + %15 = OpLabel + %41 = OpAccessChain %25 %19 %21 + %42 = OpLoad %16 %41 + %43 = OpIAdd %16 %42 %36 + %44 = OpAccessChain %25 %19 %21 + OpStore %44 %43 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %29 = OpAccessChain %25 %19 %21 + %30 = OpLoad %16 %29 + %32 = OpISub %16 %30 %31 + OpStore %29 %32 + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %3 + %11 = OpLabel + %33 = OpAccessChain %25 %19 %21 + %34 = OpLoad %16 %33 + %35 = OpIAdd %16 %34 %31 + OpStore %33 %35 + OpReturn + OpFunctionEnd + %4 = OpFunction %2 None %3 + %5 = OpLabel + %45 = OpFunctionCall %2 %6 + %46 = OpFunctionCall %2 %8 + %47 = OpFunctionCall %2 %10 + %48 = OpFunctionCall %2 %12 + %49 = OpFunctionCall %2 %14 + OpReturn + OpFunctionEnd + %12 = OpFunction %2 None %3 + %13 = OpLabel + %37 = OpAccessChain %25 %19 %21 + %38 = OpLoad %16 %37 + %39 = OpISub %16 %38 %36 + %40 = OpAccessChain %25 %19 %21 + OpStore %40 %39 + OpReturn + OpFunctionEnd + +)"; + +TEST(DiffTest, SmallFunctionsSmallDiffs) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 50 ++; Bound: 54 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "f1(" + OpName %8 "f2(" + OpName %10 "f3(" + OpName %12 "f4(" + OpName %14 "f5(" + OpName %17 "BufferOut" + OpMemberName %17 0 "o" + OpName %19 "" + OpName %22 "BufferIn" + OpMemberName %22 0 "i" + OpName %24 "" + OpMemberDecorate %17 0 Offset 0 + OpDecorate %17 BufferBlock + OpDecorate %19 DescriptorSet 0 + OpDecorate %19 Binding 1 + OpMemberDecorate %22 0 Offset 0 + OpDecorate %22 Block + OpDecorate %24 DescriptorSet 0 + OpDecorate %24 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %16 = OpTypeInt 32 0 + %17 = OpTypeStruct %16 + %18 = OpTypePointer Uniform %17 + %19 = OpVariable %18 Uniform + %20 = OpTypeInt 32 1 + %21 = OpConstant %20 0 + %22 = OpTypeStruct %16 + %23 = OpTypePointer Uniform %22 + %24 = OpVariable %23 Uniform + %25 = OpTypePointer Uniform %16 + %31 = OpConstant %20 1 + %36 = OpConstant %16 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %45 = OpFunctionCall %2 %6 + %46 = OpFunctionCall %2 %8 + %47 = OpFunctionCall %2 %10 + %48 = OpFunctionCall %2 %12 + %49 = OpFunctionCall %2 %14 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %26 = OpAccessChain %25 %24 %21 + %27 = OpLoad %16 %26 + %28 = OpAccessChain %25 %19 %21 + OpStore %28 %27 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %29 = OpAccessChain %25 %19 %21 + %30 = OpLoad %16 %29 +-%32 = OpIAdd %16 %30 %31 ++%50 = OpISub %16 %30 %31 +-OpStore %29 %32 ++OpStore %29 %50 + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %3 + %11 = OpLabel + %33 = OpAccessChain %25 %19 %21 + %34 = OpLoad %16 %33 +-%35 = OpISub %16 %34 %31 ++%51 = OpIAdd %16 %34 %31 +-OpStore %33 %35 ++OpStore %33 %51 + OpReturn + OpFunctionEnd + %12 = OpFunction %2 None %3 + %13 = OpLabel + %37 = OpAccessChain %25 %19 %21 + %38 = OpLoad %16 %37 +-%39 = OpIMul %16 %38 %36 ++%52 = OpISub %16 %38 %36 + %40 = OpAccessChain %25 %19 %21 +-OpStore %40 %39 ++OpStore %40 %52 + OpReturn + OpFunctionEnd + %14 = OpFunction %2 None %3 + %15 = OpLabel + %41 = OpAccessChain %25 %19 %21 + %42 = OpLoad %16 %41 +-%43 = OpUDiv %16 %42 %36 ++%53 = OpIAdd %16 %42 %36 + %44 = OpAccessChain %25 %19 %21 +-OpStore %44 %43 ++OpStore %44 %53 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, SmallFunctionsSmallDiffsNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpMemberDecorate %17 0 Offset 0 + OpDecorate %17 BufferBlock + OpDecorate %19 DescriptorSet 0 + OpDecorate %19 Binding 1 + OpMemberDecorate %22 0 Offset 0 + OpDecorate %22 Block + OpDecorate %24 DescriptorSet 0 + OpDecorate %24 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %16 = OpTypeInt 32 0 + %17 = OpTypeStruct %16 + %18 = OpTypePointer Uniform %17 + %19 = OpVariable %18 Uniform + %20 = OpTypeInt 32 1 + %21 = OpConstant %20 0 + %22 = OpTypeStruct %16 + %23 = OpTypePointer Uniform %22 + %24 = OpVariable %23 Uniform + %25 = OpTypePointer Uniform %16 + %31 = OpConstant %20 1 + %36 = OpConstant %16 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %45 = OpFunctionCall %2 %6 + %46 = OpFunctionCall %2 %8 + %47 = OpFunctionCall %2 %10 + %48 = OpFunctionCall %2 %12 + %49 = OpFunctionCall %2 %14 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %26 = OpAccessChain %25 %24 %21 + %27 = OpLoad %16 %26 + %28 = OpAccessChain %25 %19 %21 + OpStore %28 %27 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %29 = OpAccessChain %25 %19 %21 + %30 = OpLoad %16 %29 + %32 = OpIAdd %16 %30 %31 + OpStore %29 %32 + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %3 + %11 = OpLabel + %33 = OpAccessChain %25 %19 %21 + %34 = OpLoad %16 %33 + %35 = OpISub %16 %34 %31 + OpStore %33 %35 + OpReturn + OpFunctionEnd + %12 = OpFunction %2 None %3 + %13 = OpLabel + %37 = OpAccessChain %25 %19 %21 + %38 = OpLoad %16 %37 + %39 = OpIMul %16 %38 %36 + %40 = OpAccessChain %25 %19 %21 + OpStore %40 %39 + OpReturn + OpFunctionEnd + %14 = OpFunction %2 None %3 + %15 = OpLabel + %41 = OpAccessChain %25 %19 %21 + %42 = OpLoad %16 %41 + %43 = OpUDiv %16 %42 %36 + %44 = OpAccessChain %25 %19 %21 + OpStore %44 %43 + OpReturn + OpFunctionEnd + +)"; + constexpr char kDstNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpMemberDecorate %17 0 Offset 0 + OpDecorate %17 BufferBlock + OpDecorate %19 DescriptorSet 0 + OpDecorate %19 Binding 1 + OpMemberDecorate %22 0 Offset 0 + OpDecorate %22 Block + OpDecorate %24 DescriptorSet 0 + OpDecorate %24 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %16 = OpTypeInt 32 0 + %17 = OpTypeStruct %16 + %18 = OpTypePointer Uniform %17 + %19 = OpVariable %18 Uniform + %20 = OpTypeInt 32 1 + %21 = OpConstant %20 0 + %22 = OpTypeStruct %16 + %23 = OpTypePointer Uniform %22 + %24 = OpVariable %23 Uniform + %25 = OpTypePointer Uniform %16 + %31 = OpConstant %20 1 + %36 = OpConstant %16 2 + %6 = OpFunction %2 None %3 + %7 = OpLabel + %26 = OpAccessChain %25 %24 %21 + %27 = OpLoad %16 %26 + %28 = OpAccessChain %25 %19 %21 + OpStore %28 %27 + OpReturn + OpFunctionEnd + %14 = OpFunction %2 None %3 + %15 = OpLabel + %41 = OpAccessChain %25 %19 %21 + %42 = OpLoad %16 %41 + %43 = OpIAdd %16 %42 %36 + %44 = OpAccessChain %25 %19 %21 + OpStore %44 %43 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %29 = OpAccessChain %25 %19 %21 + %30 = OpLoad %16 %29 + %32 = OpISub %16 %30 %31 + OpStore %29 %32 + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %3 + %11 = OpLabel + %33 = OpAccessChain %25 %19 %21 + %34 = OpLoad %16 %33 + %35 = OpIAdd %16 %34 %31 + OpStore %33 %35 + OpReturn + OpFunctionEnd + %4 = OpFunction %2 None %3 + %5 = OpLabel + %45 = OpFunctionCall %2 %6 + %46 = OpFunctionCall %2 %8 + %47 = OpFunctionCall %2 %10 + %48 = OpFunctionCall %2 %12 + %49 = OpFunctionCall %2 %14 + OpReturn + OpFunctionEnd + %12 = OpFunction %2 None %3 + %13 = OpLabel + %37 = OpAccessChain %25 %19 %21 + %38 = OpLoad %16 %37 + %39 = OpISub %16 %38 %36 + %40 = OpAccessChain %25 %19 %21 + OpStore %40 %39 + OpReturn + OpFunctionEnd + +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 50 ++; Bound: 52 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpMemberDecorate %17 0 Offset 0 + OpDecorate %17 BufferBlock + OpDecorate %19 DescriptorSet 0 + OpDecorate %19 Binding 1 + OpMemberDecorate %22 0 Offset 0 + OpDecorate %22 Block + OpDecorate %24 DescriptorSet 0 + OpDecorate %24 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %16 = OpTypeInt 32 0 + %17 = OpTypeStruct %16 + %18 = OpTypePointer Uniform %17 + %19 = OpVariable %18 Uniform + %20 = OpTypeInt 32 1 + %21 = OpConstant %20 0 + %22 = OpTypeStruct %16 + %23 = OpTypePointer Uniform %22 + %24 = OpVariable %23 Uniform + %25 = OpTypePointer Uniform %16 + %31 = OpConstant %20 1 + %36 = OpConstant %16 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %45 = OpFunctionCall %2 %6 +-%46 = OpFunctionCall %2 %8 ++%46 = OpFunctionCall %2 %10 +-%47 = OpFunctionCall %2 %10 ++%47 = OpFunctionCall %2 %8 + %48 = OpFunctionCall %2 %12 + %49 = OpFunctionCall %2 %14 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %26 = OpAccessChain %25 %24 %21 + %27 = OpLoad %16 %26 + %28 = OpAccessChain %25 %19 %21 + OpStore %28 %27 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %29 = OpAccessChain %25 %19 %21 + %30 = OpLoad %16 %29 + %32 = OpIAdd %16 %30 %31 + OpStore %29 %32 + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %3 + %11 = OpLabel + %33 = OpAccessChain %25 %19 %21 + %34 = OpLoad %16 %33 + %35 = OpISub %16 %34 %31 + OpStore %33 %35 + OpReturn + OpFunctionEnd + %12 = OpFunction %2 None %3 + %13 = OpLabel + %37 = OpAccessChain %25 %19 %21 + %38 = OpLoad %16 %37 +-%39 = OpIMul %16 %38 %36 ++%50 = OpISub %16 %38 %36 + %40 = OpAccessChain %25 %19 %21 +-OpStore %40 %39 ++OpStore %40 %50 + OpReturn + OpFunctionEnd + %14 = OpFunction %2 None %3 + %15 = OpLabel + %41 = OpAccessChain %25 %19 %21 + %42 = OpLoad %16 %41 +-%43 = OpUDiv %16 %42 %36 ++%51 = OpIAdd %16 %42 %36 + %44 = OpAccessChain %25 %19 %21 +-OpStore %44 %43 ++OpStore %44 %51 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +TEST(DiffTest, SmallFunctionsSmallDiffsDumpIds) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 50 ++; Bound: 54 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "f1(" + OpName %8 "f2(" + OpName %10 "f3(" + OpName %12 "f4(" + OpName %14 "f5(" + OpName %17 "BufferOut" + OpMemberName %17 0 "o" + OpName %19 "" + OpName %22 "BufferIn" + OpMemberName %22 0 "i" + OpName %24 "" + OpMemberDecorate %17 0 Offset 0 + OpDecorate %17 BufferBlock + OpDecorate %19 DescriptorSet 0 + OpDecorate %19 Binding 1 + OpMemberDecorate %22 0 Offset 0 + OpDecorate %22 Block + OpDecorate %24 DescriptorSet 0 + OpDecorate %24 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %16 = OpTypeInt 32 0 + %17 = OpTypeStruct %16 + %18 = OpTypePointer Uniform %17 + %19 = OpVariable %18 Uniform + %20 = OpTypeInt 32 1 + %21 = OpConstant %20 0 + %22 = OpTypeStruct %16 + %23 = OpTypePointer Uniform %22 + %24 = OpVariable %23 Uniform + %25 = OpTypePointer Uniform %16 + %31 = OpConstant %20 1 + %36 = OpConstant %16 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %45 = OpFunctionCall %2 %6 + %46 = OpFunctionCall %2 %8 + %47 = OpFunctionCall %2 %10 + %48 = OpFunctionCall %2 %12 + %49 = OpFunctionCall %2 %14 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %26 = OpAccessChain %25 %24 %21 + %27 = OpLoad %16 %26 + %28 = OpAccessChain %25 %19 %21 + OpStore %28 %27 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %29 = OpAccessChain %25 %19 %21 + %30 = OpLoad %16 %29 +-%32 = OpIAdd %16 %30 %31 ++%50 = OpISub %16 %30 %31 +-OpStore %29 %32 ++OpStore %29 %50 + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %3 + %11 = OpLabel + %33 = OpAccessChain %25 %19 %21 + %34 = OpLoad %16 %33 +-%35 = OpISub %16 %34 %31 ++%51 = OpIAdd %16 %34 %31 +-OpStore %33 %35 ++OpStore %33 %51 + OpReturn + OpFunctionEnd + %12 = OpFunction %2 None %3 + %13 = OpLabel + %37 = OpAccessChain %25 %19 %21 + %38 = OpLoad %16 %37 +-%39 = OpIMul %16 %38 %36 ++%52 = OpISub %16 %38 %36 + %40 = OpAccessChain %25 %19 %21 +-OpStore %40 %39 ++OpStore %40 %52 + OpReturn + OpFunctionEnd + %14 = OpFunction %2 None %3 + %15 = OpLabel + %41 = OpAccessChain %25 %19 %21 + %42 = OpLoad %16 %41 +-%43 = OpUDiv %16 %42 %36 ++%53 = OpIAdd %16 %42 %36 + %44 = OpAccessChain %25 %19 %21 +-OpStore %44 %43 ++OpStore %44 %53 + OpReturn + OpFunctionEnd + Src -> Dst + 1 -> 1 [ExtInstImport] + 2 -> 2 [TypeVoid] + 3 -> 3 [TypeFunction] + 4 -> 4 [Function] + 5 -> 5 [Label] + 6 -> 6 [Function] + 7 -> 7 [Label] + 8 -> 8 [Function] + 9 -> 9 [Label] + 10 -> 10 [Function] + 11 -> 11 [Label] + 12 -> 12 [Function] + 13 -> 13 [Label] + 14 -> 14 [Function] + 15 -> 15 [Label] + 16 -> 16 [TypeInt] + 17 -> 17 [TypeStruct] + 18 -> 18 [TypePointer] + 19 -> 19 [Variable] + 20 -> 20 [TypeInt] + 21 -> 21 [Constant] + 22 -> 22 [TypeStruct] + 23 -> 23 [TypePointer] + 24 -> 24 [Variable] + 25 -> 25 [TypePointer] + 26 -> 26 [AccessChain] + 27 -> 27 [Load] + 28 -> 28 [AccessChain] + 29 -> 29 [AccessChain] + 30 -> 30 [Load] + 31 -> 31 [Constant] + 32 -> 50 [IAdd] + 33 -> 33 [AccessChain] + 34 -> 34 [Load] + 35 -> 51 [ISub] + 36 -> 36 [Constant] + 37 -> 37 [AccessChain] + 38 -> 38 [Load] + 39 -> 52 [IMul] + 40 -> 40 [AccessChain] + 41 -> 41 [AccessChain] + 42 -> 42 [Load] + 43 -> 53 [UDiv] + 44 -> 44 [AccessChain] + 45 -> 45 [FunctionCall] + 46 -> 46 [FunctionCall] + 47 -> 47 [FunctionCall] + 48 -> 48 [FunctionCall] + 49 -> 49 [FunctionCall] +)"; + Options options; + options.dump_id_map = true; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/small_functions_small_diffs_dst.spvasm b/test/diff/diff_files/small_functions_small_diffs_dst.spvasm new file mode 100644 index 00000000..fabf5692 --- /dev/null +++ b/test/diff/diff_files/small_functions_small_diffs_dst.spvasm @@ -0,0 +1,92 @@ + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "f1(" + OpName %8 "f2(" + OpName %10 "f3(" + OpName %12 "f4(" + OpName %14 "f5(" + OpName %17 "BufferOut" + OpMemberName %17 0 "o" + OpName %19 "" + OpName %22 "BufferIn" + OpMemberName %22 0 "i" + OpName %24 "" + OpMemberDecorate %17 0 Offset 0 + OpDecorate %17 BufferBlock + OpDecorate %19 DescriptorSet 0 + OpDecorate %19 Binding 1 + OpMemberDecorate %22 0 Offset 0 + OpDecorate %22 Block + OpDecorate %24 DescriptorSet 0 + OpDecorate %24 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %16 = OpTypeInt 32 0 + %17 = OpTypeStruct %16 + %18 = OpTypePointer Uniform %17 + %19 = OpVariable %18 Uniform + %20 = OpTypeInt 32 1 + %21 = OpConstant %20 0 + %22 = OpTypeStruct %16 + %23 = OpTypePointer Uniform %22 + %24 = OpVariable %23 Uniform + %25 = OpTypePointer Uniform %16 + %31 = OpConstant %20 1 + %36 = OpConstant %16 2 + %6 = OpFunction %2 None %3 + %7 = OpLabel + %26 = OpAccessChain %25 %24 %21 + %27 = OpLoad %16 %26 + %28 = OpAccessChain %25 %19 %21 + OpStore %28 %27 + OpReturn + OpFunctionEnd + %14 = OpFunction %2 None %3 + %15 = OpLabel + %41 = OpAccessChain %25 %19 %21 + %42 = OpLoad %16 %41 + %43 = OpIAdd %16 %42 %36 + %44 = OpAccessChain %25 %19 %21 + OpStore %44 %43 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %29 = OpAccessChain %25 %19 %21 + %30 = OpLoad %16 %29 + %32 = OpISub %16 %30 %31 + OpStore %29 %32 + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %3 + %11 = OpLabel + %33 = OpAccessChain %25 %19 %21 + %34 = OpLoad %16 %33 + %35 = OpIAdd %16 %34 %31 + OpStore %33 %35 + OpReturn + OpFunctionEnd + %4 = OpFunction %2 None %3 + %5 = OpLabel + %45 = OpFunctionCall %2 %6 + %46 = OpFunctionCall %2 %8 + %47 = OpFunctionCall %2 %10 + %48 = OpFunctionCall %2 %12 + %49 = OpFunctionCall %2 %14 + OpReturn + OpFunctionEnd + %12 = OpFunction %2 None %3 + %13 = OpLabel + %37 = OpAccessChain %25 %19 %21 + %38 = OpLoad %16 %37 + %39 = OpISub %16 %38 %36 + %40 = OpAccessChain %25 %19 %21 + OpStore %40 %39 + OpReturn + OpFunctionEnd + diff --git a/test/diff/diff_files/small_functions_small_diffs_src.spvasm b/test/diff/diff_files/small_functions_small_diffs_src.spvasm new file mode 100644 index 00000000..895285b1 --- /dev/null +++ b/test/diff/diff_files/small_functions_small_diffs_src.spvasm @@ -0,0 +1,93 @@ +;; Test where src and dst have many small functions with small differences. + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "f1(" + OpName %8 "f2(" + OpName %10 "f3(" + OpName %12 "f4(" + OpName %14 "f5(" + OpName %17 "BufferOut" + OpMemberName %17 0 "o" + OpName %19 "" + OpName %22 "BufferIn" + OpMemberName %22 0 "i" + OpName %24 "" + OpMemberDecorate %17 0 Offset 0 + OpDecorate %17 BufferBlock + OpDecorate %19 DescriptorSet 0 + OpDecorate %19 Binding 1 + OpMemberDecorate %22 0 Offset 0 + OpDecorate %22 Block + OpDecorate %24 DescriptorSet 0 + OpDecorate %24 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %16 = OpTypeInt 32 0 + %17 = OpTypeStruct %16 + %18 = OpTypePointer Uniform %17 + %19 = OpVariable %18 Uniform + %20 = OpTypeInt 32 1 + %21 = OpConstant %20 0 + %22 = OpTypeStruct %16 + %23 = OpTypePointer Uniform %22 + %24 = OpVariable %23 Uniform + %25 = OpTypePointer Uniform %16 + %31 = OpConstant %20 1 + %36 = OpConstant %16 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %45 = OpFunctionCall %2 %6 + %46 = OpFunctionCall %2 %8 + %47 = OpFunctionCall %2 %10 + %48 = OpFunctionCall %2 %12 + %49 = OpFunctionCall %2 %14 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %26 = OpAccessChain %25 %24 %21 + %27 = OpLoad %16 %26 + %28 = OpAccessChain %25 %19 %21 + OpStore %28 %27 + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %29 = OpAccessChain %25 %19 %21 + %30 = OpLoad %16 %29 + %32 = OpIAdd %16 %30 %31 + OpStore %29 %32 + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %3 + %11 = OpLabel + %33 = OpAccessChain %25 %19 %21 + %34 = OpLoad %16 %33 + %35 = OpISub %16 %34 %31 + OpStore %33 %35 + OpReturn + OpFunctionEnd + %12 = OpFunction %2 None %3 + %13 = OpLabel + %37 = OpAccessChain %25 %19 %21 + %38 = OpLoad %16 %37 + %39 = OpIMul %16 %38 %36 + %40 = OpAccessChain %25 %19 %21 + OpStore %40 %39 + OpReturn + OpFunctionEnd + %14 = OpFunction %2 None %3 + %15 = OpLabel + %41 = OpAccessChain %25 %19 %21 + %42 = OpLoad %16 %41 + %43 = OpUDiv %16 %42 %36 + %44 = OpAccessChain %25 %19 %21 + OpStore %44 %43 + OpReturn + OpFunctionEnd + diff --git a/test/diff/diff_files/spec_constant_array_size_autogen.cpp b/test/diff/diff_files/spec_constant_array_size_autogen.cpp new file mode 100644 index 00000000..1962d27e --- /dev/null +++ b/test/diff/diff_files/spec_constant_array_size_autogen.cpp @@ -0,0 +1,310 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Tests that identical specialization constants are not matched with constants +// when used as array size. +constexpr char kSrc[] = R"(; SPIR-V +; Version: 1.0 +; Generator: Google ANGLE Shader Compiler; 0 +; Bound: 27 +; Schema: 0 +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %22 "main" %4 %19 +OpSource GLSL 450 +OpName %4 "_ua_position" +OpName %17 "gl_PerVertex" +OpMemberName %17 0 "gl_Position" +OpMemberName %17 1 "gl_PointSize" +OpMemberName %17 2 "gl_ClipDistance" +OpMemberName %17 3 "gl_CullDistance" +OpName %19 "" +OpName %22 "main" +OpDecorate %4 Location 0 +OpMemberDecorate %17 1 RelaxedPrecision +OpMemberDecorate %17 0 BuiltIn Position +OpMemberDecorate %17 1 BuiltIn PointSize +OpMemberDecorate %17 2 BuiltIn ClipDistance +OpMemberDecorate %17 3 BuiltIn CullDistance +OpDecorate %17 Block +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeInt 32 0 +%8 = OpTypeVector %5 4 +%15 = OpConstant %5 8 +%16 = OpTypeArray %1 %15 +%17 = OpTypeStruct %2 %1 %16 %16 +%20 = OpTypeVoid +%25 = OpConstant %5 0 +%3 = OpTypePointer Input %2 +%13 = OpTypePointer Output %2 +%18 = OpTypePointer Output %17 +%21 = OpTypeFunction %20 +%4 = OpVariable %3 Input +%19 = OpVariable %18 Output +%22 = OpFunction %20 None %21 +%23 = OpLabel +%24 = OpLoad %2 %4 +%26 = OpAccessChain %13 %19 %25 +OpStore %26 %24 +OpReturn +OpFunctionEnd)"; +constexpr char kDst[] = R"(; SPIR-V +; Version: 1.0 +; Generator: Google ANGLE Shader Compiler; 0 +; Bound: 27 +; Schema: 0 +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %22 "main" %4 %19 +OpSource GLSL 450 +OpName %4 "_ua_position" +OpName %17 "gl_PerVertex" +OpMemberName %17 0 "gl_Position" +OpMemberName %17 1 "gl_PointSize" +OpMemberName %17 2 "gl_ClipDistance" +OpMemberName %17 3 "gl_CullDistance" +OpName %19 "" +OpName %22 "main" +OpDecorate %4 Location 0 +OpDecorate %15 SpecId 4 +OpMemberDecorate %17 1 RelaxedPrecision +OpMemberDecorate %17 0 BuiltIn Position +OpMemberDecorate %17 1 BuiltIn PointSize +OpMemberDecorate %17 2 BuiltIn ClipDistance +OpMemberDecorate %17 3 BuiltIn CullDistance +OpDecorate %17 Block +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeInt 32 0 +%8 = OpTypeVector %5 4 +%15 = OpSpecConstant %5 8 +%16 = OpTypeArray %1 %15 +%17 = OpTypeStruct %2 %1 %16 %16 +%20 = OpTypeVoid +%25 = OpConstant %5 0 +%3 = OpTypePointer Input %2 +%13 = OpTypePointer Output %2 +%18 = OpTypePointer Output %17 +%21 = OpTypeFunction %20 +%4 = OpVariable %3 Input +%19 = OpVariable %18 Output +%22 = OpFunction %20 None %21 +%23 = OpLabel +%24 = OpLoad %2 %4 +%26 = OpAccessChain %13 %19 %25 +OpStore %26 %24 +OpReturn +OpFunctionEnd +)"; + +TEST(DiffTest, SpecConstantArraySize) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 27 ++; Bound: 36 + ; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %22 "main" %4 %19 + OpSource GLSL 450 + OpName %4 "_ua_position" + OpName %17 "gl_PerVertex" + OpMemberName %17 0 "gl_Position" + OpMemberName %17 1 "gl_PointSize" + OpMemberName %17 2 "gl_ClipDistance" + OpMemberName %17 3 "gl_CullDistance" + OpName %19 "" + OpName %22 "main" + OpDecorate %4 Location 0 ++OpDecorate %34 SpecId 4 + OpMemberDecorate %17 1 RelaxedPrecision + OpMemberDecorate %17 0 BuiltIn Position + OpMemberDecorate %17 1 BuiltIn PointSize + OpMemberDecorate %17 2 BuiltIn ClipDistance + OpMemberDecorate %17 3 BuiltIn CullDistance + OpDecorate %17 Block + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 + %5 = OpTypeInt 32 0 + %8 = OpTypeVector %5 4 +-%15 = OpConstant %5 8 +-%16 = OpTypeArray %1 %15 ++%34 = OpSpecConstant %5 8 ++%35 = OpTypeArray %1 %34 +-%17 = OpTypeStruct %2 %1 %16 %16 ++%17 = OpTypeStruct %2 %1 %35 %35 + %20 = OpTypeVoid + %25 = OpConstant %5 0 + %3 = OpTypePointer Input %2 + %13 = OpTypePointer Output %2 + %18 = OpTypePointer Output %17 + %21 = OpTypeFunction %20 + %4 = OpVariable %3 Input + %19 = OpVariable %18 Output + %22 = OpFunction %20 None %21 + %23 = OpLabel + %24 = OpLoad %2 %4 + %26 = OpAccessChain %13 %19 %25 + OpStore %26 %24 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, SpecConstantArraySizeNoDebug) { + constexpr char kSrcNoDebug[] = R"(; SPIR-V +; Version: 1.0 +; Generator: Google ANGLE Shader Compiler; 0 +; Bound: 27 +; Schema: 0 +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %22 "main" %4 %19 +OpSource GLSL 450 +OpDecorate %4 Location 0 +OpMemberDecorate %17 1 RelaxedPrecision +OpMemberDecorate %17 0 BuiltIn Position +OpMemberDecorate %17 1 BuiltIn PointSize +OpMemberDecorate %17 2 BuiltIn ClipDistance +OpMemberDecorate %17 3 BuiltIn CullDistance +OpDecorate %17 Block +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeInt 32 0 +%8 = OpTypeVector %5 4 +%15 = OpConstant %5 8 +%16 = OpTypeArray %1 %15 +%17 = OpTypeStruct %2 %1 %16 %16 +%20 = OpTypeVoid +%25 = OpConstant %5 0 +%3 = OpTypePointer Input %2 +%13 = OpTypePointer Output %2 +%18 = OpTypePointer Output %17 +%21 = OpTypeFunction %20 +%4 = OpVariable %3 Input +%19 = OpVariable %18 Output +%22 = OpFunction %20 None %21 +%23 = OpLabel +%24 = OpLoad %2 %4 +%26 = OpAccessChain %13 %19 %25 +OpStore %26 %24 +OpReturn +OpFunctionEnd +)"; + constexpr char kDstNoDebug[] = R"(; SPIR-V +; Version: 1.0 +; Generator: Google ANGLE Shader Compiler; 0 +; Bound: 27 +; Schema: 0 +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %22 "main" %4 %19 +OpSource GLSL 450 +OpDecorate %4 Location 0 +OpDecorate %15 SpecId 4 +OpMemberDecorate %17 1 RelaxedPrecision +OpMemberDecorate %17 0 BuiltIn Position +OpMemberDecorate %17 1 BuiltIn PointSize +OpMemberDecorate %17 2 BuiltIn ClipDistance +OpMemberDecorate %17 3 BuiltIn CullDistance +OpDecorate %17 Block +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeInt 32 0 +%8 = OpTypeVector %5 4 +%15 = OpSpecConstant %5 8 +%16 = OpTypeArray %1 %15 +%17 = OpTypeStruct %2 %1 %16 %16 +%20 = OpTypeVoid +%25 = OpConstant %5 0 +%3 = OpTypePointer Input %2 +%13 = OpTypePointer Output %2 +%18 = OpTypePointer Output %17 +%21 = OpTypeFunction %20 +%4 = OpVariable %3 Input +%19 = OpVariable %18 Output +%22 = OpFunction %20 None %21 +%23 = OpLabel +%24 = OpLoad %2 %4 +%26 = OpAccessChain %13 %19 %25 +OpStore %26 %24 +OpReturn +OpFunctionEnd +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 27 ++; Bound: 36 + ; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %22 "main" %4 %19 + OpSource GLSL 450 + OpDecorate %4 Location 0 ++OpDecorate %34 SpecId 4 + OpMemberDecorate %17 1 RelaxedPrecision + OpMemberDecorate %17 0 BuiltIn Position + OpMemberDecorate %17 1 BuiltIn PointSize + OpMemberDecorate %17 2 BuiltIn ClipDistance + OpMemberDecorate %17 3 BuiltIn CullDistance + OpDecorate %17 Block + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 + %5 = OpTypeInt 32 0 + %8 = OpTypeVector %5 4 +-%15 = OpConstant %5 8 +-%16 = OpTypeArray %1 %15 ++%34 = OpSpecConstant %5 8 ++%35 = OpTypeArray %1 %34 +-%17 = OpTypeStruct %2 %1 %16 %16 ++%17 = OpTypeStruct %2 %1 %35 %35 + %20 = OpTypeVoid + %25 = OpConstant %5 0 + %3 = OpTypePointer Input %2 + %13 = OpTypePointer Output %2 + %18 = OpTypePointer Output %17 + %21 = OpTypeFunction %20 + %4 = OpVariable %3 Input + %19 = OpVariable %18 Output + %22 = OpFunction %20 None %21 + %23 = OpLabel + %24 = OpLoad %2 %4 + %26 = OpAccessChain %13 %19 %25 + OpStore %26 %24 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/spec_constant_array_size_dst.spvasm b/test/diff/diff_files/spec_constant_array_size_dst.spvasm new file mode 100644 index 00000000..7cedf083 --- /dev/null +++ b/test/diff/diff_files/spec_constant_array_size_dst.spvasm @@ -0,0 +1,47 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google ANGLE Shader Compiler; 0 +; Bound: 27 +; Schema: 0 +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %22 "main" %4 %19 +OpSource GLSL 450 +OpName %4 "_ua_position" +OpName %17 "gl_PerVertex" +OpMemberName %17 0 "gl_Position" +OpMemberName %17 1 "gl_PointSize" +OpMemberName %17 2 "gl_ClipDistance" +OpMemberName %17 3 "gl_CullDistance" +OpName %19 "" +OpName %22 "main" +OpDecorate %4 Location 0 +OpDecorate %15 SpecId 4 +OpMemberDecorate %17 1 RelaxedPrecision +OpMemberDecorate %17 0 BuiltIn Position +OpMemberDecorate %17 1 BuiltIn PointSize +OpMemberDecorate %17 2 BuiltIn ClipDistance +OpMemberDecorate %17 3 BuiltIn CullDistance +OpDecorate %17 Block +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeInt 32 0 +%8 = OpTypeVector %5 4 +%15 = OpSpecConstant %5 8 +%16 = OpTypeArray %1 %15 +%17 = OpTypeStruct %2 %1 %16 %16 +%20 = OpTypeVoid +%25 = OpConstant %5 0 +%3 = OpTypePointer Input %2 +%13 = OpTypePointer Output %2 +%18 = OpTypePointer Output %17 +%21 = OpTypeFunction %20 +%4 = OpVariable %3 Input +%19 = OpVariable %18 Output +%22 = OpFunction %20 None %21 +%23 = OpLabel +%24 = OpLoad %2 %4 +%26 = OpAccessChain %13 %19 %25 +OpStore %26 %24 +OpReturn +OpFunctionEnd diff --git a/test/diff/diff_files/spec_constant_array_size_src.spvasm b/test/diff/diff_files/spec_constant_array_size_src.spvasm new file mode 100644 index 00000000..e17e04ac --- /dev/null +++ b/test/diff/diff_files/spec_constant_array_size_src.spvasm @@ -0,0 +1,48 @@ +;; Tests that identical specialization constants are not matched with constants +;; when used as array size. +; SPIR-V +; Version: 1.0 +; Generator: Google ANGLE Shader Compiler; 0 +; Bound: 27 +; Schema: 0 +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %22 "main" %4 %19 +OpSource GLSL 450 +OpName %4 "_ua_position" +OpName %17 "gl_PerVertex" +OpMemberName %17 0 "gl_Position" +OpMemberName %17 1 "gl_PointSize" +OpMemberName %17 2 "gl_ClipDistance" +OpMemberName %17 3 "gl_CullDistance" +OpName %19 "" +OpName %22 "main" +OpDecorate %4 Location 0 +OpMemberDecorate %17 1 RelaxedPrecision +OpMemberDecorate %17 0 BuiltIn Position +OpMemberDecorate %17 1 BuiltIn PointSize +OpMemberDecorate %17 2 BuiltIn ClipDistance +OpMemberDecorate %17 3 BuiltIn CullDistance +OpDecorate %17 Block +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%5 = OpTypeInt 32 0 +%8 = OpTypeVector %5 4 +%15 = OpConstant %5 8 +%16 = OpTypeArray %1 %15 +%17 = OpTypeStruct %2 %1 %16 %16 +%20 = OpTypeVoid +%25 = OpConstant %5 0 +%3 = OpTypePointer Input %2 +%13 = OpTypePointer Output %2 +%18 = OpTypePointer Output %17 +%21 = OpTypeFunction %20 +%4 = OpVariable %3 Input +%19 = OpVariable %18 Output +%22 = OpFunction %20 None %21 +%23 = OpLabel +%24 = OpLoad %2 %4 +%26 = OpAccessChain %13 %19 %25 +OpStore %26 %24 +OpReturn +OpFunctionEnd diff --git a/test/diff/diff_files/spec_constant_composite_autogen.cpp b/test/diff/diff_files/spec_constant_composite_autogen.cpp new file mode 100644 index 00000000..e4b52cb6 --- /dev/null +++ b/test/diff/diff_files/spec_constant_composite_autogen.cpp @@ -0,0 +1,186 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Tests OpSpecConstantComposite matching. +constexpr char kSrc[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpDecorate %7 SpecId 3 + OpDecorate %8 SpecId 4 + OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %7 = OpSpecConstant %uint 1 + %8 = OpSpecConstant %uint 1 + %uint_1 = OpConstant %uint 1 + %v3uint = OpTypeVector %uint 3 +%gl_WorkGroupSize = OpSpecConstantComposite %v3uint %7 %8 %uint_1 + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd)"; +constexpr char kDst[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpDecorate %7 SpecId 3 + OpDecorate %8 SpecId 4 + OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %7 = OpSpecConstant %uint 2048 + %8 = OpSpecConstant %uint 1 + %uint_1 = OpConstant %uint 1 + %v3uint = OpTypeVector %uint 3 +%gl_WorkGroupSize = OpSpecConstantComposite %v3uint %7 %8 %uint_1 + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd +)"; + +TEST(DiffTest, SpecConstantComposite) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 + ; Bound: 12 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + OpExecutionMode %2 LocalSize 1 1 1 + OpSource GLSL 450 + OpName %2 "main" + OpDecorate %7 SpecId 3 + OpDecorate %8 SpecId 4 + OpDecorate %4 BuiltIn WorkgroupSize + %6 = OpTypeVoid + %3 = OpTypeFunction %6 + %9 = OpTypeInt 32 0 +-%7 = OpSpecConstant %9 1 ++%7 = OpSpecConstant %9 2048 + %8 = OpSpecConstant %9 1 + %10 = OpConstant %9 1 + %11 = OpTypeVector %9 3 + %4 = OpSpecConstantComposite %11 %7 %8 %10 + %2 = OpFunction %6 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, SpecConstantCompositeNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpDecorate %7 SpecId 3 + OpDecorate %8 SpecId 4 + OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %7 = OpSpecConstant %uint 1 + %8 = OpSpecConstant %uint 1 + %uint_1 = OpConstant %uint 1 + %v3uint = OpTypeVector %uint 3 +%gl_WorkGroupSize = OpSpecConstantComposite %v3uint %7 %8 %uint_1 + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd +)"; + constexpr char kDstNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpDecorate %7 SpecId 3 + OpDecorate %8 SpecId 4 + OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %7 = OpSpecConstant %uint 2048 + %8 = OpSpecConstant %uint 1 + %uint_1 = OpConstant %uint 1 + %v3uint = OpTypeVector %uint 3 +%gl_WorkGroupSize = OpSpecConstantComposite %v3uint %7 %8 %uint_1 + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 + ; Bound: 12 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + OpExecutionMode %2 LocalSize 1 1 1 + OpSource GLSL 450 + OpDecorate %7 SpecId 3 + OpDecorate %8 SpecId 4 + OpDecorate %4 BuiltIn WorkgroupSize + %6 = OpTypeVoid + %3 = OpTypeFunction %6 + %9 = OpTypeInt 32 0 +-%7 = OpSpecConstant %9 1 ++%7 = OpSpecConstant %9 2048 + %8 = OpSpecConstant %9 1 + %10 = OpConstant %9 1 + %11 = OpTypeVector %9 3 + %4 = OpSpecConstantComposite %11 %7 %8 %10 + %2 = OpFunction %6 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/spec_constant_composite_dst.spvasm b/test/diff/diff_files/spec_constant_composite_dst.spvasm new file mode 100644 index 00000000..3ab8d4d3 --- /dev/null +++ b/test/diff/diff_files/spec_constant_composite_dst.spvasm @@ -0,0 +1,22 @@ + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpDecorate %7 SpecId 3 + OpDecorate %8 SpecId 4 + OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %7 = OpSpecConstant %uint 2048 + %8 = OpSpecConstant %uint 1 + %uint_1 = OpConstant %uint 1 + %v3uint = OpTypeVector %uint 3 +%gl_WorkGroupSize = OpSpecConstantComposite %v3uint %7 %8 %uint_1 + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd diff --git a/test/diff/diff_files/spec_constant_composite_src.spvasm b/test/diff/diff_files/spec_constant_composite_src.spvasm new file mode 100644 index 00000000..ee48ef9d --- /dev/null +++ b/test/diff/diff_files/spec_constant_composite_src.spvasm @@ -0,0 +1,23 @@ +;; Tests OpSpecConstantComposite matching. + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpDecorate %7 SpecId 3 + OpDecorate %8 SpecId 4 + OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %7 = OpSpecConstant %uint 1 + %8 = OpSpecConstant %uint 1 + %uint_1 = OpConstant %uint 1 + %v3uint = OpTypeVector %uint 3 +%gl_WorkGroupSize = OpSpecConstantComposite %v3uint %7 %8 %uint_1 + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd diff --git a/test/diff/diff_files/unrelated_shaders_autogen.cpp b/test/diff/diff_files/unrelated_shaders_autogen.cpp new file mode 100644 index 00000000..e1a58ccc --- /dev/null +++ b/test/diff/diff_files/unrelated_shaders_autogen.cpp @@ -0,0 +1,230 @@ +// GENERATED FILE - DO NOT EDIT. +// Generated by generate_tests.py +// +// Copyright (c) 2022 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 "../diff_test_utils.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +// Tests diff of unrelated shaders (with different execution models). +constexpr char kSrc[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %4 "main" %8 %10 + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "v" + OpName %10 "a" + OpDecorate %8 Location 0 + OpDecorate %10 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Output %6 + %8 = OpVariable %7 Output + %9 = OpTypePointer Input %6 + %10 = OpVariable %9 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpLoad %6 %10 + OpStore %8 %11 + OpReturn + OpFunctionEnd +)"; +constexpr char kDst[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "color" + OpName %11 "v" + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 + %13 = OpCompositeConstruct %7 %12 %12 %12 %12 + OpStore %9 %13 + OpReturn + OpFunctionEnd + +)"; + +TEST(DiffTest, UnrelatedShaders) { + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 12 ++; Bound: 16 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 +-OpEntryPoint Vertex %4 "main" %8 %10 ++OpEntryPoint Fragment %4 "main" %14 %8 ++OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" ++OpName %14 "color" + OpName %8 "v" +-OpName %10 "a" ++OpDecorate %14 RelaxedPrecision ++OpDecorate %14 Location 0 ++OpDecorate %8 RelaxedPrecision + OpDecorate %8 Location 0 +-OpDecorate %10 Location 0 ++OpDecorate %11 RelaxedPrecision ++OpDecorate %15 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 +-%7 = OpTypePointer Output %6 ++%12 = OpTypeVector %6 4 ++%13 = OpTypePointer Output %12 ++%14 = OpVariable %13 Output +-%8 = OpVariable %7 Output ++%8 = OpVariable %9 Input + %9 = OpTypePointer Input %6 +-%10 = OpVariable %9 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel +-%11 = OpLoad %6 %10 ++%11 = OpLoad %6 %8 +-OpStore %8 %11 ++%15 = OpCompositeConstruct %12 %11 %11 %11 %11 ++OpStore %14 %15 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrc, kDst, kDiff, options); +} + +TEST(DiffTest, UnrelatedShadersNoDebug) { + constexpr char kSrcNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %4 "main" %8 %10 + OpSource ESSL 310 + OpDecorate %8 Location 0 + OpDecorate %10 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Output %6 + %8 = OpVariable %7 Output + %9 = OpTypePointer Input %6 + %10 = OpVariable %9 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpLoad %6 %10 + OpStore %8 %11 + OpReturn + OpFunctionEnd + +)"; + constexpr char kDstNoDebug[] = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 + %13 = OpCompositeConstruct %7 %12 %12 %12 %12 + OpStore %9 %13 + OpReturn + OpFunctionEnd + +)"; + constexpr char kDiff[] = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 +-; Bound: 12 ++; Bound: 15 + ; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 +-OpEntryPoint Vertex %4 "main" %8 %10 ++OpEntryPoint Fragment %4 "main" %8 %10 ++OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 ++OpDecorate %8 RelaxedPrecision + OpDecorate %8 Location 0 ++OpDecorate %10 RelaxedPrecision + OpDecorate %10 Location 0 ++OpDecorate %11 RelaxedPrecision ++OpDecorate %14 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 +-%7 = OpTypePointer Output %6 ++%12 = OpTypeVector %6 4 ++%13 = OpTypePointer Output %12 +-%8 = OpVariable %7 Output ++%8 = OpVariable %13 Output + %9 = OpTypePointer Input %6 + %10 = OpVariable %9 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpLoad %6 %10 ++%14 = OpCompositeConstruct %12 %11 %11 %11 %11 +-OpStore %8 %11 ++OpStore %8 %14 + OpReturn + OpFunctionEnd +)"; + Options options; + DoStringDiffTest(kSrcNoDebug, kDstNoDebug, kDiff, options); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_files/unrelated_shaders_dst.spvasm b/test/diff/diff_files/unrelated_shaders_dst.spvasm new file mode 100644 index 00000000..719715ba --- /dev/null +++ b/test/diff/diff_files/unrelated_shaders_dst.spvasm @@ -0,0 +1,31 @@ + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "color" + OpName %11 "v" + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %11 Location 0 + OpDecorate %12 RelaxedPrecision + OpDecorate %13 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %6 + %11 = OpVariable %10 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %6 %11 + %13 = OpCompositeConstruct %7 %12 %12 %12 %12 + OpStore %9 %13 + OpReturn + OpFunctionEnd + diff --git a/test/diff/diff_files/unrelated_shaders_src.spvasm b/test/diff/diff_files/unrelated_shaders_src.spvasm new file mode 100644 index 00000000..e77b2d20 --- /dev/null +++ b/test/diff/diff_files/unrelated_shaders_src.spvasm @@ -0,0 +1,25 @@ +;; Tests diff of unrelated shaders (with different execution models). + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %4 "main" %8 %10 + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "v" + OpName %10 "a" + OpDecorate %8 Location 0 + OpDecorate %10 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Output %6 + %8 = OpVariable %7 Output + %9 = OpTypePointer Input %6 + %10 = OpVariable %9 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpLoad %6 %10 + OpStore %8 %11 + OpReturn + OpFunctionEnd + diff --git a/test/diff/diff_test.cpp b/test/diff/diff_test.cpp new file mode 100644 index 00000000..5b11d0eb --- /dev/null +++ b/test/diff/diff_test.cpp @@ -0,0 +1,228 @@ +// Copyright (c) 2022 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/diff/diff.h" + +#include "diff_test_utils.h" + +#include "source/opt/build_module.h" +#include "source/opt/ir_context.h" +#include "source/spirv_constant.h" +#include "spirv-tools/libspirv.hpp" +#include "tools/io.h" +#include "tools/util/cli_consumer.h" + +#include <fstream> +#include <string> + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +constexpr auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6; + +std::unique_ptr<spvtools::opt::IRContext> Assemble(const std::string& spirv) { + spvtools::SpirvTools t(kDefaultEnvironment); + t.SetMessageConsumer(spvtools::utils::CLIMessageConsumer); + std::vector<uint32_t> binary; + if (!t.Assemble(spirv, &binary, + spvtools::SpirvTools::kDefaultAssembleOption | + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS)) + return nullptr; + return spvtools::BuildModule(kDefaultEnvironment, + spvtools::utils::CLIMessageConsumer, + binary.data(), binary.size()); +} + +TEST(DiffIndentTest, Diff) { + const std::string src = R"(OpCapability Shader + %ext_inst = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + %void = OpTypeVoid + %func = OpTypeFunction %void + + %main = OpFunction %void None %func + %main_entry = OpLabel + OpReturn + OpFunctionEnd;)"; + + const std::string dst = R"(OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + %void = OpTypeVoid + %func = OpTypeFunction %void + + %main = OpFunction %void None %func + %main_entry = OpLabel + OpReturn + OpFunctionEnd;)"; + + const std::string diff = R"( ; SPIR-V + ; Version: 1.6 + ; Generator: Khronos SPIR-V Tools Assembler; 0 + ; Bound: 6 + ; Schema: 0 + OpCapability Shader +- %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %2 = OpFunction %3 None %4 + %5 = OpLabel + OpReturn + OpFunctionEnd +)"; + + Options options; + options.indent = true; + DoStringDiffTest(src, dst, diff, options); +} + +TEST(DiffNoHeaderTest, Diff) { + const std::string src = R"(OpCapability Shader + %ext_inst = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + %void = OpTypeVoid + %func = OpTypeFunction %void + + %main = OpFunction %void None %func + %main_entry = OpLabel + OpReturn + OpFunctionEnd;)"; + + const std::string dst = R"(OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + %void = OpTypeVoid + %func = OpTypeFunction %void + + %main = OpFunction %void None %func + %main_entry = OpLabel + OpReturn + OpFunctionEnd;)"; + + const std::string diff = R"( OpCapability Shader +-%1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %2 = OpFunction %3 None %4 + %5 = OpLabel + OpReturn + OpFunctionEnd +)"; + + Options options; + options.no_header = true; + DoStringDiffTest(src, dst, diff, options); +} + +TEST(DiffHeaderTest, Diff) { + const std::string src_spirv = R"(OpCapability Shader + %ext_inst = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + %void = OpTypeVoid + %func = OpTypeFunction %void + + %main = OpFunction %void None %func + %main_entry = OpLabel + OpReturn + OpFunctionEnd;)"; + + const std::string dst_spirv = R"(OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + %void = OpTypeVoid + %func = OpTypeFunction %void + + %main = OpFunction %void None %func + %main_entry = OpLabel + OpReturn + OpFunctionEnd;)"; + + const std::string diff = R"( ; SPIR-V +-; Version: 1.3 ++; Version: 1.2 +-; Generator: Khronos SPIR-V Tools Assembler; 3 ++; Generator: Khronos Glslang Reference Front End; 10 + ; Bound: 6 + ; Schema: 0 + OpCapability Shader +-%1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %2 = OpFunction %3 None %4 + %5 = OpLabel + OpReturn + OpFunctionEnd +)"; + + // Load the src and dst modules + std::unique_ptr<spvtools::opt::IRContext> src = Assemble(src_spirv); + ASSERT_TRUE(src); + + std::unique_ptr<spvtools::opt::IRContext> dst = Assemble(dst_spirv); + ASSERT_TRUE(dst); + + // Differentiate them in the header. + const spvtools::opt::ModuleHeader src_header = { + SpvMagicNumber, + SPV_SPIRV_VERSION_WORD(1, 3), + SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS_ASSEMBLER, 3), + src->module()->IdBound(), + src->module()->schema(), + }; + const spvtools::opt::ModuleHeader dst_header = { + SpvMagicNumber, + SPV_SPIRV_VERSION_WORD(1, 2), + SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS_GLSLANG, 10), + dst->module()->IdBound(), + dst->module()->schema(), + }; + + src->module()->SetHeader(src_header); + dst->module()->SetHeader(dst_header); + + // Take the diff + Options options; + std::ostringstream diff_result; + spv_result_t result = + spvtools::diff::Diff(src.get(), dst.get(), diff_result, options); + ASSERT_EQ(result, SPV_SUCCESS); + + // Expect they match + EXPECT_EQ(diff_result.str(), diff); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_test_utils.cpp b/test/diff/diff_test_utils.cpp new file mode 100644 index 00000000..14bb8215 --- /dev/null +++ b/test/diff/diff_test_utils.cpp @@ -0,0 +1,58 @@ +// Copyright (c) 2022 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 "diff_test_utils.h" + +#include "source/opt/build_module.h" +#include "source/opt/ir_context.h" + +#include "spirv-tools/libspirv.hpp" +#include "tools/io.h" +#include "tools/util/cli_consumer.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { + +static constexpr auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6; + +void DoStringDiffTest(const std::string& src_spirv, + const std::string& dst_spirv, + const std::string& expected_diff, Options options) { + // Load the src and dst modules + std::unique_ptr<spvtools::opt::IRContext> src = spvtools::BuildModule( + kDefaultEnvironment, spvtools::utils::CLIMessageConsumer, src_spirv, + spvtools::SpirvTools::kDefaultAssembleOption | + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_TRUE(src); + + std::unique_ptr<spvtools::opt::IRContext> dst = spvtools::BuildModule( + kDefaultEnvironment, spvtools::utils::CLIMessageConsumer, dst_spirv, + spvtools::SpirvTools::kDefaultAssembleOption | + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_TRUE(dst); + + // Take the diff + std::ostringstream diff_result; + spv_result_t result = + spvtools::diff::Diff(src.get(), dst.get(), diff_result, options); + ASSERT_EQ(result, SPV_SUCCESS); + + // Expect they match + EXPECT_EQ(diff_result.str(), expected_diff); +} + +} // namespace diff +} // namespace spvtools diff --git a/test/diff/diff_test_utils.h b/test/diff/diff_test_utils.h new file mode 100644 index 00000000..938236e6 --- /dev/null +++ b/test/diff/diff_test_utils.h @@ -0,0 +1,30 @@ +// Copyright (c) 2022 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef TEST_DIFF_DIFF_TEST_UTILS_H_ +#define TEST_DIFF_DIFF_TEST_UTILS_H_ + +#include "source/diff/diff.h" + +namespace spvtools { +namespace diff { + +void DoStringDiffTest(const std::string& src_spirv, + const std::string& dst_spirv, + const std::string& expected_diff, Options options); + +} // namespace diff +} // namespace spvtools + +#endif // TEST_DIFF_DIFF_TEST_UTILS_H_ diff --git a/test/diff/lcs_test.cpp b/test/diff/lcs_test.cpp new file mode 100644 index 00000000..3e097b3e --- /dev/null +++ b/test/diff/lcs_test.cpp @@ -0,0 +1,329 @@ +// Copyright (c) 2022 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/diff/lcs.h" + +#include <string> + +#include "gtest/gtest.h" + +namespace spvtools { +namespace diff { +namespace { + +using Sequence = std::vector<int>; +using LCS = LongestCommonSubsequence<Sequence>; + +void VerifyMatch(const Sequence& src, const Sequence& dst, + size_t expected_match_count) { + DiffMatch src_match, dst_match; + + LCS lcs(src, dst); + size_t match_count = + lcs.Get<int>([](int s, int d) { return s == d; }, &src_match, &dst_match); + + EXPECT_EQ(match_count, expected_match_count); + + size_t src_cur = 0; + size_t dst_cur = 0; + size_t matches_seen = 0; + + while (src_cur < src.size() && dst_cur < dst.size()) { + if (src_match[src_cur] && dst_match[dst_cur]) { + EXPECT_EQ(src[src_cur], dst[dst_cur]) + << "Src: " << src_cur << " Dst: " << dst_cur; + ++src_cur; + ++dst_cur; + ++matches_seen; + continue; + } + if (!src_match[src_cur]) { + ++src_cur; + } + if (!dst_match[dst_cur]) { + ++dst_cur; + } + } + + EXPECT_EQ(matches_seen, expected_match_count); +} + +TEST(LCSTest, EmptySequences) { + Sequence src, dst; + + DiffMatch src_match, dst_match; + + LCS lcs(src, dst); + size_t match_count = + lcs.Get<int>([](int s, int d) { return s == d; }, &src_match, &dst_match); + + EXPECT_EQ(match_count, 0u); + EXPECT_TRUE(src_match.empty()); + EXPECT_TRUE(dst_match.empty()); +} + +TEST(LCSTest, EmptySrc) { + Sequence src, dst = {1, 2, 3}; + + DiffMatch src_match, dst_match; + + LCS lcs(src, dst); + size_t match_count = + lcs.Get<int>([](int s, int d) { return s == d; }, &src_match, &dst_match); + + EXPECT_EQ(match_count, 0u); + EXPECT_TRUE(src_match.empty()); + EXPECT_EQ(dst_match, DiffMatch(3, false)); +} + +TEST(LCSTest, EmptyDst) { + Sequence src = {1, 2, 3}, dst; + + DiffMatch src_match, dst_match; + + LCS lcs(src, dst); + size_t match_count = + lcs.Get<int>([](int s, int d) { return s == d; }, &src_match, &dst_match); + + EXPECT_EQ(match_count, 0u); + EXPECT_EQ(src_match, DiffMatch(3, false)); + EXPECT_TRUE(dst_match.empty()); +} + +TEST(LCSTest, Identical) { + Sequence src = {1, 2, 3, 4, 5, 6}, dst = {1, 2, 3, 4, 5, 6}; + + DiffMatch src_match, dst_match; + + LCS lcs(src, dst); + size_t match_count = + lcs.Get<int>([](int s, int d) { return s == d; }, &src_match, &dst_match); + + EXPECT_EQ(match_count, 6u); + EXPECT_EQ(src_match, DiffMatch(6, true)); + EXPECT_EQ(dst_match, DiffMatch(6, true)); +} + +TEST(LCSTest, SrcPrefix) { + Sequence src = {1, 2, 3, 4}, dst = {1, 2, 3, 4, 5, 6}; + + DiffMatch src_match, dst_match; + + LCS lcs(src, dst); + size_t match_count = + lcs.Get<int>([](int s, int d) { return s == d; }, &src_match, &dst_match); + + const DiffMatch src_expect = {true, true, true, true}; + const DiffMatch dst_expect = {true, true, true, true, false, false}; + + EXPECT_EQ(match_count, 4u); + EXPECT_EQ(src_match, src_expect); + EXPECT_EQ(dst_match, dst_expect); +} + +TEST(LCSTest, DstPrefix) { + Sequence src = {1, 2, 3, 4, 5, 6}, dst = {1, 2, 3, 4, 5}; + + DiffMatch src_match, dst_match; + + LCS lcs(src, dst); + size_t match_count = + lcs.Get<int>([](int s, int d) { return s == d; }, &src_match, &dst_match); + + const DiffMatch src_expect = {true, true, true, true, true, false}; + const DiffMatch dst_expect = {true, true, true, true, true}; + + EXPECT_EQ(match_count, 5u); + EXPECT_EQ(src_match, src_expect); + EXPECT_EQ(dst_match, dst_expect); +} + +TEST(LCSTest, SrcSuffix) { + Sequence src = {3, 4, 5, 6}, dst = {1, 2, 3, 4, 5, 6}; + + DiffMatch src_match, dst_match; + + LCS lcs(src, dst); + size_t match_count = + lcs.Get<int>([](int s, int d) { return s == d; }, &src_match, &dst_match); + + const DiffMatch src_expect = {true, true, true, true}; + const DiffMatch dst_expect = {false, false, true, true, true, true}; + + EXPECT_EQ(match_count, 4u); + EXPECT_EQ(src_match, src_expect); + EXPECT_EQ(dst_match, dst_expect); +} + +TEST(LCSTest, DstSuffix) { + Sequence src = {1, 2, 3, 4, 5, 6}, dst = {5, 6}; + + DiffMatch src_match, dst_match; + + LCS lcs(src, dst); + size_t match_count = + lcs.Get<int>([](int s, int d) { return s == d; }, &src_match, &dst_match); + + const DiffMatch src_expect = {false, false, false, false, true, true}; + const DiffMatch dst_expect = {true, true}; + + EXPECT_EQ(match_count, 2u); + EXPECT_EQ(src_match, src_expect); + EXPECT_EQ(dst_match, dst_expect); +} + +TEST(LCSTest, None) { + Sequence src = {1, 3, 5, 7, 9}, dst = {2, 4, 6, 8, 10, 12}; + + DiffMatch src_match, dst_match; + + LCS lcs(src, dst); + size_t match_count = + lcs.Get<int>([](int s, int d) { return s == d; }, &src_match, &dst_match); + + EXPECT_EQ(match_count, 0u); + EXPECT_EQ(src_match, DiffMatch(5, false)); + EXPECT_EQ(dst_match, DiffMatch(6, false)); +} + +TEST(LCSTest, NonContiguous) { + Sequence src = {1, 2, 3, 4, 5, 6, 10}, dst = {2, 4, 5, 8, 9, 10, 12}; + + DiffMatch src_match, dst_match; + + LCS lcs(src, dst); + size_t match_count = + lcs.Get<int>([](int s, int d) { return s == d; }, &src_match, &dst_match); + + const DiffMatch src_expect = {false, true, false, true, true, false, true}; + const DiffMatch dst_expect = {true, true, true, false, false, true, false}; + + EXPECT_EQ(match_count, 4u); + EXPECT_EQ(src_match, src_expect); + EXPECT_EQ(dst_match, dst_expect); +} + +TEST(LCSTest, WithDuplicates) { + Sequence src = {1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4}, + dst = {1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4}; + VerifyMatch(src, dst, 6); +} + +TEST(LCSTest, Large) { + const std::string src_str = + "GUJwrJSlkKJXxCVIAxlVgnUyOrdyRyFtlZwWMmFhYGfkFTNnhiBmClgHyrcXMVwfrRxNUfQk" + "qaoGvCbPZHAzXsaZpXHPfJxOMCUtRDmIQpfiXKbHQbhTfPqhxBDWvmTQAqwsWTLajZYtMUnf" + "hNNCfkuAXkZsaebwEbIZOxTDZsqSMUfCMoGeKJGVSNFgLTiBMbdvchHGfFRkHKcYCDjBfIcj" + "todPnvDjzQYWBcvIfVvyBzHikrwpDORaGEZLhmyztIFCLJOqeLhOzERYmVqzlsoUzruTXTXq" + "DLTxQRakOCMRrgRzCDTXfwwfDcKMBVnxRZemjcwcEsOVxwtwdBCWJycsDcZKlvrCvZaenKlv" + "vyByDQeLdxAyBnPkIMQlMQwqjUfRLybeoaOanlbFkpTPPZdHelQrIvucTHzMpWWQTbuANwvN" + "OVhCGGoIcGNDpfIsaBexlMMdHsxMGerTngmjpdPeQQJHfvKZkdYqAzrtDohqtDsaFMxQViVQ" + "YszDVgyoSHZdOXAvXkJidojLvGZOzhRajVPhWDwKuGqdaympELxHsrXAJYufdCPwJdGJfWqq" + "yvTWpcrFHOIuCEmNLnSCDsxQGRVDwyCykBJazhApfCnrOadnafvqfVuFqEXMSrYbHTfTnzbz" + "MhISyOtMUITaurCXvanCbuOXBhHyCjOhVbxnMvhlPmZBMQgEHCghtAJVMXGPNRtszVZlPxVl" + "QIPTBnPUPejlyZGPqeICyNngdQGkvKbIoWlTLBtVhMdBeUMozNlKQTIPYBeImVcMdLafuxUf" + "TIXysmcTrUTcspOSKBxhdhLwiRnREGFWJTfUKsgGOAQeYojXdrqsGjMJfiKalyoiqrgLnlij" + "CtOapoxDGVOOBalNYGzCtBlxbvaAzxipGnJpOEbmXcpeoIsAdxspKBzBDgoPVxnuRBUwmTSr" + "CRpWhxikgUYQVCwalLUIeBPRyhhsECGCXJmGDZSCIUaBwROkigzdeVPOXhgCGBEprWtNdYfL" + "tOUYJHQXxiIJgSGmWntezJFpNQoTPbRRYAGhtYvAechvBcYWocLkYFxsDAuszvQNLXdhmAHw" + "DErcjbtCdQllnKcDADVNWVezljjLrAuyGHetINMgAvJZwOEYakihYVUbZGCsHEufluLNyNHy" + "gqtSTSFFjBHiIqQejTPWybLdpWNwZrWvIWnlzUcGNQPEYHVPCbteWknjAnWrdTBeCbHUDBoK" + "aHvDStmpNRGIjvlumiZTbdZNAzUeSFnFChCsSExwXeEfDJfjyOoSBofHzJqJErvHLNyUJTjX" + "qmtgKPpMKohUPBMhtCteQFcNEpWrUVGbibMOpvBwdiWYXNissArpSasVJFgDzrqTyGkerTMX" + "gcrzFUGFZRhNdekaJeKYPogsofJaRsUQmIRyYdkrxKeMgLPpwOfSKJOqzXDoeHljTzhOwEVy" + "krOEnACFrWhufajsMitjOWdLOHHchQDddGPzxknEgdwmZepKDvRZGCuPqzeQkjOPqUBKpKLJ" + "eKieSsRXkaqxSPGajfvPKmwFWdLByEcLgvrmteazgFjmMGrLYqRRxzUOfOCokenqHVYstBHf" + "AwsWsqPTvqsRJUfGGTaYiylZMGbQqTzINhFHvdlRQvvYKBcuAHdBeKlHSxVrSsEKbcAvnIcf" + "xzdVDdwQPHMCHeZZRpGHWvKzgTGzSTbYTeOPyKvvYWmQToTpsjAtKUJUjcEHWhmdBLDTBMHJ" + "ivBXcLGtCsumNNVFyGbVviGmqHTdyBlkneibXBesKJGOUzOtIwXCPJggqBekSzNQYkALlItk" + "cbEhbdXAIKVHYpInLwxXalKZrkrpxtfuagqMGmRJnJbFQaEoYMoqPsxZpocddPXXPyvxVkaF" + "qdKISejWDhBImnEEOPDcyWTubbfVfwUztciaFJcsPLhgYVfhqlOfoNjKbmTFptFttYuyBrUI" + "zzmZypOqrjQHTGFwlHStpIwxPtMvtsEDpsmWIgwzYgwmdpbMOnfElZMYpVIcvzSWejeJcdUB" + "QUoBRUmGQVVWvEDseuozrDjgdXFScPwwsgaUPwSzScfBNrkpmEFDSZLKfNjMqvOmUtocUkbo" + "VGFEKgGLbNruwLgXHTloWDrnqymPVAtzjWPutonIsMDPeeCmTjYWAFXcyTAlBeiJTIRkZxiM" + "kLjMnAflSNJzmZkatXkYiPEMYSmzHbLKEizHbEjQOxBDzpRHiFjhedqiyMiUMvThjaRFmwll" + "aMGgwKBIKepwyoEdnuhtzJzboiNEAFKiqiWxxmkRFRoTiFWXLPAWLuzSCrajgkQhDxAQDqyM" + "VwZlhZicQLEDYYisEalesDWZAYzcvENuHUwRutIsGgsdoYwOZiURhcgdbTGWBNqhrFjvTQCj" + "VlTPNlRdRLaaqzUBBwbdtyXFkCBUYYMbmRrkFxfxbCqkgZNGyHPKLkOPnezfVTRmRQgCgHbx" + "wcZlInVOwmFePnSIbThMJosimzkhfuiqYEpwHQiemqsSDNNdbNhBLzbsPZBJZujSHJGtYKGb" + "HaAYGJZxBumsKUrATwPuqXFLfwNyImLQbchBKiJAYRZhkcrKCHXBEGYyBhBGvSqvabcRUrfq" + "AbPiMzjHAehGYjDEmxAnYLyoSFdeWVrfJUCuYZPluhXEBuyUpKaRXDKXeiCvGidpvATwMbcz" + "DZpzxrhTZYyrFORFQWTbPLCBjMKMhlRMFEiarDgGPttjmkrQVlujztMSkxXffXFNqLWOLThI" + "KBoyMHoFTEPCdUAZjLTifAdjjUehyDLEGKlRTFoLpjalziRSUjZfRYbNzhiHgTHowMMkKTwE" + "ZgnqiirMtnNpaBJqhcIVrWXPpcPWZfRpsPstHleFJDZYAsxYhOREVbFtebXTZRAIjGgWeoiN" + "qPLCCAVadqmUrjOcqIbdCTpcDRWuDVbHrZOQRPhqbyvOWwxAWJphjLiDgoAybcjzgfVktPlj" + "kNBCjelpuQfnYsiTgPpCNKYtOrxGaLEEtAuLdGdDsONHNhSn"; + const std::string dst_str = + "KzitfifORCbGhfNEbnbObUdFLLaAsLOpMkOeKupjCoatzqfHBkNJfSgqSMYouswfNMnoQngK" + "jWwyPKmEnoZWyPBUdQRmKUNudUclueKXKQefUdXWUyyqtumzsFKznrLVLwfvPZpLChNYrrHK" + "AtpfOuVHiUKyeRCrktJAhkyFKmPWrASEMvBLNOzuGlvinZjvZUUXazNEkyMPiOLdqXvCIroC" + "MeWsvjHShlLhDwLZrVlpYBnDJmILcsNFDSoaLWOKNNkNGBgNBvVjPCJXAuKfsrKZhYcdEpxK" + "UihiRkYvMiLyOUvaqBMklLDwEhvQBfCXHSRoqsLsSCzLZQhIYMhBapvHaPbDoRrHoJXZsNXc" + "rxZYCrOMIzYcVPwDCFiHBFnPNTTeAeKEMGeVUeCaAeuWZmngyPWlQBcgWumSUIfbhjVYdnpV" + "hRSJXrIoFZubBXfNOMhilAkVPixrhILZKgDoFTvytPFPfBLMnbhSOBmLWCbJsLQxrCrMAlOw" + "RmfSQyGhrjhzYVqFSBHeoQBagFwyxIjcHFZngntpVHbSwqhwHeMnWSsISPljTxSNXfCxLebW" + "GhMdlphtJbdvhEcjNpwPCFqhdquxCyOxkjsDUPNgjpDcpIMhMwMclNhfESTrroJaoyeGQclV" + "gonnhuQRmXcBwcsWeLqjNngZOlyMyfeQBwnwMVJEvGqknDyzSApniRTPgJpFoDkJJhXQFuFB" + "VqhuEPMRGCeTDOSEFmXeIHOnDxaJacvnmORwVpmrRhGjDpUCkuODNPdZMdupYExDEDnDLdNF" + "iObKBaVWpGVMKdgNLgsNxcpypBPPKKoaajeSGPZQJWSOKrkLjiFexYVmUGxJnbTNsCXXLfZp" + "jfxQAEVYvqKehBzMsVHVGWmTshWFAoCNDkNppzzjHBZWckrzSTANICioCJSpLwPwQvtXVxst" + "nTRBAboPFREEUFazibpFesCsjzUOnECwoPCOFiwGORlIZVLpUkJyhYXCENmzTBLVigOFuCWO" + "IiXBYmiMtsxnUdoqSTTGyEFFrQsNAjcDdOKDtHwlANWoUVwiJCMCQFILdGqzEePuSXFbOEOz" + "dLlEnTJbKRSTfAFToOZNtDXTfFgvQiefAKbSUWUXFcpCjRYCBNXCCcLMjjuUDXErpiNsRuIx" + "mgHsrObTEXcnmjdqxTGhTjTeYizNnkrJRhNQIqDXmZMwArBccnixpcuiGOOexjgkpcEyGAnz" + "UbgiBfflTUyJfZeFFLrZVueFkSRosebnnwAnakIrywTGByhQKWvmNQJsWQezqLhHQzXnEpeD" + "rFRTSQSpVxPzSeEzfWYzfpcenxsUyzOMLxhNEhfcuprDtqubsXehuqKqZlLQeSclvoGjuKJK" + "XoWrazsgjXXnkWHdqFESZdMGDYldyYdbpSZcgBPgEKLWZHfBirNPLUadmajYkiEzmGuWGELB" + "WLiSrMdaGSbptKmgYVqMGcQaaATStiZYteGAPxSEBHuAzzjlRHYsrdDkaGNXmzRGoalJMiCC" + "GMtWSDMhgvRSEgKnywbRgnqWXFlwrhXbbvcgLGtWSuKQBiqIlWkfPMozOTWgVoLHavDJGRYI" + "YerrmZnTMtuuxmZALWakfzUbksTwoetqkOiRPGqGZepcVXHoZyOaaaijjZWQLlIhYwiQNbfc" + "KCwhhFaMQBoaCnOecJEdKzdsMPFEYQuJNPYiiNtsYxaWBRuWjlLqGokHMNtyTQfSJKbgGdol" + "fWlOZdupouQMfUWXIYHzyJHefMDnqxxasDxtgArvDqtwjDBaVEMACPkLFpiDOoKCHqkWVizh" + "lKqbOHpsPKkhjRQRNGYRYEfxtBjYvlCvHBNUwVuIwDJYMqHxEFtwdLqYWvjdOfQmNiviDfUq" + "pbucbNwjNQfMYgwUuPnQWIPOlqHcbjtuDXvTzLtkdBQanJbrmLSyFqSapZCSPMDOrxWVYzyO" + "lwDTTJFmKxoyfPunadkHcrcSQaQsAbrQtbhqwSTXGTPURYTCbNozjAVwbmcyVxIbZudBZWYm" + "rnSDyelGCRRWYtrUxvOVWlTLHHdYuAmVMGnGbHscbjmjmAzmYLaCxNNwhmMYdExKvySxuYpE" + "rVGwfqMngBCHnZodotNaNJZiNRFWubuPDfiywXPiyVWoQMeOlSuWmpilLTIFOvfpjmJTgrWa" + "dgoxYeyPyOaglOvZVGdFOBSeqEcGXBwjoeUAXqkpvOxEpSXhmklKZydTvRVYVvfQdRNNDkCT" + "dLNfcZCFQbZORdcDOhwotoyccrSbWvlqYMoiAYeEpDzZTvkamapzZMmCpEutZFCcHBWGIIkr" + "urwDNHrobaErPpclyEegLJDtkfUWSNWZosWSbBGAHIvJsFNUlJXbnkSVycLkOVQVcNcUtiBy" + "djLDIFsycbPBEWaMvCbntNtJlOeCttvXypGnHAQFnFSiXFWWqonWuVIKmVPpKXuJtFguXCWC" + "rNExYYvxLGEmuZJLJDjHgjlQyOzeieCpizJxkrdqKCgomyEkvsyVYSsLeyLvOZQrrgEJgRFK" + "CjYtoOfluNrLdRMTRkQXmAiMRFwloYECpXCReAMxOkNiwCtutsrqWoMHsrogRqPoUCueonvW" + "MTwmkAkajfGJkhnQidwpwIMEttQkzIMOPvvyWZHpqkMHWlNTeSKibfRfwDyxveKENZhtlPwP" + "dfAjwegjRcavtFnkkTNVYdCdCrgdUvzsIcqmUjwGmVvuuQvjVrWWIDBmAzQtiZPYvCOEWjce" + "rWzeqVKeiYTJBOedmQCVidOgUIEjfRnbGvUbctYxfRybJkdmeAkLZQMRMGPOnsPbFswXAoCK" + "IxWGwohoPpEJxslbqHFKSwknxTmrDCITRZWEDkGQeucPxHBdYkduwbYhKnoxCKhgjBFiFawC" + "QtgTDldTQmlOsBiGLquMjuecAbrUJJvNtXbFNGjWxaZPimSRXUJWgRbydpsczOqSFIeEtuKA" + "ZpRhmLtPdVNKdSDQZeeImUFmUwXApRTUNHItyvFyJtNtn"; + + Sequence src; + Sequence dst; + + src.reserve(src_str.length()); + dst.reserve(dst_str.length()); + + for (char c : src_str) { + src.push_back(c); + } + for (char c : dst_str) { + dst.push_back(c); + } + + VerifyMatch(src, dst, 723); +} + +} // namespace +} // namespace diff +} // namespace spvtools diff --git a/test/ext_inst.cldebug100_test.cpp b/test/ext_inst.cldebug100_test.cpp index 4f1e1067..0bbdd3a9 100644 --- a/test/ext_inst.cldebug100_test.cpp +++ b/test/ext_inst.cldebug100_test.cpp @@ -581,7 +581,7 @@ INSTANTIATE_TEST_SUITE_P( OpenCLDebugInfo100DebugSource, ExtInstCLDebugInfo100RoundTripTest, ::testing::ValuesIn(std::vector<InstructionCase>({ // TODO(dneto): Should this be a list of sourc texts, - // to accomodate length limits? + // to accommodate length limits? CASE_I(Source), CASE_II(Source), }))); diff --git a/test/ext_inst.opencl_test.cpp b/test/ext_inst.opencl_test.cpp index 7547d922..d80a9bd7 100644 --- a/test/ext_inst.opencl_test.cpp +++ b/test/ext_inst.opencl_test.cpp @@ -233,7 +233,7 @@ INSTANTIATE_TEST_SUITE_P( CASE3(UMad_hi, u_mad_hi), // enum value 204 }))); -// OpenCL.std: 2.3 Common instrucitons +// OpenCL.std: 2.3 Common instructions INSTANTIATE_TEST_SUITE_P( OpenCLCommon, ExtInstOpenCLStdRoundTripTest, ::testing::ValuesIn(std::vector<InstructionCase>({ diff --git a/test/fuzz/fuzz_test_util.cpp b/test/fuzz/fuzz_test_util.cpp index bf0a4ff5..93c9c584 100644 --- a/test/fuzz/fuzz_test_util.cpp +++ b/test/fuzz/fuzz_test_util.cpp @@ -149,7 +149,7 @@ void DumpTransformationsJson( json_options.add_whitespace = true; auto json_generation_status = google::protobuf::util::MessageToJsonString( transformations, &json_string, json_options); - if (json_generation_status == google::protobuf::util::Status::OK) { + if (json_generation_status.ok()) { std::ofstream transformations_json_file(filename); transformations_json_file << json_string; transformations_json_file.close(); diff --git a/test/fuzz/fuzzerutil_test.cpp b/test/fuzz/fuzzerutil_test.cpp index 0ad3e742..1286d38d 100644 --- a/test/fuzz/fuzzerutil_test.cpp +++ b/test/fuzz/fuzzerutil_test.cpp @@ -70,7 +70,7 @@ TEST(FuzzerUtilMaybeFindBlockTest, BasicTest) { ASSERT_TRUE(fuzzerutil::MaybeFindBlock(ir_context, block_id2) != nullptr); // Block with id 13 cannot be found. ASSERT_FALSE(fuzzerutil::MaybeFindBlock(ir_context, block_id3) != nullptr); - // Block with id 8 exisits but don't not of type OpLabel. + // Block with id 8 exists but don't not of type OpLabel. ASSERT_FALSE(fuzzerutil::MaybeFindBlock(ir_context, block_id4) != nullptr); } @@ -965,7 +965,7 @@ TEST(FuzzerutilTest, FuzzerUtilMaybeGetPointerTypeTest) { ASSERT_EQ( 91, fuzzerutil::MaybeGetPointerType(ir_context, 90, input_storage_class)); - // A pointer with id=91 and pointee type 90 exisits, but the type should be + // A pointer with id=91 and pointee type 90 exists, but the type should be // input. ASSERT_EQ(0, fuzzerutil::MaybeGetPointerType(ir_context, 90, function_storage_class)); diff --git a/test/fuzz/transformation_add_parameter_test.cpp b/test/fuzz/transformation_add_parameter_test.cpp index 2ae60c9d..7f069b55 100644 --- a/test/fuzz/transformation_add_parameter_test.cpp +++ b/test/fuzz/transformation_add_parameter_test.cpp @@ -367,7 +367,7 @@ TEST(TransformationAddParameterTest, NonPointerNotApplicableTest) { transformation_bad_3.IsApplicable(context.get(), transformation_context)); // Function with id 14 does not have any callers. - // Bad: Id 18 is not a vaild type. + // Bad: Id 18 is not a valid type. TransformationAddParameter transformation_bad_4(14, 50, 18, {{}}, 51); ASSERT_FALSE( transformation_bad_4.IsApplicable(context.get(), transformation_context)); diff --git a/test/fuzz/transformation_add_type_int_test.cpp b/test/fuzz/transformation_add_type_int_test.cpp index ed8e00a6..4cbfed0f 100644 --- a/test/fuzz/transformation_add_type_int_test.cpp +++ b/test/fuzz/transformation_add_type_int_test.cpp @@ -87,7 +87,7 @@ TEST(TransformationAddTypeIntTest, IsApplicable) { transformation.IsApplicable(context.get(), transformation_context)); // By default SPIR-V does not support 16-bit integers. - // Below we add such capability, so the test should now be succesful. + // Below we add such capability, so the test should now be successful. context.get()->get_feature_mgr()->AddCapability(SpvCapabilityInt16); ASSERT_TRUE(TransformationAddTypeInt(7, 16, true) .IsApplicable(context.get(), transformation_context)); diff --git a/test/fuzz/transformation_adjust_branch_weights_test.cpp b/test/fuzz/transformation_adjust_branch_weights_test.cpp index 1bf2c593..5984a3e9 100644 --- a/test/fuzz/transformation_adjust_branch_weights_test.cpp +++ b/test/fuzz/transformation_adjust_branch_weights_test.cpp @@ -106,7 +106,7 @@ TEST(TransformationAdjustBranchWeightsTest, IsApplicableTest) { kConsoleMessageConsumer)); TransformationContext transformation_context( MakeUnique<FactManager>(context.get()), validator_options); - // Tests OpBranchConditional instruction with weigths. + // Tests OpBranchConditional instruction with weights. auto instruction_descriptor = MakeInstructionDescriptor(33, SpvOpBranchConditional, 0); auto transformation = diff --git a/test/fuzzers/BUILD.gn b/test/fuzzers/BUILD.gn index 2aef4e8f..fc458a4f 100644 --- a/test/fuzzers/BUILD.gn +++ b/test/fuzzers/BUILD.gn @@ -87,18 +87,21 @@ spvtools_fuzzer("spvtools_dis_fuzzer_src") { spvtools_fuzzer("spvtools_opt_performance_fuzzer_src") { sources = [ "spvtools_opt_performance_fuzzer.cpp", + "spvtools_opt_fuzzer_common.cpp", ] } spvtools_fuzzer("spvtools_opt_legalization_fuzzer_src") { sources = [ "spvtools_opt_legalization_fuzzer.cpp", + "spvtools_opt_fuzzer_common.cpp", ] } spvtools_fuzzer("spvtools_opt_size_fuzzer_src") { sources = [ "spvtools_opt_size_fuzzer.cpp", + "spvtools_opt_fuzzer_common.cpp", ] } diff --git a/test/fuzzers/CMakeLists.txt b/test/fuzzers/CMakeLists.txt index 50c45895..e1fe516a 100644 --- a/test/fuzzers/CMakeLists.txt +++ b/test/fuzzers/CMakeLists.txt @@ -46,9 +46,9 @@ if (${SPIRV_BUILD_LIBFUZZER_TARGETS}) add_spvtools_libfuzzer_target(TARGET spvtools_as_fuzzer SRCS spvtools_as_fuzzer.cpp random_generator.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) add_spvtools_libfuzzer_target(TARGET spvtools_binary_parser_fuzzer SRCS spvtools_binary_parser_fuzzer.cpp random_generator.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) add_spvtools_libfuzzer_target(TARGET spvtools_dis_fuzzer SRCS spvtools_dis_fuzzer.cpp random_generator.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) - add_spvtools_libfuzzer_target(TARGET spvtools_opt_legalization_fuzzer SRCS spvtools_opt_legalization_fuzzer.cpp random_generator.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY}) - add_spvtools_libfuzzer_target(TARGET spvtools_opt_performance_fuzzer SRCS spvtools_opt_performance_fuzzer.cpp random_generator.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY}) - add_spvtools_libfuzzer_target(TARGET spvtools_opt_size_fuzzer SRCS spvtools_opt_size_fuzzer.cpp random_generator.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY}) + add_spvtools_libfuzzer_target(TARGET spvtools_opt_legalization_fuzzer SRCS spvtools_opt_legalization_fuzzer.cpp spvtools_opt_fuzzer_common.cpp random_generator.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY}) + add_spvtools_libfuzzer_target(TARGET spvtools_opt_performance_fuzzer SRCS spvtools_opt_performance_fuzzer.cpp spvtools_opt_fuzzer_common.cpp random_generator.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY}) + add_spvtools_libfuzzer_target(TARGET spvtools_opt_size_fuzzer SRCS spvtools_opt_size_fuzzer.cpp spvtools_opt_fuzzer_common.cpp random_generator.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY}) add_spvtools_libfuzzer_target(TARGET spvtools_val_fuzzer SRCS spvtools_val_fuzzer.cpp random_generator.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) if (${SPIRV_BUILD_FUZZER}) add_spvtools_libfuzzer_target(TARGET spvtools_fuzz_fuzzer SRCS spvtools_fuzz_fuzzer.cpp random_generator.cpp LIBS SPIRV-Tools-fuzz ${SPIRV_TOOLS_FULL_VISIBILITY}) diff --git a/test/fuzzers/spvtools_as_fuzzer.cpp b/test/fuzzers/spvtools_as_fuzzer.cpp index ba3f5b97..8ead1cff 100644 --- a/test/fuzzers/spvtools_as_fuzzer.cpp +++ b/test/fuzzers/spvtools_as_fuzzer.cpp @@ -32,22 +32,13 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { return 0; } - std::vector<uint32_t> input; - input.resize(size >> 2); - size_t count = 0; - for (size_t i = 0; (i + 3) < size; i += 4) { - input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) | - (data[i + 3]) << 24; - } - - std::vector<char> input_str; - size_t char_count = input.size() * sizeof(uint32_t) / sizeof(char); - input_str.resize(char_count); - memcpy(input_str.data(), input.data(), input.size() * sizeof(uint32_t)); + std::vector<char> contents; + contents.resize(size); + memcpy(contents.data(), data, size); spv_binary binary = nullptr; spv_diagnostic diagnostic = nullptr; - spvTextToBinaryWithOptions(context, input_str.data(), input_str.size(), + spvTextToBinaryWithOptions(context, contents.data(), contents.size(), SPV_TEXT_TO_BINARY_OPTION_NONE, &binary, &diagnostic); if (diagnostic) { @@ -61,7 +52,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { binary = nullptr; } - spvTextToBinaryWithOptions(context, input_str.data(), input_str.size(), + spvTextToBinaryWithOptions(context, contents.data(), contents.size(), SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS, &binary, &diagnostic); if (diagnostic) { diff --git a/test/fuzzers/spvtools_opt_fuzzer_common.cpp b/test/fuzzers/spvtools_opt_fuzzer_common.cpp new file mode 100644 index 00000000..49785090 --- /dev/null +++ b/test/fuzzers/spvtools_opt_fuzzer_common.cpp @@ -0,0 +1,84 @@ +// Copyright (c) 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "test/fuzzers/spvtools_opt_fuzzer_common.h" + +#include "source/opt/build_module.h" +#include "test/fuzzers/random_generator.h" + +namespace spvtools { +namespace fuzzers { + +int OptFuzzerTestOneInput( + const uint8_t* data, size_t size, + const std::function<void(spvtools::Optimizer&)>& register_passes) { + if (size < 1) { + return 0; + } + + spvtools::fuzzers::RandomGenerator random_gen(data, size); + auto target_env = random_gen.GetTargetEnv(); + spvtools::Optimizer optimizer(target_env); + optimizer.SetMessageConsumer([](spv_message_level_t, const char*, + const spv_position_t&, const char*) {}); + + std::vector<uint32_t> input; + input.resize(size >> 2); + + size_t count = 0; + for (size_t i = 0; (i + 3) < size; i += 4) { + input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) | + (data[i + 3]) << 24; + } + + // The largest possible id bound is used when running the optimizer, to avoid + // the problem of id overflows. + const size_t kFinalIdLimit = UINT32_MAX; + + // The input is scanned to check that it does not already use an id too close + // to this limit. This still gives the optimizer a large set of ids to + // consume. It is thus very unlikely that id overflow will occur during + // fuzzing. If it does, then the initial id limit should be decreased. + const size_t kInitialIdLimit = kFinalIdLimit - 1000000U; + + // Build the module and scan it to check that all used ids are below the + // initial limit. + auto ir_context = + spvtools::BuildModule(target_env, nullptr, input.data(), input.size()); + if (ir_context == nullptr) { + // It was not possible to build a valid module; that's OK - skip this input. + return 0; + } + if (ir_context->module()->id_bound() >= kInitialIdLimit) { + // The input already has a very large id bound. The input is thus abandoned, + // to avoid the possibility of ending up hitting the id bound limit. + return 0; + } + + // Set the optimizer and its validator up with the largest possible id bound + // limit. + spvtools::ValidatorOptions validator_options; + spvtools::OptimizerOptions optimizer_options; + optimizer_options.set_max_id_bound(kFinalIdLimit); + validator_options.SetUniversalLimit(spv_validator_limit_max_id_bound, + kFinalIdLimit); + optimizer_options.set_validator_options(validator_options); + register_passes(optimizer); + optimizer.Run(input.data(), input.size(), &input, optimizer_options); + + return 0; +} + +} // namespace fuzzers +} // namespace spvtools diff --git a/test/fuzzers/spvtools_opt_fuzzer_common.h b/test/fuzzers/spvtools_opt_fuzzer_common.h new file mode 100644 index 00000000..b8d4281c --- /dev/null +++ b/test/fuzzers/spvtools_opt_fuzzer_common.h @@ -0,0 +1,35 @@ +// Copyright (c) 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef TEST_FUZZERS_SPVTOOLS_OPT_FUZZER_COMMON_H_ +#define TEST_FUZZERS_SPVTOOLS_OPT_FUZZER_COMMON_H_ + +#include <cinttypes> +#include <cstddef> +#include <functional> + +#include "spirv-tools/optimizer.hpp" + +namespace spvtools { +namespace fuzzers { + +// Helper function capturing the common logic for the various optimizer fuzzers. +int OptFuzzerTestOneInput( + const uint8_t* data, size_t size, + const std::function<void(spvtools::Optimizer&)>& register_passes); + +} // namespace fuzzers +} // namespace spvtools + +#endif // TEST_FUZZERS_SPVTOOLS_OPT_FUZZER_COMMON_H_ diff --git a/test/fuzzers/spvtools_opt_legalization_fuzzer.cpp b/test/fuzzers/spvtools_opt_legalization_fuzzer.cpp index 6f4d7e8b..fac4d23c 100644 --- a/test/fuzzers/spvtools_opt_legalization_fuzzer.cpp +++ b/test/fuzzers/spvtools_opt_legalization_fuzzer.cpp @@ -12,33 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include <cstdint> -#include <vector> +#include <cinttypes> +#include <cstddef> +#include <functional> #include "spirv-tools/optimizer.hpp" -#include "test/fuzzers/random_generator.h" +#include "test/fuzzers/spvtools_opt_fuzzer_common.h" extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - if (size < 1) { - return 0; - } - - spvtools::fuzzers::RandomGenerator random_gen(data, size); - spvtools::Optimizer optimizer(random_gen.GetTargetEnv()); - optimizer.SetMessageConsumer([](spv_message_level_t, const char*, - const spv_position_t&, const char*) {}); - - std::vector<uint32_t> input; - input.resize(size >> 2); - - size_t count = 0; - for (size_t i = 0; (i + 3) < size; i += 4) { - input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) | - (data[i + 3]) << 24; - } - - optimizer.RegisterLegalizationPasses(); - optimizer.Run(input.data(), input.size(), &input); - - return 0; + return spvtools::fuzzers::OptFuzzerTestOneInput( + data, size, [](spvtools::Optimizer& optimizer) -> void { + optimizer.RegisterLegalizationPasses(); + }); } diff --git a/test/fuzzers/spvtools_opt_performance_fuzzer.cpp b/test/fuzzers/spvtools_opt_performance_fuzzer.cpp index 9c47d7d7..e6038b90 100644 --- a/test/fuzzers/spvtools_opt_performance_fuzzer.cpp +++ b/test/fuzzers/spvtools_opt_performance_fuzzer.cpp @@ -12,33 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include <cstdint> -#include <vector> +#include <cinttypes> +#include <cstddef> +#include <functional> #include "spirv-tools/optimizer.hpp" -#include "test/fuzzers/random_generator.h" +#include "test/fuzzers/spvtools_opt_fuzzer_common.h" extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - if (size < 1) { - return 0; - } - - spvtools::fuzzers::RandomGenerator random_gen(data, size); - spvtools::Optimizer optimizer(random_gen.GetTargetEnv()); - optimizer.SetMessageConsumer([](spv_message_level_t, const char*, - const spv_position_t&, const char*) {}); - - std::vector<uint32_t> input; - input.resize(size >> 2); - - size_t count = 0; - for (size_t i = 0; (i + 3) < size; i += 4) { - input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) | - (data[i + 3]) << 24; - } - - optimizer.RegisterPerformancePasses(); - optimizer.Run(input.data(), input.size(), &input); - - return 0; + return spvtools::fuzzers::OptFuzzerTestOneInput( + data, size, [](spvtools::Optimizer& optimizer) -> void { + optimizer.RegisterPerformancePasses(); + }); } diff --git a/test/fuzzers/spvtools_opt_size_fuzzer.cpp b/test/fuzzers/spvtools_opt_size_fuzzer.cpp index 10fac42a..65492b1a 100644 --- a/test/fuzzers/spvtools_opt_size_fuzzer.cpp +++ b/test/fuzzers/spvtools_opt_size_fuzzer.cpp @@ -12,33 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include <cstdint> -#include <vector> +#include <cinttypes> +#include <cstddef> +#include <functional> #include "spirv-tools/optimizer.hpp" -#include "test/fuzzers/random_generator.h" +#include "test/fuzzers/spvtools_opt_fuzzer_common.h" extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - if (size < 1) { - return 0; - } - - spvtools::fuzzers::RandomGenerator random_gen(data, size); - spvtools::Optimizer optimizer(random_gen.GetTargetEnv()); - optimizer.SetMessageConsumer([](spv_message_level_t, const char*, - const spv_position_t&, const char*) {}); - - std::vector<uint32_t> input; - input.resize(size >> 2); - - size_t count = 0; - for (size_t i = 0; (i + 3) < size; i += 4) { - input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) | - (data[i + 3]) << 24; - } - - optimizer.RegisterSizePasses(); - optimizer.Run(input.data(), input.size(), &input); - - return 0; + return spvtools::fuzzers::OptFuzzerTestOneInput( + data, size, [](spvtools::Optimizer& optimizer) -> void { + optimizer.RegisterSizePasses(); + }); } diff --git a/test/hex_float_test.cpp b/test/hex_float_test.cpp index ffdb8bda..7edfd43d 100644 --- a/test/hex_float_test.cpp +++ b/test/hex_float_test.cpp @@ -1343,7 +1343,7 @@ struct StreamParseCase { template <typename T> std::ostream& operator<<(std::ostream& os, const StreamParseCase<T>& fspc) { os << "StreamParseCase(" << fspc.literal - << ", expect_succes:" << int(fspc.expect_success) << "," + << ", expect_success:" << int(fspc.expect_success) << "," << fspc.expected_suffix << "," << fspc.expected_value << ")"; return os; } diff --git a/test/link/binary_version_test.cpp b/test/link/binary_version_test.cpp index 80aab0fd..a56030f4 100644 --- a/test/link/binary_version_test.cpp +++ b/test/link/binary_version_test.cpp @@ -20,40 +20,57 @@ namespace spvtools { namespace { +using ::testing::HasSubstr; using BinaryVersion = spvtest::LinkerTest; -TEST_F(BinaryVersion, LinkerChoosesMaxSpirvVersion) { +spvtest::Binary CreateBinary(uint32_t version) { + return { + // clang-format off + // Header + SpvMagicNumber, + version, + SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS, 0), + 1u, // NOTE: Bound + 0u, // NOTE: Schema; reserved + + // OpCapability Shader + SpvOpCapability | 2u << SpvWordCountShift, + SpvCapabilityShader, + + // OpMemoryModel Logical Simple + SpvOpMemoryModel | 3u << SpvWordCountShift, + SpvAddressingModelLogical, + SpvMemoryModelSimple + // clang-format on + }; +} + +TEST_F(BinaryVersion, Match) { // clang-format off spvtest::Binaries binaries = { - { - SpvMagicNumber, - 0x00010300u, - SPV_GENERATOR_CODEPLAY, - 1u, // NOTE: Bound - 0u // NOTE: Schema; reserved - }, - { - SpvMagicNumber, - 0x00010500u, - SPV_GENERATOR_CODEPLAY, - 1u, // NOTE: Bound - 0u // NOTE: Schema; reserved - }, - { - SpvMagicNumber, - 0x00010100u, - SPV_GENERATOR_CODEPLAY, - 1u, // NOTE: Bound - 0u // NOTE: Schema; reserved - } + CreateBinary(SPV_SPIRV_VERSION_WORD(1, 3)), + CreateBinary(SPV_SPIRV_VERSION_WORD(1, 3)), }; // clang-format on spvtest::Binary linked_binary; - - ASSERT_EQ(SPV_SUCCESS, Link(binaries, &linked_binary)); + ASSERT_EQ(SPV_SUCCESS, Link(binaries, &linked_binary)) << GetErrorMessage(); EXPECT_THAT(GetErrorMessage(), std::string()); + EXPECT_EQ(SPV_SPIRV_VERSION_WORD(1, 3), linked_binary[1]); +} - EXPECT_EQ(0x00010500u, linked_binary[1]); +TEST_F(BinaryVersion, Mismatch) { + // clang-format off + spvtest::Binaries binaries = { + CreateBinary(SPV_SPIRV_VERSION_WORD(1, 3)), + CreateBinary(SPV_SPIRV_VERSION_WORD(1, 5)), + }; + // clang-format on + spvtest::Binary linked_binary; + ASSERT_EQ(SPV_ERROR_INTERNAL, Link(binaries, &linked_binary)) + << GetErrorMessage(); + EXPECT_THAT(GetErrorMessage(), + HasSubstr("Conflicting SPIR-V versions: 1.3 (input modules 1 " + "through 1) vs 1.5 (input module 2).")); } } // namespace diff --git a/test/link/entry_points_test.cpp b/test/link/entry_points_test.cpp index bac8e02e..df7ea20c 100644 --- a/test/link/entry_points_test.cpp +++ b/test/link/entry_points_test.cpp @@ -26,6 +26,8 @@ class EntryPoints : public spvtest::LinkerTest {}; TEST_F(EntryPoints, SameModelDifferentName) { const std::string body1 = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %3 "foo" %1 = OpTypeVoid %2 = OpTypeFunction %1 @@ -33,6 +35,8 @@ OpEntryPoint GLCompute %3 "foo" OpFunctionEnd )"; const std::string body2 = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %3 "bar" %1 = OpTypeVoid %2 = OpTypeFunction %1 @@ -41,12 +45,15 @@ OpFunctionEnd )"; spvtest::Binary linked_binary; - EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)); + ASSERT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)) + << GetErrorMessage(); EXPECT_THAT(GetErrorMessage(), std::string()); } TEST_F(EntryPoints, DifferentModelSameName) { const std::string body1 = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %3 "foo" %1 = OpTypeVoid %2 = OpTypeFunction %1 @@ -54,6 +61,8 @@ OpEntryPoint GLCompute %3 "foo" OpFunctionEnd )"; const std::string body2 = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 OpEntryPoint Vertex %3 "foo" %1 = OpTypeVoid %2 = OpTypeFunction %1 @@ -62,12 +71,15 @@ OpFunctionEnd )"; spvtest::Binary linked_binary; - EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)); + ASSERT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)) + << GetErrorMessage(); EXPECT_THAT(GetErrorMessage(), std::string()); } TEST_F(EntryPoints, SameModelAndName) { const std::string body1 = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %3 "foo" %1 = OpTypeVoid %2 = OpTypeFunction %1 @@ -75,6 +87,8 @@ OpEntryPoint GLCompute %3 "foo" OpFunctionEnd )"; const std::string body2 = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %3 "foo" %1 = OpTypeVoid %2 = OpTypeFunction %1 diff --git a/test/link/global_values_amount_test.cpp b/test/link/global_values_amount_test.cpp index 2c4ee1f0..3158b7ec 100644 --- a/test/link/global_values_amount_test.cpp +++ b/test/link/global_values_amount_test.cpp @@ -22,85 +22,56 @@ namespace { using ::testing::HasSubstr; +const uint32_t binary_count = 2u; + class EntryPointsAmountTest : public spvtest::LinkerTest { public: - EntryPointsAmountTest() { binaries.reserve(0xFFFF); } + EntryPointsAmountTest() { binaries.reserve(binary_count + 1u); } void SetUp() override { - binaries.push_back({SpvMagicNumber, - SpvVersion, - SPV_GENERATOR_CODEPLAY, - 10u, // NOTE: Bound - 0u, // NOTE: Schema; reserved - - 3u << SpvWordCountShift | SpvOpTypeFloat, - 1u, // NOTE: Result ID - 32u, // NOTE: Width - - 4u << SpvWordCountShift | SpvOpTypePointer, - 2u, // NOTE: Result ID - SpvStorageClassInput, - 1u, // NOTE: Type ID - - 2u << SpvWordCountShift | SpvOpTypeVoid, - 3u, // NOTE: Result ID - - 3u << SpvWordCountShift | SpvOpTypeFunction, - 4u, // NOTE: Result ID - 3u, // NOTE: Return type - - 5u << SpvWordCountShift | SpvOpFunction, - 3u, // NOTE: Result type - 5u, // NOTE: Result ID - SpvFunctionControlMaskNone, - 4u, // NOTE: Function type - - 2u << SpvWordCountShift | SpvOpLabel, - 6u, // NOTE: Result ID - - 4u << SpvWordCountShift | SpvOpVariable, - 2u, // NOTE: Type ID - 7u, // NOTE: Result ID - SpvStorageClassFunction, - - 4u << SpvWordCountShift | SpvOpVariable, - 2u, // NOTE: Type ID - 8u, // NOTE: Result ID - SpvStorageClassFunction, - - 4u << SpvWordCountShift | SpvOpVariable, - 2u, // NOTE: Type ID - 9u, // NOTE: Result ID - SpvStorageClassFunction, - - 1u << SpvWordCountShift | SpvOpReturn, - - 1u << SpvWordCountShift | SpvOpFunctionEnd}); - for (size_t i = 0u; i < 2u; ++i) { - spvtest::Binary binary = { - SpvMagicNumber, - SpvVersion, - SPV_GENERATOR_CODEPLAY, - 103u, // NOTE: Bound - 0u, // NOTE: Schema; reserved - - 3u << SpvWordCountShift | SpvOpTypeFloat, - 1u, // NOTE: Result ID - 32u, // NOTE: Width - - 4u << SpvWordCountShift | SpvOpTypePointer, - 2u, // NOTE: Result ID - SpvStorageClassInput, - 1u // NOTE: Type ID - }; - - for (uint32_t j = 0u; j < 0xFFFFu / 2u; ++j) { - binary.push_back(4u << SpvWordCountShift | SpvOpVariable); - binary.push_back(2u); // NOTE: Type ID - binary.push_back(j + 3u); // NOTE: Result ID - binary.push_back(SpvStorageClassInput); - } - binaries.push_back(binary); + const uint32_t global_variable_count_per_binary = + (SPV_LIMIT_GLOBAL_VARIABLES_MAX - 1u) / binary_count; + + spvtest::Binary common_binary = { + // clang-format off + SpvMagicNumber, + SpvVersion, + SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS, 0), + 3u + global_variable_count_per_binary, // NOTE: Bound + 0u, // NOTE: Schema; reserved + + SpvOpCapability | 2u << SpvWordCountShift, + SpvCapabilityShader, + + SpvOpMemoryModel | 3u << SpvWordCountShift, + SpvAddressingModelLogical, + SpvMemoryModelSimple, + + SpvOpTypeFloat | 3u << SpvWordCountShift, + 1u, // NOTE: Result ID + 32u, // NOTE: Width + + SpvOpTypePointer | 4u << SpvWordCountShift, + 2u, // NOTE: Result ID + SpvStorageClassInput, + 1u // NOTE: Type ID + // clang-format on + }; + + binaries.push_back({}); + spvtest::Binary& binary = binaries.back(); + binary.reserve(common_binary.size() + global_variable_count_per_binary * 4); + binary.insert(binary.end(), common_binary.cbegin(), common_binary.cend()); + + for (uint32_t i = 0u; i < global_variable_count_per_binary; ++i) { + binary.push_back(SpvOpVariable | 4u << SpvWordCountShift); + binary.push_back(2u); // NOTE: Type ID + binary.push_back(3u + i); // NOTE: Result ID + binary.push_back(SpvStorageClassInput); + } + + for (uint32_t i = 0u; i < binary_count - 1u; ++i) { + binaries.push_back(binaries.back()); } } void TearDown() override { binaries.clear(); } @@ -111,42 +82,53 @@ class EntryPointsAmountTest : public spvtest::LinkerTest { TEST_F(EntryPointsAmountTest, UnderLimit) { spvtest::Binary linked_binary; - EXPECT_EQ(SPV_SUCCESS, Link(binaries, &linked_binary)); + ASSERT_EQ(SPV_SUCCESS, Link(binaries, &linked_binary)) << GetErrorMessage(); EXPECT_THAT(GetErrorMessage(), std::string()); } TEST_F(EntryPointsAmountTest, OverLimit) { - binaries.push_back({SpvMagicNumber, - SpvVersion, - SPV_GENERATOR_CODEPLAY, - 5u, // NOTE: Bound - 0u, // NOTE: Schema; reserved - - 3u << SpvWordCountShift | SpvOpTypeFloat, - 1u, // NOTE: Result ID - 32u, // NOTE: Width - - 4u << SpvWordCountShift | SpvOpTypePointer, - 2u, // NOTE: Result ID - SpvStorageClassInput, - 1u, // NOTE: Type ID - - 4u << SpvWordCountShift | SpvOpVariable, - 2u, // NOTE: Type ID - 3u, // NOTE: Result ID - SpvStorageClassInput, - - 4u << SpvWordCountShift | SpvOpVariable, - 2u, // NOTE: Type ID - 4u, // NOTE: Result ID - SpvStorageClassInput}); + binaries.push_back({ + // clang-format off + SpvMagicNumber, + SpvVersion, + SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS, 0), + 5u, // NOTE: Bound + 0u, // NOTE: Schema; reserved + + SpvOpCapability | 2u << SpvWordCountShift, + SpvCapabilityShader, + + SpvOpMemoryModel | 3u << SpvWordCountShift, + SpvAddressingModelLogical, + SpvMemoryModelSimple, + + SpvOpTypeFloat | 3u << SpvWordCountShift, + 1u, // NOTE: Result ID + 32u, // NOTE: Width + + SpvOpTypePointer | 4u << SpvWordCountShift, + 2u, // NOTE: Result ID + SpvStorageClassInput, + 1u, // NOTE: Type ID + + SpvOpVariable | 4u << SpvWordCountShift, + 2u, // NOTE: Type ID + 3u, // NOTE: Result ID + SpvStorageClassInput, + + SpvOpVariable | 4u << SpvWordCountShift, + 2u, // NOTE: Type ID + 4u, // NOTE: Result ID + SpvStorageClassInput + // clang-format on + }); spvtest::Binary linked_binary; - - EXPECT_EQ(SPV_ERROR_INTERNAL, Link(binaries, &linked_binary)); - EXPECT_THAT(GetErrorMessage(), - HasSubstr("The limit of global values, 65535, was exceeded; " - "65536 global values were found.")); + ASSERT_EQ(SPV_SUCCESS, Link(binaries, &linked_binary)) << GetErrorMessage(); + EXPECT_THAT( + GetErrorMessage(), + HasSubstr("The minimum limit of global values, 65535, was exceeded; " + "65536 global values were found.")); } } // namespace diff --git a/test/link/ids_limit_test.cpp b/test/link/ids_limit_test.cpp index 6d7815a2..846fbef8 100644 --- a/test/link/ids_limit_test.cpp +++ b/test/link/ids_limit_test.cpp @@ -21,51 +21,114 @@ namespace spvtools { namespace { using ::testing::HasSubstr; -using IdsLimit = spvtest::LinkerTest; - -TEST_F(IdsLimit, UnderLimit) { - spvtest::Binaries binaries = { - { - SpvMagicNumber, SpvVersion, SPV_GENERATOR_CODEPLAY, - 0x2FFFFFu, // NOTE: Bound - 0u, // NOTE: Schema; reserved - }, - { - SpvMagicNumber, SpvVersion, SPV_GENERATOR_CODEPLAY, - 0x100000u, // NOTE: Bound - 0u, // NOTE: Schema; reserved - }}; - spvtest::Binary linked_binary; - ASSERT_EQ(SPV_SUCCESS, Link(binaries, &linked_binary)); +class IdsLimit : public spvtest::LinkerTest { + public: + IdsLimit() { binaries.reserve(2u); } + + void SetUp() override { + const uint32_t id_bound = SPV_LIMIT_RESULT_ID_BOUND - 1u; + const uint32_t constant_count = + id_bound - + 2u; // One ID is used for TypeBool, and (constant_count + 1) < id_bound + + // This is needed, as otherwise the ID bound will get reset to 1 while + // running the RemoveDuplicates pass. + spvtest::Binary common_binary = { + // clang-format off + SpvMagicNumber, + SpvVersion, + SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS, 0), + id_bound, // NOTE: Bound + 0u, // NOTE: Schema; reserved + + SpvOpCapability | 2u << SpvWordCountShift, + SpvCapabilityShader, + + SpvOpMemoryModel | 3u << SpvWordCountShift, + SpvAddressingModelLogical, + SpvMemoryModelSimple, + + SpvOpTypeBool | 2u << SpvWordCountShift, + 1u // NOTE: Result ID + // clang-format on + }; + + binaries.push_back({}); + spvtest::Binary& binary = binaries.back(); + binary.reserve(common_binary.size() + constant_count * 3u); + binary.insert(binary.end(), common_binary.cbegin(), common_binary.cend()); + + for (uint32_t i = 0u; i < constant_count; ++i) { + binary.push_back(SpvOpConstantTrue | 3u << SpvWordCountShift); + binary.push_back(1u); // NOTE: Type ID + binary.push_back(2u + i); // NOTE: Result ID + } + } + void TearDown() override { binaries.clear(); } + + spvtest::Binaries binaries; +}; + +spvtest::Binary CreateBinary(uint32_t id_bound) { + return { + // clang-format off + // Header + SpvMagicNumber, + SpvVersion, + SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS, 0), + id_bound, // NOTE: Bound + 0u, // NOTE: Schema; reserved + + // OpCapability Shader + SpvOpCapability | 2u << SpvWordCountShift, + SpvCapabilityShader, + + // OpMemoryModel Logical Simple + SpvOpMemoryModel | 3u << SpvWordCountShift, + SpvAddressingModelLogical, + SpvMemoryModelSimple + // clang-format on + }; +} + +TEST_F(IdsLimit, DISABLED_UnderLimit) { + spvtest::Binary linked_binary; + ASSERT_EQ(SPV_SUCCESS, Link(binaries, &linked_binary)) << GetErrorMessage(); EXPECT_THAT(GetErrorMessage(), std::string()); - EXPECT_EQ(0x3FFFFEu, linked_binary[3]); + EXPECT_EQ(0x3FFFFFu, linked_binary[3]); +} + +TEST_F(IdsLimit, DISABLED_OverLimit) { + spvtest::Binary& binary = binaries.back(); + + const uint32_t id_bound = binary[3]; + binary[3] = id_bound + 1u; + + binary.push_back(SpvOpConstantFalse | 3u << SpvWordCountShift); + binary.push_back(1u); // NOTE: Type ID + binary.push_back(id_bound); // NOTE: Result ID + + spvtest::Binary linked_binary; + ASSERT_EQ(SPV_SUCCESS, Link(binaries, &linked_binary)) << GetErrorMessage(); + EXPECT_THAT( + GetErrorMessage(), + HasSubstr("The minimum limit of IDs, 4194303, was exceeded: 4194304 is " + "the current ID bound.")); + EXPECT_EQ(0x400000u, linked_binary[3]); } -TEST_F(IdsLimit, OverLimit) { - spvtest::Binaries binaries = { - { - SpvMagicNumber, SpvVersion, SPV_GENERATOR_CODEPLAY, - 0x2FFFFFu, // NOTE: Bound - 0u, // NOTE: Schema; reserved - }, - { - SpvMagicNumber, SpvVersion, SPV_GENERATOR_CODEPLAY, - 0x100000u, // NOTE: Bound - 0u, // NOTE: Schema; reserved - }, - { - SpvMagicNumber, SpvVersion, SPV_GENERATOR_CODEPLAY, - 3u, // NOTE: Bound - 0u, // NOTE: Schema; reserved - }}; +TEST_F(IdsLimit, DISABLED_Overflow) { + spvtest::Binaries binaries = {CreateBinary(0xFFFFFFFFu), + CreateBinary(0x00000002u)}; spvtest::Binary linked_binary; - EXPECT_EQ(SPV_ERROR_INVALID_ID, Link(binaries, &linked_binary)); - EXPECT_THAT(GetErrorMessage(), - HasSubstr("The limit of IDs, 4194303, was exceeded: 4194304 is " - "the current ID bound.")); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, Link(binaries, &linked_binary)); + EXPECT_THAT( + GetErrorMessage(), + HasSubstr("Too many IDs (4294967296): combining all modules would " + "overflow the 32-bit word of the SPIR-V header.")); } } // namespace diff --git a/test/link/matching_imports_to_exports_test.cpp b/test/link/matching_imports_to_exports_test.cpp index e76c69fb..6b02fc46 100644 --- a/test/link/matching_imports_to_exports_test.cpp +++ b/test/link/matching_imports_to_exports_test.cpp @@ -26,6 +26,9 @@ using MatchingImportsToExports = spvtest::LinkerTest; TEST_F(MatchingImportsToExports, Default) { const std::string body1 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Import %2 = OpTypeFloat 32 %1 = OpVariable %2 Uniform @@ -33,6 +36,9 @@ OpDecorate %1 LinkageAttributes "foo" Import )"; const std::string body2 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Export %2 = OpTypeFloat 32 %3 = OpConstant %2 42 @@ -40,11 +46,14 @@ OpDecorate %1 LinkageAttributes "foo" Export )"; spvtest::Binary linked_binary; - EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)) + ASSERT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)) << GetErrorMessage(); const std::string expected_res = - R"(OpModuleProcessed "Linked by SPIR-V Tools Linker" + R"(OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL +OpModuleProcessed "Linked by SPIR-V Tools Linker" %1 = OpTypeFloat 32 %2 = OpVariable %1 Input %3 = OpConstant %1 42 @@ -52,7 +61,7 @@ OpDecorate %1 LinkageAttributes "foo" Export )"; std::string res_body; SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); - EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) + ASSERT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) << GetErrorMessage(); EXPECT_EQ(expected_res, res_body); } @@ -60,23 +69,29 @@ OpDecorate %1 LinkageAttributes "foo" Export TEST_F(MatchingImportsToExports, NotALibraryExtraExports) { const std::string body = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Export %2 = OpTypeFloat 32 %1 = OpVariable %2 Uniform )"; spvtest::Binary linked_binary; - EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body}, &linked_binary)) + ASSERT_EQ(SPV_SUCCESS, AssembleAndLink({body}, &linked_binary)) << GetErrorMessage(); const std::string expected_res = - R"(OpModuleProcessed "Linked by SPIR-V Tools Linker" + R"(OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL +OpModuleProcessed "Linked by SPIR-V Tools Linker" %1 = OpTypeFloat 32 %2 = OpVariable %1 Uniform )"; std::string res_body; SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); - EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) + ASSERT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) << GetErrorMessage(); EXPECT_EQ(expected_res, res_body); } @@ -84,6 +99,9 @@ OpDecorate %1 LinkageAttributes "foo" Export TEST_F(MatchingImportsToExports, LibraryExtraExports) { const std::string body = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Export %2 = OpTypeFloat 32 %1 = OpVariable %2 Uniform @@ -92,10 +110,13 @@ OpDecorate %1 LinkageAttributes "foo" Export spvtest::Binary linked_binary; LinkerOptions options; options.SetCreateLibrary(true); - EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body}, &linked_binary, options)) + ASSERT_EQ(SPV_SUCCESS, AssembleAndLink({body}, &linked_binary, options)) << GetErrorMessage(); const std::string expected_res = R"(OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpModuleProcessed "Linked by SPIR-V Tools Linker" OpDecorate %1 LinkageAttributes "foo" Export %2 = OpTypeFloat 32 @@ -103,7 +124,7 @@ OpDecorate %1 LinkageAttributes "foo" Export )"; std::string res_body; SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); - EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) + ASSERT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) << GetErrorMessage(); EXPECT_EQ(expected_res, res_body); } @@ -111,11 +132,18 @@ OpDecorate %1 LinkageAttributes "foo" Export TEST_F(MatchingImportsToExports, UnresolvedImports) { const std::string body1 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Import %2 = OpTypeFloat 32 %1 = OpVariable %2 Uniform )"; - const std::string body2 = R"()"; + const std::string body2 = R"( +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL +)"; spvtest::Binary linked_binary; EXPECT_EQ(SPV_ERROR_INVALID_BINARY, @@ -127,6 +155,9 @@ OpDecorate %1 LinkageAttributes "foo" Import TEST_F(MatchingImportsToExports, TypeMismatch) { const std::string body1 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Import %2 = OpTypeFloat 32 %1 = OpVariable %2 Uniform @@ -134,6 +165,9 @@ OpDecorate %1 LinkageAttributes "foo" Import )"; const std::string body2 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Export %2 = OpTypeInt 32 0 %3 = OpConstant %2 42 @@ -153,6 +187,9 @@ OpDecorate %1 LinkageAttributes "foo" Export TEST_F(MatchingImportsToExports, MultipleDefinitions) { const std::string body1 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Import %2 = OpTypeFloat 32 %1 = OpVariable %2 Uniform @@ -160,6 +197,9 @@ OpDecorate %1 LinkageAttributes "foo" Import )"; const std::string body2 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Export %2 = OpTypeFloat 32 %3 = OpConstant %2 42 @@ -167,6 +207,9 @@ OpDecorate %1 LinkageAttributes "foo" Export )"; const std::string body3 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Export %2 = OpTypeFloat 32 %3 = OpConstant %2 -1 @@ -185,6 +228,9 @@ OpDecorate %1 LinkageAttributes "foo" Export TEST_F(MatchingImportsToExports, SameNameDifferentTypes) { const std::string body1 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Import %2 = OpTypeFloat 32 %1 = OpVariable %2 Uniform @@ -192,6 +238,9 @@ OpDecorate %1 LinkageAttributes "foo" Import )"; const std::string body2 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Export %2 = OpTypeInt 32 0 %3 = OpConstant %2 42 @@ -199,6 +248,9 @@ OpDecorate %1 LinkageAttributes "foo" Export )"; const std::string body3 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Export %2 = OpTypeFloat 32 %3 = OpConstant %2 12 @@ -217,6 +269,9 @@ OpDecorate %1 LinkageAttributes "foo" Export TEST_F(MatchingImportsToExports, DecorationMismatch) { const std::string body1 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Import OpDecorate %2 Constant %2 = OpTypeFloat 32 @@ -225,6 +280,9 @@ OpDecorate %2 Constant )"; const std::string body2 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Export %2 = OpTypeFloat 32 %3 = OpConstant %2 42 @@ -244,8 +302,10 @@ OpDecorate %1 LinkageAttributes "foo" Export TEST_F(MatchingImportsToExports, FuncParamAttrDifferButStillMatchExportToImport) { const std::string body1 = R"( -OpCapability Kernel OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Import OpDecorate %2 FuncParamAttr Zext %3 = OpTypeVoid @@ -256,8 +316,10 @@ OpDecorate %2 FuncParamAttr Zext OpFunctionEnd )"; const std::string body2 = R"( -OpCapability Kernel OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Export OpDecorate %2 FuncParamAttr Sext %3 = OpTypeVoid @@ -271,10 +333,12 @@ OpFunctionEnd )"; spvtest::Binary linked_binary; - EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)) + ASSERT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)) << GetErrorMessage(); - const std::string expected_res = R"(OpCapability Kernel + const std::string expected_res = R"(OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpModuleProcessed "Linked by SPIR-V Tools Linker" OpDecorate %1 FuncParamAttr Sext %2 = OpTypeVoid @@ -288,7 +352,7 @@ OpFunctionEnd )"; std::string res_body; SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); - EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) + ASSERT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) << GetErrorMessage(); EXPECT_EQ(expected_res, res_body); } @@ -296,6 +360,9 @@ OpFunctionEnd TEST_F(MatchingImportsToExports, FunctionCtrl) { const std::string body1 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Import %2 = OpTypeVoid %3 = OpTypeFunction %2 @@ -306,6 +373,9 @@ OpFunctionEnd )"; const std::string body2 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Export %2 = OpTypeVoid %3 = OpTypeFunction %2 @@ -316,11 +386,14 @@ OpFunctionEnd )"; spvtest::Binary linked_binary; - EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)) + ASSERT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)) << GetErrorMessage(); const std::string expected_res = - R"(OpModuleProcessed "Linked by SPIR-V Tools Linker" + R"(OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL +OpModuleProcessed "Linked by SPIR-V Tools Linker" %1 = OpTypeVoid %2 = OpTypeFunction %1 %3 = OpTypeFloat 32 @@ -332,15 +405,17 @@ OpFunctionEnd )"; std::string res_body; SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); - EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) + ASSERT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) << GetErrorMessage(); EXPECT_EQ(expected_res, res_body); } TEST_F(MatchingImportsToExports, UseExportedFuncParamAttr) { const std::string body1 = R"( -OpCapability Kernel OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Import OpDecorate %2 FuncParamAttr Zext %2 = OpDecorationGroup @@ -356,8 +431,10 @@ OpFunctionEnd OpFunctionEnd )"; const std::string body2 = R"( -OpCapability Kernel OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Export OpDecorate %2 FuncParamAttr Sext %3 = OpTypeVoid @@ -371,10 +448,12 @@ OpFunctionEnd )"; spvtest::Binary linked_binary; - EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)) + ASSERT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)) << GetErrorMessage(); - const std::string expected_res = R"(OpCapability Kernel + const std::string expected_res = R"(OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpModuleProcessed "Linked by SPIR-V Tools Linker" OpDecorate %1 FuncParamAttr Zext %1 = OpDecorationGroup @@ -394,15 +473,17 @@ OpFunctionEnd )"; std::string res_body; SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); - EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) + ASSERT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) << GetErrorMessage(); EXPECT_EQ(expected_res, res_body); } TEST_F(MatchingImportsToExports, NamesAndDecorations) { const std::string body1 = R"( -OpCapability Kernel OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpName %1 "foo" OpName %3 "param" OpDecorate %1 LinkageAttributes "foo" Import @@ -422,8 +503,10 @@ OpFunctionEnd OpFunctionEnd )"; const std::string body2 = R"( -OpCapability Kernel OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpName %1 "foo" OpName %2 "param" OpDecorate %1 LinkageAttributes "foo" Export @@ -440,10 +523,12 @@ OpFunctionEnd )"; spvtest::Binary linked_binary; - EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)) + ASSERT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)) << GetErrorMessage(); - const std::string expected_res = R"(OpCapability Kernel + const std::string expected_res = R"(OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpName %1 "foo" OpName %2 "param" OpModuleProcessed "Linked by SPIR-V Tools Linker" @@ -467,7 +552,7 @@ OpFunctionEnd )"; std::string res_body; SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); - EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) + ASSERT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) << GetErrorMessage(); EXPECT_EQ(expected_res, res_body); } diff --git a/test/link/memory_model_test.cpp b/test/link/memory_model_test.cpp index 2add5046..280a776a 100644 --- a/test/link/memory_model_test.cpp +++ b/test/link/memory_model_test.cpp @@ -50,9 +50,9 @@ OpMemoryModel Physical32 Simple spvtest::Binary linked_binary; EXPECT_EQ(SPV_ERROR_INTERNAL, AssembleAndLink({body1, body2}, &linked_binary)); - EXPECT_THAT( - GetErrorMessage(), - HasSubstr("Conflicting addressing models: Logical vs Physical32.")); + EXPECT_THAT(GetErrorMessage(), + HasSubstr("Conflicting addressing models: Logical (input modules " + "1 through 1) vs Physical32 (input module 2).")); } TEST_F(MemoryModel, MemoryMismatch) { @@ -67,7 +67,38 @@ OpMemoryModel Logical GLSL450 EXPECT_EQ(SPV_ERROR_INTERNAL, AssembleAndLink({body1, body2}, &linked_binary)); EXPECT_THAT(GetErrorMessage(), - HasSubstr("Conflicting memory models: Simple vs GLSL450.")); + HasSubstr("Conflicting memory models: Simple (input modules 1 " + "through 1) vs GLSL450 (input module 2).")); +} + +TEST_F(MemoryModel, FirstLackMemoryModel) { + const std::string body1 = R"( +)"; + const std::string body2 = R"( +OpMemoryModel Logical GLSL450 +)"; + + spvtest::Binary linked_binary; + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, + AssembleAndLink({body1, body2}, &linked_binary)); + EXPECT_THAT( + GetErrorMessage(), + HasSubstr("Input module 1 is lacking an OpMemoryModel instruction.")); +} + +TEST_F(MemoryModel, SecondLackMemoryModel) { + const std::string body1 = R"( +OpMemoryModel Logical GLSL450 +)"; + const std::string body2 = R"( +)"; + + spvtest::Binary linked_binary; + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, + AssembleAndLink({body1, body2}, &linked_binary)); + EXPECT_THAT( + GetErrorMessage(), + HasSubstr("Input module 2 is lacking an OpMemoryModel instruction.")); } } // namespace diff --git a/test/link/partial_linkage_test.cpp b/test/link/partial_linkage_test.cpp index c43b06e5..bf4b508b 100644 --- a/test/link/partial_linkage_test.cpp +++ b/test/link/partial_linkage_test.cpp @@ -26,6 +26,9 @@ using PartialLinkage = spvtest::LinkerTest; TEST_F(PartialLinkage, Allowed) { const std::string body1 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Import OpDecorate %2 LinkageAttributes "bar" Import %3 = OpTypeFloat 32 @@ -34,6 +37,9 @@ OpDecorate %2 LinkageAttributes "bar" Import )"; const std::string body2 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "bar" Export %2 = OpTypeFloat 32 %3 = OpConstant %2 3.1415 @@ -44,9 +50,13 @@ OpDecorate %1 LinkageAttributes "bar" Export LinkerOptions linker_options; linker_options.SetAllowPartialLinkage(true); ASSERT_EQ(SPV_SUCCESS, - AssembleAndLink({body1, body2}, &linked_binary, linker_options)); + AssembleAndLink({body1, body2}, &linked_binary, linker_options)) + << GetErrorMessage(); const std::string expected_res = R"(OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpModuleProcessed "Linked by SPIR-V Tools Linker" OpDecorate %1 LinkageAttributes "foo" Import %2 = OpTypeFloat 32 @@ -64,6 +74,9 @@ OpDecorate %1 LinkageAttributes "foo" Import TEST_F(PartialLinkage, Disallowed) { const std::string body1 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "foo" Import OpDecorate %2 LinkageAttributes "bar" Import %3 = OpTypeFloat 32 @@ -72,6 +85,9 @@ OpDecorate %2 LinkageAttributes "bar" Import )"; const std::string body2 = R"( OpCapability Linkage +OpCapability Addresses +OpCapability Kernel +OpMemoryModel Physical64 OpenCL OpDecorate %1 LinkageAttributes "bar" Export %2 = OpTypeFloat 32 %3 = OpConstant %2 3.1415 diff --git a/test/link/type_match_test.cpp b/test/link/type_match_test.cpp index dae70c16..efc5cf7e 100644 --- a/test/link/type_match_test.cpp +++ b/test/link/type_match_test.cpp @@ -66,6 +66,7 @@ using TypeMatch = spvtest::LinkerTest; "OpCapability Kernel\n" \ "OpCapability Shader\n" \ "OpCapability Addresses\n" \ + "OpMemoryModel Physical64 OpenCL\n" \ "OpDecorate %var LinkageAttributes \"foo\" " \ "{Import,Export}\n" \ "; CHECK: [[baseint:%\\w+]] = OpTypeInt 32 1\n" \ diff --git a/test/link/unique_ids_test.cpp b/test/link/unique_ids_test.cpp index 55c70ea6..16a9b52a 100644 --- a/test/link/unique_ids_test.cpp +++ b/test/link/unique_ids_test.cpp @@ -135,7 +135,8 @@ TEST_F(UniqueIds, UniquelyMerged) { LinkerOptions options; options.SetVerifyIds(true); spv_result_t res = AssembleAndLink(bodies, &linked_binary, options); - EXPECT_EQ(SPV_SUCCESS, res); + ASSERT_EQ(SPV_SUCCESS, res) << GetErrorMessage(); + EXPECT_THAT(GetErrorMessage(), std::string()); } } // namespace diff --git a/test/operand_pattern_test.cpp b/test/operand_pattern_test.cpp index 1caf008f..a98a9d76 100644 --- a/test/operand_pattern_test.cpp +++ b/test/operand_pattern_test.cpp @@ -91,9 +91,14 @@ INSTANTIATE_TEST_SUITE_P( {SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS, 0, {PREFIX0}, {PREFIX0}}, // Unknown bits means no change. Use all bits that aren't in the // grammar. - // The last mask enum is 0x20 + // The used mask bits are: + // 1 through... + // 0x20 SpvMemoryAccessNonPrivatePointerMask + // also + // 0x10000 SpvMemoryAccessAliasScopeINTELMaskShift + // 0x20000 SpvMemoryAccessNoAliasINTELMaskMask {SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS, - 0xffffffc0, + 0xffffffc0 ^ (0x10000) ^ (0x20000), {PREFIX1}, {PREFIX1}}, // Volatile has no operands. @@ -111,6 +116,7 @@ INSTANTIATE_TEST_SUITE_P( SpvMemoryAccessVolatileMask | SpvMemoryAccessAlignedMask, {PREFIX1}, {PREFIX1, SPV_OPERAND_TYPE_LITERAL_INTEGER}}, + // Newer masks are not tested })); #undef PREFIX0 #undef PREFIX1 diff --git a/test/opt/CMakeLists.txt b/test/opt/CMakeLists.txt index bc44e8d6..6dfb1b75 100644 --- a/test/opt/CMakeLists.txt +++ b/test/opt/CMakeLists.txt @@ -42,6 +42,7 @@ add_spvtools_unittest(TARGET opt desc_sroa_test.cpp eliminate_dead_const_test.cpp eliminate_dead_functions_test.cpp + eliminate_dead_input_components_test.cpp eliminate_dead_member_test.cpp feature_manager_test.cpp fix_storage_class_test.cpp @@ -82,6 +83,7 @@ add_spvtools_unittest(TARGET opt propagator_test.cpp reduce_load_size_test.cpp redundancy_elimination_test.cpp + remove_dontinline_test.cpp remove_unused_interface_variables_test.cpp register_liveness.cpp relax_float_ops_test.cpp @@ -91,9 +93,10 @@ add_spvtools_unittest(TARGET opt scalar_replacement_test.cpp set_spec_const_default_value_test.cpp simplification_test.cpp + spread_volatile_semantics_test.cpp strength_reduction_test.cpp strip_debug_info_test.cpp - strip_reflect_info_test.cpp + strip_nonsemantic_info_test.cpp struct_cfg_analysis_test.cpp type_manager_test.cpp types_test.cpp diff --git a/test/opt/aggressive_dead_code_elim_test.cpp b/test/opt/aggressive_dead_code_elim_test.cpp index f228c8cb..25f85416 100644 --- a/test/opt/aggressive_dead_code_elim_test.cpp +++ b/test/opt/aggressive_dead_code_elim_test.cpp @@ -38,55 +38,45 @@ TEST_F(AggressiveDCETest, EliminateExtendedInst) { // vec4 dv = sqrt(Dead); // gl_FragColor = v; // } - - const std::string predefs1 = - R"(OpCapability Shader + const std::string spirv = R"( +OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 +; CHECK: OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor OpEntryPoint Fragment %main "main" %BaseColor %Dead %gl_FragColor OpExecutionMode %main OriginUpperLeft OpSource GLSL 140 -)"; - - const std::string names_before = - R"(OpName %main "main" +OpName %main "main" OpName %v "v" OpName %BaseColor "BaseColor" +; CHECK-NOT: OpName %dv "dv" OpName %dv "dv" +; CHECK-NOT: OpName %Dead "Dead" OpName %Dead "Dead" OpName %gl_FragColor "gl_FragColor" -)"; - - const std::string names_after = - R"(OpName %main "main" -OpName %v "v" -OpName %BaseColor "BaseColor" -OpName %Dead "Dead" -OpName %gl_FragColor "gl_FragColor" -)"; - - const std::string predefs2 = - R"(%void = OpTypeVoid +%void = OpTypeVoid %9 = OpTypeFunction %void %float = OpTypeFloat 32 %v4float = OpTypeVector %float 4 %_ptr_Function_v4float = OpTypePointer Function %v4float %_ptr_Input_v4float = OpTypePointer Input %v4float %BaseColor = OpVariable %_ptr_Input_v4float Input +; CHECK-NOT: %Dead = OpVariable %Dead = OpVariable %_ptr_Input_v4float Input %_ptr_Output_v4float = OpTypePointer Output %v4float %gl_FragColor = OpVariable %_ptr_Output_v4float Output -)"; - - const std::string func_before = - R"(%main = OpFunction %void None %9 +%main = OpFunction %void None %9 %15 = OpLabel %v = OpVariable %_ptr_Function_v4float Function +; CHECK-NOT: %dv = OpVariable %dv = OpVariable %_ptr_Function_v4float Function %16 = OpLoad %v4float %BaseColor OpStore %v %16 +; CHECK-NOT: OpLoad %v4float %Dead %17 = OpLoad %v4float %Dead +; CHECK-NOT: OpExtInst %v4float %1 Sqrt %18 = OpExtInst %v4float %1 Sqrt %17 +; CHECK-NOT: OpStore %dv OpStore %dv %18 %19 = OpLoad %v4float %v OpStore %gl_FragColor %19 @@ -94,21 +84,7 @@ OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%main = OpFunction %void None %9 -%15 = OpLabel -%v = OpVariable %_ptr_Function_v4float Function -%16 = OpLoad %v4float %BaseColor -OpStore %v %16 -%19 = OpLoad %v4float %v -OpStore %gl_FragColor %19 -OpReturn -OpFunctionEnd -)"; - - SinglePassRunAndCheck<AggressiveDCEPass>( - predefs1 + names_before + predefs2 + func_before, - predefs1 + names_after + predefs2 + func_after, true, true); + SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true); } TEST_F(AggressiveDCETest, NoEliminateFrexp) { @@ -242,35 +218,23 @@ TEST_F(AggressiveDCETest, EliminateDecorate) { // gl_FragColor = v; // } - const std::string predefs1 = - R"(OpCapability Shader + const std::string spirv = + R"( +OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %main "main" %BaseColor %Dead %gl_FragColor OpExecutionMode %main OriginUpperLeft OpSource GLSL 140 -)"; - - const std::string names_before = - R"(OpName %main "main" +OpName %main "main" OpName %v "v" OpName %BaseColor "BaseColor" OpName %dv "dv" OpName %Dead "Dead" OpName %gl_FragColor "gl_FragColor" +; CHECK-NOT: OpDecorate OpDecorate %8 RelaxedPrecision -)"; - - const std::string names_after = - R"(OpName %main "main" -OpName %v "v" -OpName %BaseColor "BaseColor" -OpName %Dead "Dead" -OpName %gl_FragColor "gl_FragColor" -)"; - - const std::string predefs2_before = - R"(%void = OpTypeVoid +%void = OpTypeVoid %10 = OpTypeFunction %void %float = OpTypeFloat 32 %v4float = OpTypeVector %float 4 @@ -281,29 +245,14 @@ OpName %gl_FragColor "gl_FragColor" %float_0_5 = OpConstant %float 0.5 %_ptr_Output_v4float = OpTypePointer Output %v4float %gl_FragColor = OpVariable %_ptr_Output_v4float Output -)"; - - const std::string predefs2_after = - R"(%void = OpTypeVoid -%10 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v4float = OpTypeVector %float 4 -%_ptr_Function_v4float = OpTypePointer Function %v4float -%_ptr_Input_v4float = OpTypePointer Input %v4float -%BaseColor = OpVariable %_ptr_Input_v4float Input -%Dead = OpVariable %_ptr_Input_v4float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%gl_FragColor = OpVariable %_ptr_Output_v4float Output -)"; - - const std::string func_before = - R"(%main = OpFunction %void None %10 +%main = OpFunction %void None %10 %17 = OpLabel %v = OpVariable %_ptr_Function_v4float Function %dv = OpVariable %_ptr_Function_v4float Function %18 = OpLoad %v4float %BaseColor OpStore %v %18 %19 = OpLoad %v4float %Dead +; CHECK-NOT: OpVectorTimesScalar %8 = OpVectorTimesScalar %v4float %19 %float_0_5 OpStore %dv %8 %20 = OpLoad %v4float %v @@ -312,21 +261,7 @@ OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%main = OpFunction %void None %10 -%17 = OpLabel -%v = OpVariable %_ptr_Function_v4float Function -%18 = OpLoad %v4float %BaseColor -OpStore %v %18 -%20 = OpLoad %v4float %v -OpStore %gl_FragColor %20 -OpReturn -OpFunctionEnd -)"; - - SinglePassRunAndCheck<AggressiveDCEPass>( - predefs1 + names_before + predefs2_before + func_before, - predefs1 + names_after + predefs2_after + func_after, true, true); + SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true); } TEST_F(AggressiveDCETest, Simple) { @@ -342,53 +277,44 @@ TEST_F(AggressiveDCETest, Simple) { // gl_FragColor = v; // } - const std::string predefs1 = - R"(OpCapability Shader + const std::string spirv = + R"( +OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 +; CHECK: OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor OpEntryPoint Fragment %main "main" %BaseColor %Dead %gl_FragColor OpExecutionMode %main OriginUpperLeft OpSource GLSL 140 -)"; - - const std::string names_before = - R"(OpName %main "main" +OpName %main "main" OpName %v "v" OpName %BaseColor "BaseColor" +; CHECK-NOT: OpName %dv "dv" OpName %dv "dv" +; CHECK-NOT: OpName %Dead "Dead" OpName %Dead "Dead" OpName %gl_FragColor "gl_FragColor" -)"; - - const std::string names_after = - R"(OpName %main "main" -OpName %v "v" -OpName %BaseColor "BaseColor" -OpName %Dead "Dead" -OpName %gl_FragColor "gl_FragColor" -)"; - - const std::string predefs2 = - R"(%void = OpTypeVoid +%void = OpTypeVoid %9 = OpTypeFunction %void %float = OpTypeFloat 32 %v4float = OpTypeVector %float 4 %_ptr_Function_v4float = OpTypePointer Function %v4float %_ptr_Input_v4float = OpTypePointer Input %v4float %BaseColor = OpVariable %_ptr_Input_v4float Input +; CHECK-NOT: %Dead = OpVariable %Dead = OpVariable %_ptr_Input_v4float Input %_ptr_Output_v4float = OpTypePointer Output %v4float %gl_FragColor = OpVariable %_ptr_Output_v4float Output -)"; - - const std::string func_before = - R"(%main = OpFunction %void None %9 +%main = OpFunction %void None %9 %15 = OpLabel %v = OpVariable %_ptr_Function_v4float Function +; CHECK-NOT: %dv = OpVariable %dv = OpVariable %_ptr_Function_v4float Function %16 = OpLoad %v4float %BaseColor OpStore %v %16 +; CHECK-NOT: OpLoad %v4float %Dead %17 = OpLoad %v4float %Dead +; CHECK-NOT: OpStore %dv OpStore %dv %17 %18 = OpLoad %v4float %v OpStore %gl_FragColor %18 @@ -396,21 +322,7 @@ OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%main = OpFunction %void None %9 -%15 = OpLabel -%v = OpVariable %_ptr_Function_v4float Function -%16 = OpLoad %v4float %BaseColor -OpStore %v %16 -%18 = OpLoad %v4float %v -OpStore %gl_FragColor %18 -OpReturn -OpFunctionEnd -)"; - - SinglePassRunAndCheck<AggressiveDCEPass>( - predefs1 + names_before + predefs2 + func_before, - predefs1 + names_after + predefs2 + func_after, true, true); + SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true); } TEST_F(AggressiveDCETest, OptAllowListExtension) { @@ -426,35 +338,22 @@ TEST_F(AggressiveDCETest, OptAllowListExtension) { // gl_FragColor = v; // } - const std::string predefs1 = + const std::string spirv = R"(OpCapability Shader OpExtension "SPV_AMD_gpu_shader_int16" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 +; CHECK: OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor OpEntryPoint Fragment %main "main" %BaseColor %Dead %gl_FragColor OpExecutionMode %main OriginUpperLeft OpSource GLSL 140 -)"; - - const std::string names_before = - R"(OpName %main "main" +OpName %main "main" OpName %v "v" OpName %BaseColor "BaseColor" OpName %dv "dv" OpName %Dead "Dead" OpName %gl_FragColor "gl_FragColor" -)"; - - const std::string names_after = - R"(OpName %main "main" -OpName %v "v" -OpName %BaseColor "BaseColor" -OpName %Dead "Dead" -OpName %gl_FragColor "gl_FragColor" -)"; - - const std::string predefs2 = - R"(%void = OpTypeVoid +%void = OpTypeVoid %9 = OpTypeFunction %void %float = OpTypeFloat 32 %v4float = OpTypeVector %float 4 @@ -464,10 +363,7 @@ OpName %gl_FragColor "gl_FragColor" %Dead = OpVariable %_ptr_Input_v4float Input %_ptr_Output_v4float = OpTypePointer Output %v4float %gl_FragColor = OpVariable %_ptr_Output_v4float Output -)"; - - const std::string func_before = - R"(%main = OpFunction %void None %9 +%main = OpFunction %void None %9 %15 = OpLabel %v = OpVariable %_ptr_Function_v4float Function %dv = OpVariable %_ptr_Function_v4float Function @@ -481,21 +377,7 @@ OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%main = OpFunction %void None %9 -%15 = OpLabel -%v = OpVariable %_ptr_Function_v4float Function -%16 = OpLoad %v4float %BaseColor -OpStore %v %16 -%18 = OpLoad %v4float %v -OpStore %gl_FragColor %18 -OpReturn -OpFunctionEnd -)"; - - SinglePassRunAndCheck<AggressiveDCEPass>( - predefs1 + names_before + predefs2 + func_before, - predefs1 + names_after + predefs2 + func_after, true, true); + SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true); } TEST_F(AggressiveDCETest, NoOptDenyListExtension) { @@ -571,7 +453,7 @@ TEST_F(AggressiveDCETest, ElimWithCall) { // gl_FragColor = vec4(0.0); // } - const std::string defs_before = + const std::string text = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 @@ -600,54 +482,25 @@ OpName %gl_FragColor "gl_FragColor" %gl_FragColor = OpVariable %_ptr_Output_v4float Output %float_0 = OpConstant %float 0 %20 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 -)"; - - const std::string defs_after = - R"(OpCapability Shader -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %i1 %i2 %gl_FragColor -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 140 -OpName %main "main" -OpName %nothing_vf4_ "nothing(vf4;" -OpName %v "v" -OpName %v1 "v1" -OpName %i1 "i1" -OpName %i2 "i2" -OpName %param "param" -OpName %gl_FragColor "gl_FragColor" -%void = OpTypeVoid -%12 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v4float = OpTypeVector %float 4 -%_ptr_Function_v4float = OpTypePointer Function %v4float -%16 = OpTypeFunction %void %_ptr_Function_v4float -%_ptr_Input_v4float = OpTypePointer Input %v4float -%i1 = OpVariable %_ptr_Input_v4float Input -%i2 = OpVariable %_ptr_Input_v4float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%gl_FragColor = OpVariable %_ptr_Output_v4float Output -%float_0 = OpConstant %float 0 -%20 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 -)"; - - const std::string func_before = - R"(%main = OpFunction %void None %12 +%main = OpFunction %void None %12 %21 = OpLabel %v1 = OpVariable %_ptr_Function_v4float Function %v2 = OpVariable %_ptr_Function_v4float Function %param = OpVariable %_ptr_Function_v4float Function %22 = OpLoad %v4float %i1 OpStore %v1 %22 +; CHECK-NOT: OpLoad %v4float %i2 %23 = OpLoad %v4float %i2 +; CHECK-NOT: OpStore %v2 OpStore %v2 %23 %24 = OpLoad %v4float %v1 OpStore %param %24 +; CHECK: OpFunctionCall %void %nothing_vf4_ %25 = OpFunctionCall %void %nothing_vf4_ %param OpStore %gl_FragColor %20 OpReturn OpFunctionEnd +; CHECK: %nothing_vf4_ = OpFunction %nothing_vf4_ = OpFunction %void None %16 %v = OpFunctionParameter %_ptr_Function_v4float %26 = OpLabel @@ -655,28 +508,7 @@ OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%main = OpFunction %void None %12 -%21 = OpLabel -%v1 = OpVariable %_ptr_Function_v4float Function -%param = OpVariable %_ptr_Function_v4float Function -%22 = OpLoad %v4float %i1 -OpStore %v1 %22 -%24 = OpLoad %v4float %v1 -OpStore %param %24 -%25 = OpFunctionCall %void %nothing_vf4_ %param -OpStore %gl_FragColor %20 -OpReturn -OpFunctionEnd -%nothing_vf4_ = OpFunction %void None %16 -%v = OpFunctionParameter %_ptr_Function_v4float -%26 = OpLabel -OpReturn -OpFunctionEnd -)"; - - SinglePassRunAndCheck<AggressiveDCEPass>(defs_before + func_before, - defs_after + func_after, true, true); + SinglePassRunAndMatch<AggressiveDCEPass>(text, true); } TEST_F(AggressiveDCETest, NoParamElim) { @@ -998,7 +830,7 @@ TEST_F(AggressiveDCETest, PrivateStoreElimInEntryNoCalls) { // OutColor = v; // } - const std::string predefs_before = + const std::string spirv = R"(OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 @@ -1008,6 +840,7 @@ OpSource GLSL 450 OpName %main "main" OpName %v "v" OpName %BaseColor "BaseColor" +; CHECK-NOT: OpName %dv "dv" OpName %dv "dv" OpName %Dead "Dead" OpName %OutColor "OutColor" @@ -1019,49 +852,22 @@ OpDecorate %OutColor Location 0 %float = OpTypeFloat 32 %v4float = OpTypeVector %float 4 %_ptr_Function_v4float = OpTypePointer Function %v4float +; CHECK-NOT: OpTypePointer Private %_ptr_Private_v4float = OpTypePointer Private %v4float %_ptr_Input_v4float = OpTypePointer Input %v4float %BaseColor = OpVariable %_ptr_Input_v4float Input %Dead = OpVariable %_ptr_Input_v4float Input %_ptr_Output_v4float = OpTypePointer Output %v4float +; CHECK-NOT: %dv = OpVariable %dv = OpVariable %_ptr_Private_v4float Private %OutColor = OpVariable %_ptr_Output_v4float Output -)"; - - const std::string predefs_after = - R"(OpCapability Shader -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %BaseColor %Dead %OutColor -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 450 -OpName %main "main" -OpName %v "v" -OpName %BaseColor "BaseColor" -OpName %Dead "Dead" -OpName %OutColor "OutColor" -OpDecorate %BaseColor Location 0 -OpDecorate %Dead Location 1 -OpDecorate %OutColor Location 0 -%void = OpTypeVoid -%9 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v4float = OpTypeVector %float 4 -%_ptr_Function_v4float = OpTypePointer Function %v4float -%_ptr_Input_v4float = OpTypePointer Input %v4float -%BaseColor = OpVariable %_ptr_Input_v4float Input -%Dead = OpVariable %_ptr_Input_v4float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%OutColor = OpVariable %_ptr_Output_v4float Output -)"; - - const std::string main_before = - R"(%main = OpFunction %void None %9 +%main = OpFunction %void None %9 %16 = OpLabel %v = OpVariable %_ptr_Function_v4float Function %17 = OpLoad %v4float %BaseColor OpStore %v %17 %18 = OpLoad %v4float %Dead +; CHECK-NOT: OpStore %dv OpStore %dv %18 %19 = OpLoad %v4float %v %20 = OpFNegate %v4float %19 @@ -1070,21 +876,7 @@ OpReturn OpFunctionEnd )"; - const std::string main_after = - R"(%main = OpFunction %void None %9 -%16 = OpLabel -%v = OpVariable %_ptr_Function_v4float Function -%17 = OpLoad %v4float %BaseColor -OpStore %v %17 -%19 = OpLoad %v4float %v -%20 = OpFNegate %v4float %19 -OpStore %OutColor %20 -OpReturn -OpFunctionEnd -)"; - - SinglePassRunAndCheck<AggressiveDCEPass>( - predefs_before + main_before, predefs_after + main_after, true, true); + SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true); } TEST_F(AggressiveDCETest, NoPrivateStoreElimIfLoad) { @@ -1288,7 +1080,7 @@ TEST_F(AggressiveDCETest, WorkgroupStoreElimInEntryNoCalls) { // OutColor = v; // } - const std::string predefs_before = + const std::string spirv = R"(OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 @@ -1298,6 +1090,7 @@ OpSource GLSL 450 OpName %main "main" OpName %v "v" OpName %BaseColor "BaseColor" +; CHECK-NOT: OpName %dv "dv" OpName %dv "dv" OpName %Dead "Dead" OpName %OutColor "OutColor" @@ -1309,49 +1102,22 @@ OpDecorate %OutColor Location 0 %float = OpTypeFloat 32 %v4float = OpTypeVector %float 4 %_ptr_Function_v4float = OpTypePointer Function %v4float +; CHECK-NOT: OpTypePointer Workgroup %_ptr_Workgroup_v4float = OpTypePointer Workgroup %v4float %_ptr_Input_v4float = OpTypePointer Input %v4float %BaseColor = OpVariable %_ptr_Input_v4float Input %Dead = OpVariable %_ptr_Input_v4float Input %_ptr_Output_v4float = OpTypePointer Output %v4float +; CHECK-NOT: %dv = OpVariable %dv = OpVariable %_ptr_Workgroup_v4float Workgroup %OutColor = OpVariable %_ptr_Output_v4float Output -)"; - - const std::string predefs_after = - R"(OpCapability Shader -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %BaseColor %Dead %OutColor -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 450 -OpName %main "main" -OpName %v "v" -OpName %BaseColor "BaseColor" -OpName %Dead "Dead" -OpName %OutColor "OutColor" -OpDecorate %BaseColor Location 0 -OpDecorate %Dead Location 1 -OpDecorate %OutColor Location 0 -%void = OpTypeVoid -%9 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v4float = OpTypeVector %float 4 -%_ptr_Function_v4float = OpTypePointer Function %v4float -%_ptr_Input_v4float = OpTypePointer Input %v4float -%BaseColor = OpVariable %_ptr_Input_v4float Input -%Dead = OpVariable %_ptr_Input_v4float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%OutColor = OpVariable %_ptr_Output_v4float Output -)"; - - const std::string main_before = - R"(%main = OpFunction %void None %9 +%main = OpFunction %void None %9 %16 = OpLabel %v = OpVariable %_ptr_Function_v4float Function %17 = OpLoad %v4float %BaseColor OpStore %v %17 %18 = OpLoad %v4float %Dead +; CHECK-NOT: OpStore %dv OpStore %dv %18 %19 = OpLoad %v4float %v %20 = OpFNegate %v4float %19 @@ -1360,21 +1126,7 @@ OpReturn OpFunctionEnd )"; - const std::string main_after = - R"(%main = OpFunction %void None %9 -%16 = OpLabel -%v = OpVariable %_ptr_Function_v4float Function -%17 = OpLoad %v4float %BaseColor -OpStore %v %17 -%19 = OpLoad %v4float %v -%20 = OpFNegate %v4float %19 -OpStore %OutColor %20 -OpReturn -OpFunctionEnd -)"; - - SinglePassRunAndCheck<AggressiveDCEPass>( - predefs_before + main_before, predefs_after + main_after, true, true); + SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true); } TEST_F(AggressiveDCETest, EliminateDeadIfThenElse) { @@ -1393,7 +1145,7 @@ TEST_F(AggressiveDCETest, EliminateDeadIfThenElse) { // OutColor = vec4(1.0,1.0,1.0,1.0); // } - const std::string predefs_before = + const std::string spirv = R"(OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 @@ -1424,34 +1176,11 @@ OpDecorate %OutColor Location 0 %OutColor = OpVariable %_ptr_Output_v4float Output %float_1 = OpConstant %float 1 %21 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 -)"; - - const std::string predefs_after = - R"(OpCapability Shader -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %BaseColor %OutColor -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 450 -OpName %main "main" -OpName %BaseColor "BaseColor" -OpName %OutColor "OutColor" -OpDecorate %BaseColor Location 0 -OpDecorate %OutColor Location 0 -%void = OpTypeVoid -%7 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v4float = OpTypeVector %float 4 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%BaseColor = OpVariable %_ptr_Input_v4float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%OutColor = OpVariable %_ptr_Output_v4float Output -%float_1 = OpConstant %float 1 -%21 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 -)"; - - const std::string func_before = - R"(%main = OpFunction %void None %7 +; CHECK: = OpFunction %void +; CHECK-NEXT: %22 = OpLabel +; CHECK-NEXT: OpBranch %26 +; CHECK-NEXT: %26 = OpLabel +%main = OpFunction %void None %7 %22 = OpLabel %d = OpVariable %_ptr_Function_float Function %23 = OpAccessChain %_ptr_Input_float %BaseColor %uint_0 @@ -1475,18 +1204,7 @@ OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%main = OpFunction %void None %7 -%22 = OpLabel -OpBranch %26 -%26 = OpLabel -OpStore %OutColor %21 -OpReturn -OpFunctionEnd -)"; - - SinglePassRunAndCheck<AggressiveDCEPass>( - predefs_before + func_before, predefs_after + func_after, true, true); + SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true); } TEST_F(AggressiveDCETest, EliminateDeadIfThen) { @@ -1503,7 +1221,7 @@ TEST_F(AggressiveDCETest, EliminateDeadIfThen) { // OutColor = vec4(1.0,1.0,1.0,1.0); // } - const std::string predefs_before = + const std::string spirv = R"(OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 @@ -1533,34 +1251,11 @@ OpDecorate %OutColor Location 0 %OutColor = OpVariable %_ptr_Output_v4float Output %float_1 = OpConstant %float 1 %20 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 -)"; - - const std::string predefs_after = - R"(OpCapability Shader -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %BaseColor %OutColor -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 450 -OpName %main "main" -OpName %BaseColor "BaseColor" -OpName %OutColor "OutColor" -OpDecorate %BaseColor Location 0 -OpDecorate %OutColor Location 0 -%void = OpTypeVoid -%7 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v4float = OpTypeVector %float 4 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%BaseColor = OpVariable %_ptr_Input_v4float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%OutColor = OpVariable %_ptr_Output_v4float Output -%float_1 = OpConstant %float 1 -%20 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 -)"; - - const std::string func_before = - R"(%main = OpFunction %void None %7 +; CHECK: = OpFunction +; CHECK-NEXT: %21 = OpLabel +; CHECK-NEXT: OpBranch [[target:%\w+]] +; CHECK-NEXT: [[target]] = OpLabel +%main = OpFunction %void None %7 %21 = OpLabel %d = OpVariable %_ptr_Function_float Function %22 = OpAccessChain %_ptr_Input_float %BaseColor %uint_0 @@ -1579,18 +1274,7 @@ OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%main = OpFunction %void None %7 -%21 = OpLabel -OpBranch %25 -%25 = OpLabel -OpStore %OutColor %20 -OpReturn -OpFunctionEnd -)"; - - SinglePassRunAndCheck<AggressiveDCEPass>( - predefs_before + func_before, predefs_after + func_after, true, true); + SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true); } TEST_F(AggressiveDCETest, EliminateDeadSwitch) { @@ -1609,7 +1293,7 @@ TEST_F(AggressiveDCETest, EliminateDeadSwitch) { // } // OutColor = vec4(1.0,1.0,1.0,1.0); // } - const std::string before = + const std::string spirv = R"(OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 @@ -1642,6 +1326,10 @@ TEST_F(AggressiveDCETest, EliminateDeadSwitch) { %OutColor = OpVariable %_ptr_Output_v4float Output %float_1 = OpConstant %float 1 %27 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +; CHECK: = OpFunction +; CHECK-NEXT: = OpLabel +; CHECK-NEXT: OpBranch [[target:%\w+]] +; CHECK-NEXT: [[target]] = OpLabel %main = OpFunction %void None %3 %5 = OpLabel %d = OpVariable %_ptr_Function_float Function @@ -1658,45 +1346,7 @@ TEST_F(AggressiveDCETest, EliminateDeadSwitch) { OpReturn OpFunctionEnd)"; - const std::string after = - R"(OpCapability Shader -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %x %BaseColor %OutColor -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 450 -OpName %main "main" -OpName %x "x" -OpName %BaseColor "BaseColor" -OpName %OutColor "OutColor" -OpDecorate %x Flat -OpDecorate %x Location 1 -OpDecorate %BaseColor Location 0 -OpDecorate %OutColor Location 0 -%void = OpTypeVoid -%3 = OpTypeFunction %void -%int = OpTypeInt 32 1 -%_ptr_Input_int = OpTypePointer Input %int -%x = OpVariable %_ptr_Input_int Input -%float = OpTypeFloat 32 -%v4float = OpTypeVector %float 4 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%BaseColor = OpVariable %_ptr_Input_v4float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%OutColor = OpVariable %_ptr_Output_v4float Output -%float_1 = OpConstant %float 1 -%27 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 -%main = OpFunction %void None %3 -%5 = OpLabel -OpBranch %11 -%11 = OpLabel -OpStore %OutColor %27 -OpReturn -OpFunctionEnd -)"; - - SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); - SinglePassRunAndCheck<AggressiveDCEPass>(before, after, true, true); + SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true); } TEST_F(AggressiveDCETest, EliminateDeadIfThenElseNested) { @@ -1721,7 +1371,7 @@ TEST_F(AggressiveDCETest, EliminateDeadIfThenElseNested) { // OutColor = vec4(1.0,1.0,1.0,1.0); // } - const std::string predefs_before = + const std::string spirv = R"(OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 @@ -1754,34 +1404,14 @@ OpDecorate %OutColor Location 0 %OutColor = OpVariable %_ptr_Output_v4float Output %float_1 = OpConstant %float 1 %23 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 -)"; - const std::string predefs_after = - R"(OpCapability Shader -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %BaseColor %OutColor -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 450 -OpName %main "main" -OpName %BaseColor "BaseColor" -OpName %OutColor "OutColor" -OpDecorate %BaseColor Location 0 -OpDecorate %OutColor Location 0 -%void = OpTypeVoid -%7 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v4float = OpTypeVector %float 4 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%BaseColor = OpVariable %_ptr_Input_v4float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%OutColor = OpVariable %_ptr_Output_v4float Output -%float_1 = OpConstant %float 1 -%23 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 -)"; +; CHECK: = OpFunction +; CHECK-NEXT: = OpLabel +; CHECK-NEXT: OpBranch [[target:%\w+]] +; CHECK-NEXT: [[target]] = OpLabel +; CHECK-NOT: OpLabel - const std::string func_before = - R"(%main = OpFunction %void None %7 +%main = OpFunction %void None %7 %24 = OpLabel %d = OpVariable %_ptr_Function_float Function %25 = OpAccessChain %_ptr_Input_float %BaseColor %uint_0 @@ -1823,18 +1453,7 @@ OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%main = OpFunction %void None %7 -%24 = OpLabel -OpBranch %28 -%28 = OpLabel -OpStore %OutColor %23 -OpReturn -OpFunctionEnd -)"; - - SinglePassRunAndCheck<AggressiveDCEPass>( - predefs_before + func_before, predefs_after + func_after, true, true); + SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true); } TEST_F(AggressiveDCETest, NoEliminateLiveIfThenElse) { @@ -2578,7 +2197,7 @@ TEST_F(AggressiveDCETest, EliminateEntireFunctionBody) { // d = BaseColor.z; // } - const std::string predefs_before = + const std::string spirv = R"(OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 @@ -2607,32 +2226,15 @@ OpDecorate %OutColor Location 0 %uint_2 = OpConstant %uint 2 %_ptr_Output_v4float = OpTypePointer Output %v4float %OutColor = OpVariable %_ptr_Output_v4float Output -)"; - const std::string predefs_after = - R"(OpCapability Shader -%1 = OpExtInstImport "GLSL.std.450" -OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" %BaseColor %OutColor -OpExecutionMode %main OriginUpperLeft -OpSource GLSL 450 -OpName %main "main" -OpName %BaseColor "BaseColor" -OpName %OutColor "OutColor" -OpDecorate %BaseColor Location 0 -OpDecorate %OutColor Location 0 -%void = OpTypeVoid -%7 = OpTypeFunction %void -%float = OpTypeFloat 32 -%v4float = OpTypeVector %float 4 -%_ptr_Input_v4float = OpTypePointer Input %v4float -%BaseColor = OpVariable %_ptr_Input_v4float Input -%_ptr_Output_v4float = OpTypePointer Output %v4float -%OutColor = OpVariable %_ptr_Output_v4float Output -)"; +; CHECK: = OpFunction +; CHECK-NEXT: = OpLabel +; CHECK-NEXT: OpBranch [[target:%\w+]] +; CHECK-NEXT: [[target]] = OpLabel +; CHECK-NEXT: OpReturn +; CHECK-NEXT: OpFunctionEnd - const std::string func_before = - R"(%main = OpFunction %void None %7 +%main = OpFunction %void None %7 %20 = OpLabel %d = OpVariable %_ptr_Function_float Function %21 = OpAccessChain %_ptr_Input_float %BaseColor %uint_0 @@ -2655,17 +2257,7 @@ OpReturn OpFunctionEnd )"; - const std::string func_after = - R"(%main = OpFunction %void None %7 -%20 = OpLabel -OpBranch %24 -%24 = OpLabel -OpReturn -OpFunctionEnd -)"; - - SinglePassRunAndCheck<AggressiveDCEPass>( - predefs_before + func_before, predefs_after + func_after, true, true); + SinglePassRunAndMatch<AggressiveDCEPass>(spirv, true); } TEST_F(AggressiveDCETest, EliminateUselessInnerLoop) { @@ -4731,7 +4323,7 @@ INSTANTIATE_TEST_SUITE_P( }, // Uint vector type spec constants. One vector has all component dead, - // another vector has one dead unsigend integer and one used unsigned + // another vector has one dead unsigned integer and one used unsigned // integer. { /* .used_consts = */ @@ -5521,10 +5113,9 @@ OpCapability ImageBuffer OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 -OpEntryPoint GLCompute %2 "min" %gl_GlobalInvocationID +OpEntryPoint GLCompute %2 "min" OpExecutionMode %2 LocalSize 64 1 1 OpSource HLSL 600 -OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId OpDecorate %4 DescriptorSet 4 OpDecorate %4 Binding 70 %uint = OpTypeInt 32 0 @@ -5535,12 +5126,9 @@ OpDecorate %4 Binding 70 %10 = OpTypeFunction %void %uint_0 = OpConstant %uint 0 %uint_1 = OpConstant %uint 1 -%v3uint = OpTypeVector %uint 3 -%_ptr_Input_v3uint = OpTypePointer Input %v3uint %_ptr_Image_uint = OpTypePointer Image %uint %4 = OpVariable %_ptr_UniformConstant_6 UniformConstant %16 = OpVariable %_ptr_Private_6 Private -%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input %2 = OpFunction %void None %10 %17 = OpLabel %18 = OpLoad %6 %4 @@ -6393,8 +5981,8 @@ OpFunctionEnd TEST_F(AggressiveDCETest, DeadInputInterfaceV13) { const std::string spirv = R"( -; CHECK: OpEntryPoint GLCompute %main "main" [[var:%\w+]] -; CHECK: [[var]] = OpVariable +; CHECK: OpEntryPoint GLCompute %main "main" +; CHECK-NOT: OpVariable OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" %dead @@ -6417,8 +6005,8 @@ OpFunctionEnd TEST_F(AggressiveDCETest, DeadInputInterfaceV14) { const std::string spirv = R"( -; CHECK: OpEntryPoint GLCompute %main "main" [[var:%\w+]] -; CHECK: [[var]] = OpVariable +; CHECK: OpEntryPoint GLCompute %main "main" +; CHECK-NOT: OpVariable OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" %dead diff --git a/test/opt/assembly_builder.h b/test/opt/assembly_builder.h index 1673c092..b94e90f5 100644 --- a/test/opt/assembly_builder.h +++ b/test/opt/assembly_builder.h @@ -70,7 +70,7 @@ class AssemblyBuilder { static const uint32_t SPEC_ID_BASE = 200; public: - // Initalize a minimal SPIR-V assembly code as the template. The minimal + // Initialize a minimal SPIR-V assembly code as the template. The minimal // module contains an empty main function and some predefined names for the // main function. AssemblyBuilder() @@ -102,7 +102,7 @@ class AssemblyBuilder { }); } - // Appends OpName instructions to this builder. Instrcution strings that do + // Appends OpName instructions to this builder. Instruction strings that do // not start with 'OpName ' will be skipped. Returns the references of this // assembly builder. AssemblyBuilder& AppendNames(const std::vector<std::string>& vec_asm_code) { diff --git a/test/opt/block_merge_test.cpp b/test/opt/block_merge_test.cpp index 6903c4e7..9698fed2 100644 --- a/test/opt/block_merge_test.cpp +++ b/test/opt/block_merge_test.cpp @@ -884,7 +884,7 @@ TEST_F(BlockMergeTest, MergeHeaders) { ; CHECK-NEXT: [[header]] = OpLabel ; CHECK-NEXT: OpSelectionMerge [[merge:%\w+]] ; CHECK: [[merge]] = OpLabel -; CHEKC: OpReturn +; CHECK: OpReturn OpCapability Shader OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %func "func" diff --git a/test/opt/ccp_test.cpp b/test/opt/ccp_test.cpp index 212e0514..f0f24362 100644 --- a/test/opt/ccp_test.cpp +++ b/test/opt/ccp_test.cpp @@ -582,6 +582,35 @@ TEST_F(CCPTest, SkipSpecConstantInstrucitons) { EXPECT_EQ(std::get<1>(res), Pass::Status::SuccessWithoutChange); } +TEST_F(CCPTest, FoldConstantCompositeInstrucitonsWithSpecConst) { + const std::string spv_asm = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + %void = OpTypeVoid + %4 = OpTypeFunction %void + %bool = OpTypeBool + %v3bool = OpTypeVector %bool 3 + %_struct_8 = OpTypeStruct %v3bool + %true = OpConstantTrue %bool +; CHECK: [[spec_const:%\w+]] = OpSpecConstantComposite %v3bool + %11 = OpSpecConstantComposite %v3bool %true %true %true + %12 = OpConstantComposite %_struct_8 %11 +; CHECK: OpFunction + %1 = OpFunction %void None %4 + %29 = OpLabel + %31 = OpCompositeExtract %v3bool %12 0 +; CHECK: OpCompositeExtract %bool [[spec_const]] 0 + %32 = OpCompositeExtract %bool %31 0 + OpReturn + OpFunctionEnd + )"; + + auto result = SinglePassRunAndMatch<CCPPass>(spv_asm, true); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + TEST_F(CCPTest, UpdateSubsequentPhisToVarying) { const std::string text = R"( OpCapability Shader @@ -1234,6 +1263,86 @@ OpFunctionEnd SinglePassRunAndCheck<CCPPass>(text, text, false); } + +// Test from https://github.com/KhronosGroup/SPIRV-Tools/issues/4462. +// The test was causing a lateral movement in the constant lattice, which was +// not being detected as varying by CCP. In this test, FClamp is evaluated +// twice. On the first evaluation, if computes FClamp(0.5, 0.5, -1) which +// returns -1. On the second evaluation, it computes FClamp(0.5, 0.5, VARYING) +// which returns 0.5. +// +// Both fold() computations are correct given the semantics of FClamp() but +// this causes a lateral transition in the constant lattice which was not being +// considered VARYING by CCP. +TEST_F(CCPTest, LateralLatticeTransition) { + const std::string text = R"(OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %gl_FragCoord %outColor + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 310 + OpName %main "main" + OpName %gl_FragCoord "gl_FragCoord" + OpName %outColor "outColor" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %outColor Location 0 + %void = OpTypeVoid + %6 = OpTypeFunction %void + %float = OpTypeFloat 32 + %float_0_5 = OpConstant %float 0.5 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Input_float = OpTypePointer Input %float + %float_0 = OpConstant %float 0 + %bool = OpTypeBool + %float_n1 = OpConstant %float -1 + %float_1 = OpConstant %float 1 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %outColor = OpVariable %_ptr_Output_v4float Output + +; This constant is created during the first evaluation of the CompositeConstruct +; CHECK: [[new_constant:%\d+]] = OpConstantComposite %v4float %float_n1 %float_0_5 %float_0 %float_1 + + %main = OpFunction %void None %6 + %19 = OpLabel + %20 = OpAccessChain %_ptr_Input_float %gl_FragCoord %uint_0 + %21 = OpLoad %float %20 + %22 = OpFOrdLessThan %bool %21 %float_0 + OpSelectionMerge %23 None + OpBranchConditional %22 %24 %25 + %24 = OpLabel + OpBranch %23 + %25 = OpLabel + OpBranch %26 + %26 = OpLabel + OpBranch %23 + %23 = OpLabel + %27 = OpPhi %float %float_n1 %24 %float_0_5 %26 + %28 = OpExtInst %float %1 FClamp %float_0_5 %float_0_5 %27 + + ; On first evaluation, the result from FClamp will return 0.5. + ; But on second evaluation, FClamp should return VARYING. Check + ; that CCP is not keeping the first result. + ; CHECK-NOT: %29 = OpCompositeConstruct %v4float %float_0_5 %float_0_5 %float_0 %float_1 + %29 = OpCompositeConstruct %v4float %28 %float_0_5 %float_0 %float_1 + + ; CHECK-NOT: OpCopyObject %v4float [[new_constant]] + %42 = OpCopyObject %v4float %29 + + ; CHECK-NOT: OpStore %outColor [[new_constant]] + OpStore %outColor %42 + + OpReturn + OpFunctionEnd +)"; + + auto result = SinglePassRunAndMatch<CCPPass>(text, true); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/test/opt/code_sink_test.cpp b/test/opt/code_sink_test.cpp index f1bd1275..bf5029b6 100644 --- a/test/opt/code_sink_test.cpp +++ b/test/opt/code_sink_test.cpp @@ -378,7 +378,7 @@ TEST_F(CodeSinkTest, MoveReadOnlyLoadWithSync) { %uint = OpTypeInt 32 0 %uint_0 = OpConstant %uint 0 %uint_4 = OpConstant %uint 4 -%mem_semantics = OpConstant %uint 0x42 ; Uniform memeory arquire +%mem_semantics = OpConstant %uint 0x42 ; Uniform memory arquire %_arr_uint_uint_4 = OpTypeArray %uint %uint_4 %_ptr_Uniform_uint = OpTypePointer Uniform %uint %_ptr_Uniform__arr_uint_uint_4 = OpTypePointer Uniform %_arr_uint_uint_4 @@ -419,7 +419,7 @@ TEST_F(CodeSinkTest, DontMoveBecauseOfSync) { %uint = OpTypeInt 32 0 %uint_0 = OpConstant %uint 0 %uint_4 = OpConstant %uint 4 -%mem_semantics = OpConstant %uint 0x42 ; Uniform memeory arquire +%mem_semantics = OpConstant %uint 0x42 ; Uniform memory arquire %_arr_uint_uint_4 = OpTypeStruct %uint %_ptr_Uniform_uint = OpTypePointer Uniform %uint %_ptr_Uniform__arr_uint_uint_4 = OpTypePointer Uniform %_arr_uint_uint_4 @@ -460,7 +460,7 @@ TEST_F(CodeSinkTest, DontMoveBecauseOfAtomicWithSync) { %uint = OpTypeInt 32 0 %uint_0 = OpConstant %uint 0 %uint_4 = OpConstant %uint 4 -%mem_semantics = OpConstant %uint 0x42 ; Uniform memeory arquire +%mem_semantics = OpConstant %uint 0x42 ; Uniform memory arquire %_arr_uint_uint_4 = OpTypeStruct %uint %_ptr_Uniform_uint = OpTypePointer Uniform %uint %_ptr_Uniform__arr_uint_uint_4 = OpTypePointer Uniform %_arr_uint_uint_4 diff --git a/test/opt/compact_ids_test.cpp b/test/opt/compact_ids_test.cpp index ba31d84b..7c232fe4 100644 --- a/test/opt/compact_ids_test.cpp +++ b/test/opt/compact_ids_test.cpp @@ -310,6 +310,41 @@ OpFunctionEnd EXPECT_THAT(disassembly, ::testing::Eq(expected)); } +TEST(CompactIds, ResetIdBound) { + const std::string input(R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "main" +OpExecutionMode %1 OriginUpperLeft +%void = OpTypeVoid +%3 = OpTypeFunction %void +%1 = OpFunction %void None %3 +%4 = OpLabel +OpReturn +OpFunctionEnd +)"); + + spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_1); + std::unique_ptr<IRContext> context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, input, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(context, nullptr); + + CompactIdsPass compact_id_pass; + context->module()->SetIdBound(20000); + const auto status = compact_id_pass.Run(context.get()); + EXPECT_EQ(status, Pass::Status::SuccessWithChange); + EXPECT_EQ(context->module()->id_bound(), 5); + + // Test output just in case + std::vector<uint32_t> binary; + context->module()->ToBinary(&binary, false); + std::string disassembly; + tools.Disassemble(binary, &disassembly, + SpirvTools::kDefaultDisassembleOption); + + EXPECT_THAT(disassembly, ::testing::Eq(input)); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/test/opt/dead_branch_elim_test.cpp b/test/opt/dead_branch_elim_test.cpp index 9c1ef2a3..b04c8f5e 100644 --- a/test/opt/dead_branch_elim_test.cpp +++ b/test/opt/dead_branch_elim_test.cpp @@ -1613,7 +1613,7 @@ OpBranch %7 %11 = OpLogicalOr %bool %true %false OpBranch %7 %7 = OpLabel -; This phi is in a block preceeding the merge %14! +; This phi is in a block preceding the merge %14! %8 = OpPhi %bool %10 %5 %11 %6 OpBranch %14 %14 = OpLabel diff --git a/test/opt/decoration_manager_test.cpp b/test/opt/decoration_manager_test.cpp index fcfbff06..c9fabe78 100644 --- a/test/opt/decoration_manager_test.cpp +++ b/test/opt/decoration_manager_test.cpp @@ -118,7 +118,7 @@ class DecorationManagerTest : public ::testing::Test { TEST_F(DecorationManagerTest, ComparingDecorationsWithDiffOpcodesDecorateDecorateId) { IRContext ir_context(SPV_ENV_UNIVERSAL_1_2, GetConsumer()); - // This parameter can be interprated both as { SpvDecorationConstant } + // This parameter can be interpreted both as { SpvDecorationConstant } // and also as a list of IDs: { 22 } const std::vector<uint32_t> param{SpvDecorationConstant}; // OpDecorate %1 Constant @@ -137,7 +137,7 @@ TEST_F(DecorationManagerTest, TEST_F(DecorationManagerTest, ComparingDecorationsWithDiffOpcodesDecorateDecorateString) { IRContext ir_context(SPV_ENV_UNIVERSAL_1_2, GetConsumer()); - // This parameter can be interprated both as { SpvDecorationConstant } + // This parameter can be interpreted both as { SpvDecorationConstant } // and also as a null-terminated string with a single character with value 22. const std::vector<uint32_t> param{SpvDecorationConstant}; // OpDecorate %1 Constant diff --git a/test/opt/def_use_test.cpp b/test/opt/def_use_test.cpp index cfdad74a..48a485e8 100644 --- a/test/opt/def_use_test.cpp +++ b/test/opt/def_use_test.cpp @@ -950,6 +950,369 @@ INSTANTIATE_TEST_SUITE_P( ); // clang-format on +// We re-use the same replace usecases, we need to similarly exercise the +// DefUseManager by replacing instructions and uses. +using CompactIdempotence = ::testing::TestWithParam<ReplaceUseCase>; + +TEST_P(CompactIdempotence, Case) { + const auto& tc = GetParam(); + + // Build module. + const std::vector<const char*> text = {tc.before}; + std::unique_ptr<IRContext> context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, JoinAllInsts(text), + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(nullptr, context); + + // Force a re-build of def-use manager. + context->InvalidateAnalyses(IRContext::Analysis::kAnalysisDefUse); + (void)context->get_def_use_mgr(); + + // Do the substitution. + for (const auto& candidate : tc.candidates) { + context->ReplaceAllUsesWith(candidate.first, candidate.second); + } + + // Ensure new/uncompacted managers produce the same result + EXPECT_TRUE(CompareAndPrintDifferences(*context->get_def_use_mgr(), + DefUseManager(context->module()))); + + EXPECT_EQ(tc.after, DisassembleModule(context->module())); + CheckDef(tc.du, context->get_def_use_mgr()->id_to_defs()); + CheckUse(tc.du, context->get_def_use_mgr(), context->module()->IdBound()); + + // Compare again after compacting the defuse manager's storage + context->get_def_use_mgr()->CompactStorage(); + + CheckDef(tc.du, context->get_def_use_mgr()->id_to_defs()); + CheckUse(tc.du, context->get_def_use_mgr(), context->module()->IdBound()); + + EXPECT_TRUE(CompareAndPrintDifferences(*context->get_def_use_mgr(), + DefUseManager(context->module()))); +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P( + TestCase, CompactIdempotence, + ::testing::ValuesIn(std::vector<ReplaceUseCase>{ + { // no use, no replace request + "", {}, "", {}, + }, + { // replace one use + "%1 = OpTypeBool " + "%2 = OpTypeVector %1 3 " + "%3 = OpTypeInt 32 0 ", + {{1, 3}}, + "%1 = OpTypeBool\n" + "%2 = OpTypeVector %3 3\n" + "%3 = OpTypeInt 32 0", + { + { // defs + {1, "%1 = OpTypeBool"}, + {2, "%2 = OpTypeVector %3 3"}, + {3, "%3 = OpTypeInt 32 0"}, + }, + { // uses + {3, {"%2 = OpTypeVector %3 3"}}, + }, + }, + }, + { // replace and then replace back + "%1 = OpTypeBool " + "%2 = OpTypeVector %1 3 " + "%3 = OpTypeInt 32 0", + {{1, 3}, {3, 1}}, + "%1 = OpTypeBool\n" + "%2 = OpTypeVector %1 3\n" + "%3 = OpTypeInt 32 0", + { + { // defs + {1, "%1 = OpTypeBool"}, + {2, "%2 = OpTypeVector %1 3"}, + {3, "%3 = OpTypeInt 32 0"}, + }, + { // uses + {1, {"%2 = OpTypeVector %1 3"}}, + }, + }, + }, + { // replace with the same id + "%1 = OpTypeBool " + "%2 = OpTypeVector %1 3", + {{1, 1}, {2, 2}, {3, 3}}, + "%1 = OpTypeBool\n" + "%2 = OpTypeVector %1 3", + { + { // defs + {1, "%1 = OpTypeBool"}, + {2, "%2 = OpTypeVector %1 3"}, + }, + { // uses + {1, {"%2 = OpTypeVector %1 3"}}, + }, + }, + }, + { // replace in sequence + "%1 = OpTypeBool " + "%2 = OpTypeVector %1 3 " + "%3 = OpTypeInt 32 0 " + "%4 = OpTypeInt 32 1 ", + {{1, 3}, {3, 4}}, + "%1 = OpTypeBool\n" + "%2 = OpTypeVector %4 3\n" + "%3 = OpTypeInt 32 0\n" + "%4 = OpTypeInt 32 1", + { + { // defs + {1, "%1 = OpTypeBool"}, + {2, "%2 = OpTypeVector %4 3"}, + {3, "%3 = OpTypeInt 32 0"}, + {4, "%4 = OpTypeInt 32 1"}, + }, + { // uses + {4, {"%2 = OpTypeVector %4 3"}}, + }, + }, + }, + { // replace multiple uses + "%1 = OpTypeBool " + "%2 = OpTypeVector %1 2 " + "%3 = OpTypeVector %1 3 " + "%4 = OpTypeVector %1 4 " + "%5 = OpTypeMatrix %2 2 " + "%6 = OpTypeMatrix %3 3 " + "%7 = OpTypeMatrix %4 4 " + "%8 = OpTypeInt 32 0 " + "%9 = OpTypeInt 32 1 " + "%10 = OpTypeInt 64 0", + {{1, 8}, {2, 9}, {4, 10}}, + "%1 = OpTypeBool\n" + "%2 = OpTypeVector %8 2\n" + "%3 = OpTypeVector %8 3\n" + "%4 = OpTypeVector %8 4\n" + "%5 = OpTypeMatrix %9 2\n" + "%6 = OpTypeMatrix %3 3\n" + "%7 = OpTypeMatrix %10 4\n" + "%8 = OpTypeInt 32 0\n" + "%9 = OpTypeInt 32 1\n" + "%10 = OpTypeInt 64 0", + { + { // defs + {1, "%1 = OpTypeBool"}, + {2, "%2 = OpTypeVector %8 2"}, + {3, "%3 = OpTypeVector %8 3"}, + {4, "%4 = OpTypeVector %8 4"}, + {5, "%5 = OpTypeMatrix %9 2"}, + {6, "%6 = OpTypeMatrix %3 3"}, + {7, "%7 = OpTypeMatrix %10 4"}, + {8, "%8 = OpTypeInt 32 0"}, + {9, "%9 = OpTypeInt 32 1"}, + {10, "%10 = OpTypeInt 64 0"}, + }, + { // uses + {8, + { + "%2 = OpTypeVector %8 2", + "%3 = OpTypeVector %8 3", + "%4 = OpTypeVector %8 4", + } + }, + {9, {"%5 = OpTypeMatrix %9 2"}}, + {3, {"%6 = OpTypeMatrix %3 3"}}, + {10, {"%7 = OpTypeMatrix %10 4"}}, + }, + }, + }, + { // OpPhi. + kOpPhiTestFunction, + // replace one id used by OpPhi, replace one id generated by OpPhi + {{9, 13}, {11, 9}}, + "%1 = OpTypeVoid\n" + "%6 = OpTypeInt 32 0\n" + "%10 = OpTypeFloat 32\n" + "%16 = OpTypeBool\n" + "%3 = OpTypeFunction %1\n" + "%8 = OpConstant %6 0\n" + "%18 = OpConstant %6 1\n" + "%12 = OpConstant %10 1\n" + "%2 = OpFunction %1 None %3\n" + "%4 = OpLabel\n" + "OpBranch %5\n" + + "%5 = OpLabel\n" + "%7 = OpPhi %6 %8 %4 %13 %5\n" // %9 -> %13 + "%11 = OpPhi %10 %12 %4 %13 %5\n" + "%9 = OpIAdd %6 %7 %8\n" + "%13 = OpFAdd %10 %9 %12\n" // %11 -> %9 + "%17 = OpSLessThan %16 %7 %18\n" + "OpLoopMerge %19 %5 None\n" + "OpBranchConditional %17 %5 %19\n" + + "%19 = OpLabel\n" + "OpReturn\n" + "OpFunctionEnd", + { + { // defs. + {1, "%1 = OpTypeVoid"}, + {2, "%2 = OpFunction %1 None %3"}, + {3, "%3 = OpTypeFunction %1"}, + {4, "%4 = OpLabel"}, + {5, "%5 = OpLabel"}, + {6, "%6 = OpTypeInt 32 0"}, + {7, "%7 = OpPhi %6 %8 %4 %13 %5"}, + {8, "%8 = OpConstant %6 0"}, + {9, "%9 = OpIAdd %6 %7 %8"}, + {10, "%10 = OpTypeFloat 32"}, + {11, "%11 = OpPhi %10 %12 %4 %13 %5"}, + {12, "%12 = OpConstant %10 1.0"}, + {13, "%13 = OpFAdd %10 %9 %12"}, + {16, "%16 = OpTypeBool"}, + {17, "%17 = OpSLessThan %16 %7 %18"}, + {18, "%18 = OpConstant %6 1"}, + {19, "%19 = OpLabel"}, + }, + { // uses + {1, + { + "%2 = OpFunction %1 None %3", + "%3 = OpTypeFunction %1", + } + }, + {3, {"%2 = OpFunction %1 None %3"}}, + {4, + { + "%7 = OpPhi %6 %8 %4 %13 %5", + "%11 = OpPhi %10 %12 %4 %13 %5", + } + }, + {5, + { + "OpBranch %5", + "%7 = OpPhi %6 %8 %4 %13 %5", + "%11 = OpPhi %10 %12 %4 %13 %5", + "OpLoopMerge %19 %5 None", + "OpBranchConditional %17 %5 %19", + } + }, + {6, + { + // Can't properly check constants + // "%8 = OpConstant %6 0", + // "%18 = OpConstant %6 1", + "%7 = OpPhi %6 %8 %4 %13 %5", + "%9 = OpIAdd %6 %7 %8" + } + }, + {7, + { + "%9 = OpIAdd %6 %7 %8", + "%17 = OpSLessThan %16 %7 %18", + } + }, + {8, + { + "%7 = OpPhi %6 %8 %4 %13 %5", + "%9 = OpIAdd %6 %7 %8", + } + }, + {9, {"%13 = OpFAdd %10 %9 %12"}}, // uses of %9 changed from %7 to %13 + {10, + { + "%11 = OpPhi %10 %12 %4 %13 %5", + // "%12 = OpConstant %10 1", + "%13 = OpFAdd %10 %9 %12" + } + }, + // no more uses of %11 + {12, + { + "%11 = OpPhi %10 %12 %4 %13 %5", + "%13 = OpFAdd %10 %9 %12" + } + }, + {13, { + "%7 = OpPhi %6 %8 %4 %13 %5", + "%11 = OpPhi %10 %12 %4 %13 %5", + } + }, + {16, {"%17 = OpSLessThan %16 %7 %18"}}, + {17, {"OpBranchConditional %17 %5 %19"}}, + {18, {"%17 = OpSLessThan %16 %7 %18"}}, + {19, + { + "OpLoopMerge %19 %5 None", + "OpBranchConditional %17 %5 %19", + } + }, + }, + }, + }, + { // OpPhi defining and referencing the same id. + "%1 = OpTypeBool " + "%3 = OpTypeFunction %1 " + "%2 = OpConstantTrue %1 " + + "%4 = OpFunction %3 None %1 " + "%6 = OpLabel " + " OpBranch %7 " + "%7 = OpLabel " + "%8 = OpPhi %1 %8 %7 %2 %6 " // both defines and uses %8 + " OpBranch %7 " + " OpFunctionEnd", + {{8, 2}}, + "%1 = OpTypeBool\n" + "%3 = OpTypeFunction %1\n" + "%2 = OpConstantTrue %1\n" + + "%4 = OpFunction %3 None %1\n" + "%6 = OpLabel\n" + "OpBranch %7\n" + "%7 = OpLabel\n" + "%8 = OpPhi %1 %2 %7 %2 %6\n" // use of %8 changed to %2 + "OpBranch %7\n" + "OpFunctionEnd", + { + { // defs + {1, "%1 = OpTypeBool"}, + {2, "%2 = OpConstantTrue %1"}, + {3, "%3 = OpTypeFunction %1"}, + {4, "%4 = OpFunction %3 None %1"}, + {6, "%6 = OpLabel"}, + {7, "%7 = OpLabel"}, + {8, "%8 = OpPhi %1 %2 %7 %2 %6"}, + }, + { // uses + {1, + { + "%2 = OpConstantTrue %1", + "%3 = OpTypeFunction %1", + "%4 = OpFunction %3 None %1", + "%8 = OpPhi %1 %2 %7 %2 %6", + } + }, + {2, + { + // Only checking users + "%8 = OpPhi %1 %2 %7 %2 %6", + } + }, + {3, {"%4 = OpFunction %3 None %1"}}, + {6, {"%8 = OpPhi %1 %2 %7 %2 %6"}}, + {7, + { + "OpBranch %7", + "%8 = OpPhi %1 %2 %7 %2 %6", + "OpBranch %7", + } + }, + // {8, {"%8 = OpPhi %1 %8 %7 %2 %6"}}, + }, + }, + }, + }) +); +// clang-format on + struct KillDefCase { const char* before; std::vector<uint32_t> ids_to_kill; @@ -1656,7 +2019,7 @@ INSTANTIATE_TEST_SUITE_P( "OpGroupDecorate %1 %2 %3", }, }, - // memeber decorate + // member decorate { // code "OpMemberDecorate %1 0 RelaxedPrecision " @@ -1707,9 +2070,10 @@ TEST_F(UpdateUsesTest, KeepOldUses) { def->SetInOperands({{SPV_OPERAND_TYPE_ID, {25}}}); context->UpdateDefUse(def); - auto users = def_use_mgr->id_to_users(); - UserEntry entry = {def, use}; - EXPECT_THAT(users, Contains(entry)); + auto scanUser = [&](Instruction* user) { return user != use; }; + bool userFound = !def_use_mgr->WhileEachUser(def, scanUser); + + EXPECT_TRUE(userFound); } // clang-format on diff --git a/test/opt/desc_sroa_test.cpp b/test/opt/desc_sroa_test.cpp index dcb625d6..91c950e8 100644 --- a/test/opt/desc_sroa_test.cpp +++ b/test/opt/desc_sroa_test.cpp @@ -833,6 +833,93 @@ TEST_F(DescriptorScalarReplacementTest, MemberDecorationForResourceStruct) { SinglePassRunAndMatch<DescriptorScalarReplacement>(shader, true); } +TEST_F(DescriptorScalarReplacementTest, DecorateStringForReflect) { + // Check that an OpDecorateString instruction is correctly cloned to new + // variable. + + const std::string shader = R"( +; CHECK: OpName %g_testTextures_0_ "g_testTextures[0]" +; CHECK: OpDecorate %g_testTextures_0_ DescriptorSet 0 +; CHECK: OpDecorate %g_testTextures_0_ Binding 0 +; CHECK: OpDecorateString %g_testTextures_0_ UserTypeGOOGLE "texture2d" + OpCapability Shader + OpExtension "SPV_GOOGLE_hlsl_functionality1" + OpExtension "SPV_GOOGLE_user_type" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %gl_FragCoord %out_var_SV_Target + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + OpName %type_2d_image "type.2d.image" + OpName %g_testTextures "g_testTextures" + OpName %out_var_SV_Target "out.var.SV_Target" + OpName %main "main" + OpName %param_var_vPixelPos "param.var.vPixelPos" + OpName %src_main "src.main" + OpName %vPixelPos "vPixelPos" + OpName %bb_entry "bb.entry" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorateString %gl_FragCoord UserSemantic "SV_Position" + OpDecorateString %out_var_SV_Target UserSemantic "SV_Target" + OpDecorate %out_var_SV_Target Location 0 + OpDecorate %g_testTextures DescriptorSet 0 + OpDecorate %g_testTextures Binding 0 + OpDecorateString %g_testTextures UserTypeGOOGLE "texture2d" + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint_2 = OpConstant %uint 2 + %float = OpTypeFloat 32 +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_arr_type_2d_image_uint_2 = OpTypeArray %type_2d_image %uint_2 +%_ptr_UniformConstant__arr_type_2d_image_uint_2 = OpTypePointer UniformConstant %_arr_type_2d_image_uint_2 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %18 = OpTypeFunction %void +%_ptr_Function_v4float = OpTypePointer Function %v4float + %25 = OpTypeFunction %v4float %_ptr_Function_v4float + %v2float = OpTypeVector %float 2 + %v3uint = OpTypeVector %uint 3 + %v3int = OpTypeVector %int 3 + %v2int = OpTypeVector %int 2 +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image +%g_testTextures = OpVariable %_ptr_UniformConstant__arr_type_2d_image_uint_2 UniformConstant +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %18 + %19 = OpLabel +%param_var_vPixelPos = OpVariable %_ptr_Function_v4float Function + %22 = OpLoad %v4float %gl_FragCoord + OpStore %param_var_vPixelPos %22 + %23 = OpFunctionCall %v4float %src_main %param_var_vPixelPos + OpStore %out_var_SV_Target %23 + OpReturn + OpFunctionEnd + %src_main = OpFunction %v4float None %25 + %vPixelPos = OpFunctionParameter %_ptr_Function_v4float + %bb_entry = OpLabel + %28 = OpLoad %v4float %vPixelPos + %30 = OpVectorShuffle %v2float %28 %28 0 1 + %31 = OpCompositeExtract %float %30 0 + %32 = OpCompositeExtract %float %30 1 + %33 = OpConvertFToU %uint %31 + %34 = OpConvertFToU %uint %32 + %36 = OpCompositeConstruct %v3uint %33 %34 %uint_0 + %38 = OpBitcast %v3int %36 + %40 = OpVectorShuffle %v2int %38 %38 0 1 + %41 = OpCompositeExtract %int %38 2 + %43 = OpAccessChain %_ptr_UniformConstant_type_2d_image %g_testTextures %int_0 + %44 = OpLoad %type_2d_image %43 + %45 = OpImageFetch %v4float %44 %40 Lod %41 + OpReturnValue %45 + OpFunctionEnd +)"; + + SinglePassRunAndMatch<DescriptorScalarReplacement>(shader, true); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/test/opt/dominator_tree/generated.cpp b/test/opt/dominator_tree/generated.cpp index 534f770b..4fccef05 100644 --- a/test/opt/dominator_tree/generated.cpp +++ b/test/opt/dominator_tree/generated.cpp @@ -57,7 +57,7 @@ void check_dominance(const DominatorAnalysisBase& dom_tree, const Function* fn, } } -// Check that x does not dominates y and vise versa +// Check that x does not dominates y and vice versa void check_no_dominance(const DominatorAnalysisBase& dom_tree, const Function* fn, uint32_t x, uint32_t y) { SCOPED_TRACE("Check no domination for Basic Block " + std::to_string(x) + diff --git a/test/opt/eliminate_dead_const_test.cpp b/test/opt/eliminate_dead_const_test.cpp index 59f06f9f..87aab545 100644 --- a/test/opt/eliminate_dead_const_test.cpp +++ b/test/opt/eliminate_dead_const_test.cpp @@ -547,7 +547,7 @@ INSTANTIATE_TEST_SUITE_P( }, // Uint vector type spec constants. One vector has all component dead, - // another vector has one dead unsigend integer and one used unsigned + // another vector has one dead unsigned integer and one used unsigned // integer. { /* .used_consts = */ diff --git a/test/opt/eliminate_dead_input_components_test.cpp b/test/opt/eliminate_dead_input_components_test.cpp new file mode 100644 index 00000000..b0098f73 --- /dev/null +++ b/test/opt/eliminate_dead_input_components_test.cpp @@ -0,0 +1,404 @@ +// Copyright (c) 2022 The Khronos Group Inc. +// Copyright (c) 2022 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 <vector> + +#include "gmock/gmock.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using ElimDeadInputComponentsTest = PassTest<::testing::Test>; + +TEST_F(ElimDeadInputComponentsTest, ElimOneConstantIndex) { + // Should reduce to uv[2] + // + // #version 450 + // + // layout(location = 0) in vec4 uv[8]; + // + // out gl_PerVertex { + // vec4 gl_Position; + // }; + // + // void main() + // { + // gl_Position = uv[1]; + // } + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %_ %uv + OpSource GLSL 450 + OpName %main "main" + OpName %gl_PerVertex "gl_PerVertex" + OpMemberName %gl_PerVertex 0 "gl_Position" + OpName %_ "" + OpName %uv "uv" + OpMemberDecorate %gl_PerVertex 0 BuiltIn Position + OpDecorate %gl_PerVertex Block + OpDecorate %uv Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%gl_PerVertex = OpTypeStruct %v4float +%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex + %_ = OpVariable %_ptr_Output_gl_PerVertex Output + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %uint_8 = OpConstant %uint 8 +%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8 +%_ptr_Input__arr_v4float_uint_8 = OpTypePointer Input %_arr_v4float_uint_8 + %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input + %int_1 = OpConstant %int 1 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +;CHECK-NOT: %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input +;CHECK: %uv = OpVariable %_ptr_Input__arr_v4float_uint_2 Input + %main = OpFunction %void None %3 + %5 = OpLabel + %20 = OpAccessChain %_ptr_Input_v4float %uv %int_1 + %21 = OpLoad %v4float %20 + %23 = OpAccessChain %_ptr_Output_v4float %_ %int_0 + OpStore %23 %21 + OpReturn + OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_3); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true); +} + +TEST_F(ElimDeadInputComponentsTest, ElimOneConstantIndexInBounds) { + // Same as ElimOneConstantIndex but with OpInBoundsAccessChain + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %_ %uv + OpSource GLSL 450 + OpName %main "main" + OpName %gl_PerVertex "gl_PerVertex" + OpMemberName %gl_PerVertex 0 "gl_Position" + OpName %_ "" + OpName %uv "uv" + OpMemberDecorate %gl_PerVertex 0 BuiltIn Position + OpDecorate %gl_PerVertex Block + OpDecorate %uv Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%gl_PerVertex = OpTypeStruct %v4float +%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex + %_ = OpVariable %_ptr_Output_gl_PerVertex Output + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %uint_8 = OpConstant %uint 8 +%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8 +%_ptr_Input__arr_v4float_uint_8 = OpTypePointer Input %_arr_v4float_uint_8 + %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input + %int_1 = OpConstant %int 1 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +;CHECK-NOT: %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input +;CHECK: %uv = OpVariable %_ptr_Input__arr_v4float_uint_2 Input + %main = OpFunction %void None %3 + %5 = OpLabel + %20 = OpInBoundsAccessChain %_ptr_Input_v4float %uv %int_1 + %21 = OpLoad %v4float %20 + %23 = OpAccessChain %_ptr_Output_v4float %_ %int_0 + OpStore %23 %21 + OpReturn + OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_3); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true); +} + +TEST_F(ElimDeadInputComponentsTest, ElimTwoConstantIndices) { + // Should reduce to uv[4] + // + // #version 450 + // + // layout(location = 0) in vec4 uv[8]; + // + // out gl_PerVertex { + // vec4 gl_Position; + // }; + // + // void main() + // { + // gl_Position = uv[1] + uv[3]; + // } + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %_ %uv + OpSource GLSL 450 + OpName %main "main" + OpName %gl_PerVertex "gl_PerVertex" + OpMemberName %gl_PerVertex 0 "gl_Position" + OpName %_ "" + OpName %uv "uv" + OpMemberDecorate %gl_PerVertex 0 BuiltIn Position + OpDecorate %gl_PerVertex Block + OpDecorate %uv Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%gl_PerVertex = OpTypeStruct %v4float +%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex + %_ = OpVariable %_ptr_Output_gl_PerVertex Output + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %uint_8 = OpConstant %uint 8 +%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8 +%_ptr_Input__arr_v4float_uint_8 = OpTypePointer Input %_arr_v4float_uint_8 + %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input + %int_1 = OpConstant %int 1 +%_ptr_Input_v4float = OpTypePointer Input %v4float + %int_3 = OpConstant %int 3 +%_ptr_Output_v4float = OpTypePointer Output %v4float +;CHECK-NOT: %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input +;CHECK: %uv = OpVariable %_ptr_Input__arr_v4float_uint_4 Input + %main = OpFunction %void None %3 + %5 = OpLabel + %20 = OpAccessChain %_ptr_Input_v4float %uv %int_1 + %21 = OpLoad %v4float %20 + %23 = OpAccessChain %_ptr_Input_v4float %uv %int_3 + %24 = OpLoad %v4float %23 + %25 = OpFAdd %v4float %21 %24 + %27 = OpAccessChain %_ptr_Output_v4float %_ %int_0 + OpStore %27 %25 + OpReturn + OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_3); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true); +} + +TEST_F(ElimDeadInputComponentsTest, NoElimMaxConstantIndex) { + // Should not reduce uv[8] because of max index of 7 + // + // #version 450 + // + // layout(location = 0) in vec4 uv[8]; + // + // out gl_PerVertex { + // vec4 gl_Position; + // }; + // + // void main() + // { + // gl_Position = uv[1] + uv[7]; + // } + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %_ %uv + OpSource GLSL 450 + OpName %main "main" + OpName %gl_PerVertex "gl_PerVertex" + OpMemberName %gl_PerVertex 0 "gl_Position" + OpName %_ "" + OpName %uv "uv" + OpMemberDecorate %gl_PerVertex 0 BuiltIn Position + OpDecorate %gl_PerVertex Block + OpDecorate %uv Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%gl_PerVertex = OpTypeStruct %v4float +%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex + %_ = OpVariable %_ptr_Output_gl_PerVertex Output + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %uint_8 = OpConstant %uint 8 +%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8 +%_ptr_Input__arr_v4float_uint_8 = OpTypePointer Input %_arr_v4float_uint_8 + %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input + %int_1 = OpConstant %int 1 +%_ptr_Input_v4float = OpTypePointer Input %v4float + %int_7 = OpConstant %int 7 +%_ptr_Output_v4float = OpTypePointer Output %v4float +;CHECK: %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input + %main = OpFunction %void None %3 + %5 = OpLabel + %20 = OpAccessChain %_ptr_Input_v4float %uv %int_1 + %21 = OpLoad %v4float %20 + %23 = OpAccessChain %_ptr_Input_v4float %uv %int_7 + %24 = OpLoad %v4float %23 + %25 = OpFAdd %v4float %21 %24 + %27 = OpAccessChain %_ptr_Output_v4float %_ %int_0 + OpStore %27 %25 + OpReturn + OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_3); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true); +} + +TEST_F(ElimDeadInputComponentsTest, NoElimNonConstantIndex) { + // Should not reduce uv[8] because of non-constant index of ui + // + // #version 450 + // + // layout(location = 0) in vec4 uv[8]; + // + // out gl_PerVertex { + // vec4 gl_Position; + // }; + // + // uniform ubname { + // int ui; + // } ubinst; + // + // void main() + // { + // gl_Position = uv[1] + uv[ubinst.ui]; + // } + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %_ %uv %ubinst + OpSource GLSL 450 + OpName %main "main" + OpName %gl_PerVertex "gl_PerVertex" + OpMemberName %gl_PerVertex 0 "gl_Position" + OpName %_ "" + OpName %uv "uv" + OpName %ubname "ubname" + OpMemberName %ubname 0 "ui" + OpName %ubinst "ubinst" + OpMemberDecorate %gl_PerVertex 0 BuiltIn Position + OpDecorate %gl_PerVertex Block + OpDecorate %uv Location 0 + OpMemberDecorate %ubname 0 Offset 0 + OpDecorate %ubname Block + OpDecorate %ubinst DescriptorSet 0 + OpDecorate %ubinst Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%gl_PerVertex = OpTypeStruct %v4float +%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex + %_ = OpVariable %_ptr_Output_gl_PerVertex Output + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %uint_8 = OpConstant %uint 8 +%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8 +%_ptr_Input__arr_v4float_uint_8 = OpTypePointer Input %_arr_v4float_uint_8 + %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input + %int_1 = OpConstant %int 1 +%_ptr_Input_v4float = OpTypePointer Input %v4float + %ubname = OpTypeStruct %int +%_ptr_Uniform_ubname = OpTypePointer Uniform %ubname + %ubinst = OpVariable %_ptr_Uniform_ubname Uniform +%_ptr_Uniform_int = OpTypePointer Uniform %int +%_ptr_Output_v4float = OpTypePointer Output %v4float +;CHECK: %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input + %main = OpFunction %void None %3 + %5 = OpLabel + %20 = OpAccessChain %_ptr_Input_v4float %uv %int_1 + %21 = OpLoad %v4float %20 + %26 = OpAccessChain %_ptr_Uniform_int %ubinst %int_0 + %27 = OpLoad %int %26 + %28 = OpAccessChain %_ptr_Input_v4float %uv %27 + %29 = OpLoad %v4float %28 + %30 = OpFAdd %v4float %21 %29 + %32 = OpAccessChain %_ptr_Output_v4float %_ %int_0 + OpStore %32 %30 + OpReturn + OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_3); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true); +} + +TEST_F(ElimDeadInputComponentsTest, NoElimNonIndexedAccessChain) { + // Should not change due to non-indexed access chain + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %_ %uv + OpSource GLSL 450 + OpName %main "main" + OpName %gl_PerVertex "gl_PerVertex" + OpMemberName %gl_PerVertex 0 "gl_Position" + OpName %_ "" + OpName %uv "uv" + OpMemberDecorate %gl_PerVertex 0 BuiltIn Position + OpDecorate %gl_PerVertex Block + OpDecorate %uv Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%gl_PerVertex = OpTypeStruct %v4float +%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex + %_ = OpVariable %_ptr_Output_gl_PerVertex Output + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %uint_8 = OpConstant %uint 8 +%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8 +%_ptr_Input__arr_v4float_uint_8 = OpTypePointer Input %_arr_v4float_uint_8 + %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input + %int_1 = OpConstant %int 1 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +;CHECK: %uv = OpVariable %_ptr_Input__arr_v4float_uint_8 Input + %main = OpFunction %void None %3 + %5 = OpLabel + %20 = OpAccessChain %_ptr_Input__arr_v4float_uint_8 %uv + OpReturn + OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_3); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch<EliminateDeadInputComponentsPass>(text, true); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/test/opt/flatten_decoration_test.cpp b/test/opt/flatten_decoration_test.cpp index d8d88677..63207fd2 100644 --- a/test/opt/flatten_decoration_test.cpp +++ b/test/opt/flatten_decoration_test.cpp @@ -43,7 +43,7 @@ OpName %Camera "Camera" )"; } -// Retuns types +// Returns types std::string TypesAndFunctionsAssembly() { return R"(%void = OpTypeVoid diff --git a/test/opt/fold_test.cpp b/test/opt/fold_test.cpp index 0487e78c..7565ca7f 100644 --- a/test/opt/fold_test.cpp +++ b/test/opt/fold_test.cpp @@ -224,6 +224,7 @@ OpName %main "main" %104 = OpConstant %float 0 ; Need a def with an numerical id to define id maps. %float_null = OpConstantNull %float %float_0 = OpConstant %float 0 +%float_n0 = OpConstant %float -0.0 %float_1 = OpConstant %float 1 %float_2 = OpConstant %float 2 %float_3 = OpConstant %float 3 @@ -249,6 +250,7 @@ OpName %main "main" %105 = OpConstant %double 0 ; Need a def with an numerical id to define id maps. %double_null = OpConstantNull %double %double_0 = OpConstant %double 0 +%double_n0 = OpConstant %double -0.0 %double_1 = OpConstant %double 1 %double_2 = OpConstant %double 2 %double_3 = OpConstant %double 3 @@ -1987,7 +1989,39 @@ INSTANTIATE_TEST_SUITE_P(FloatConstantFoldingTest, FloatInstructionFoldingTest, "%2 = OpExtInst %float %1 Pow %float_2 %float_3\n" + "OpReturn\n" + "OpFunctionEnd", - 2, 8.0) + 2, 8.0), + // Test case 43: Fold 1.0 / -0.0. + InstructionFoldingCase<float>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFDiv %float %float_1 %float_n0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, -std::numeric_limits<float>::infinity()), + // Test case 44: Fold -1.0 / -0.0 + InstructionFoldingCase<float>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFDiv %float %float_n1 %float_n0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, std::numeric_limits<float>::infinity()), + // Test case 45: Fold 0.0 / 0.0 + InstructionFoldingCase<float>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFDiv %float %float_0 %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, std::numeric_limits<float>::quiet_NaN()), + // Test case 46: Fold 0.0 / -0.0 + InstructionFoldingCase<float>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFDiv %float %float_0 %float_n0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, std::numeric_limits<float>::quiet_NaN()) )); // clang-format on @@ -2019,7 +2053,11 @@ TEST_P(DoubleInstructionFoldingTest, Case) { const_mrg->GetConstantFromInst(inst)->AsFloatConstant(); EXPECT_NE(result, nullptr); if (result != nullptr) { - EXPECT_EQ(result->GetDoubleValue(), tc.expected_result); + if (!std::isnan(tc.expected_result)) { + EXPECT_EQ(result->GetDoubleValue(), tc.expected_result); + } else { + EXPECT_TRUE(std::isnan(result->GetDoubleValue())); + } } } } @@ -2222,7 +2260,39 @@ INSTANTIATE_TEST_SUITE_P(DoubleConstantFoldingTest, DoubleInstructionFoldingTest "%2 = OpExtInst %double %1 Pow %double_2 %double_3\n" + "OpReturn\n" + "OpFunctionEnd", - 2, 8.0) + 2, 8.0), + // Test case 23: Fold 1.0 / -0.0. + InstructionFoldingCase<double>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFDiv %double %double_1 %double_n0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, -std::numeric_limits<double>::infinity()), + // Test case 24: Fold -1.0 / -0.0 + InstructionFoldingCase<double>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFDiv %double %double_n1 %double_n0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, std::numeric_limits<double>::infinity()), + // Test case 25: Fold 0.0 / 0.0 + InstructionFoldingCase<double>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFDiv %double %double_0 %double_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, std::numeric_limits<double>::quiet_NaN()), + // Test case 26: Fold 0.0 / -0.0 + InstructionFoldingCase<double>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFDiv %double %double_0 %double_n0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, std::numeric_limits<double>::quiet_NaN()) )); // clang-format on @@ -7017,12 +7087,33 @@ INSTANTIATE_TEST_SUITE_P(DotProductMatchingTest, MatchingInstructionFoldingTest, 3, true) )); +INSTANTIATE_TEST_SUITE_P(VectorShuffleMatchingTest, MatchingInstructionFoldingTest, +::testing::Values( + // Test case 0: Using OpDot to extract last element. + InstructionFoldingCase<bool>( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + + "; CHECK: [[v2int:%\\w+]] = OpTypeVector [[int]] 2{{[[:space:]]}}\n" + + "; CHECK: [[null:%\\w+]] = OpConstantNull [[v2int]]\n" + + "; CHECK: OpVectorShuffle\n" + + "; CHECK: %3 = OpVectorShuffle [[v2int]] [[null]] {{%\\w+}} 4294967295 2\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%load = OpLoad %int %n\n" + + "%2 = OpVectorShuffle %v2int %v2int_null %v2int_2_3 3 0xFFFFFFFF \n" + + "%3 = OpVectorShuffle %v2int %2 %v2int_2_3 1 2 \n" + + "OpReturn\n" + + "OpFunctionEnd", + 3, true) + )); + using MatchingInstructionWithNoResultFoldingTest = ::testing::TestWithParam<InstructionFoldingCase<bool>>; // Test folding instructions that do not have a result. The instruction // that will be folded is the last instruction before the return. If there -// are multiple returns, there is not guarentee which one is used. +// are multiple returns, there is not guarantee which one is used. TEST_P(MatchingInstructionWithNoResultFoldingTest, Case) { const auto& tc = GetParam(); diff --git a/test/opt/freeze_spec_const_test.cpp b/test/opt/freeze_spec_const_test.cpp index e5999cec..ad0fc32e 100644 --- a/test/opt/freeze_spec_const_test.cpp +++ b/test/opt/freeze_spec_const_test.cpp @@ -128,6 +128,51 @@ TEST_F(FreezeSpecConstantValueRemoveDecorationTest, /* skip_nop = */ true); } +TEST_F(FreezeSpecConstantValueRemoveDecorationTest, + RemoveDecorationForLocalSizeIdWithSpecId) { + std::vector<const char*> text = { + // clang-format off + "OpCapability Shader", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint GLCompute %2 \"main\"", + "OpExecutionModeId %2 LocalSizeId %uint_32 %uint_1 %uint_1_0", + "OpSource GLSL 450", + "OpDecorate %3 SpecId 18", + "OpDecorate %5 SpecId 19", + "%void = OpTypeVoid", + "%9 = OpTypeFunction %void", + "%uint = OpTypeInt 32 0", + "%uint_32 = OpSpecConstant %uint 32", + "%uint_1 = OpConstant %uint 1", + "%uint_1_0 = OpSpecConstant %uint 1", + "%2 = OpFunction %void None %9", + "%11 = OpLabel", + "OpReturn", + "OpFunctionEnd", + // clang-format on + }; + std::string expected_disassembly = SelectiveJoin(text, [](const char* line) { + return std::string(line).find("SpecId") != std::string::npos; + }); + std::vector<std::pair<const char*, const char*>> replacement_pairs = { + {"%uint_32 = OpSpecConstant %uint 32", "%uint_32 = OpConstant %uint 32"}, + {"%uint_1_0 = OpSpecConstant %uint 1", "%uint_1_0 = OpConstant %uint 1"}, + }; + for (auto& p : replacement_pairs) { + EXPECT_TRUE(FindAndReplace(&expected_disassembly, p.first, p.second)) + << "text:\n" + << expected_disassembly << "\n" + << "find_str:\n" + << p.first << "\n" + << "replace_str:\n" + << p.second << "\n"; + } + SinglePassRunAndCheck<FreezeSpecConstantValuePass>(JoinAllInsts(text), + expected_disassembly, + /* skip_nop = */ true); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/test/opt/graphics_robust_access_test.cpp b/test/opt/graphics_robust_access_test.cpp index 3c23347d..057b909d 100644 --- a/test/opt/graphics_robust_access_test.cpp +++ b/test/opt/graphics_robust_access_test.cpp @@ -1242,7 +1242,7 @@ TEST_F(GraphicsRobustAccessTest, ACArrayRTArrayStructVectorElem) { ; CHECK-DAG: %int_9 = OpConstant %int 9 ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647 ; CHECK: OpLabel - ; This access chain is manufatured only so we can compute the array length. + ; This access chain is manufactured only so we can compute the array length. ; Note that the %int_9 is already clamped ; CHECK: %[[ssbo_base:\w+]] = )" << ac << R"( %[[ssbo_p]] %var %int_9 diff --git a/test/opt/inline_test.cpp b/test/opt/inline_test.cpp index d22f027c..cefd8e54 100644 --- a/test/opt/inline_test.cpp +++ b/test/opt/inline_test.cpp @@ -4238,7 +4238,7 @@ TEST_F(InlineTest, CreateConstantForInlinedAt) { // This shader causes CreateDebugInlinedAt to generate a constant. // Using the Constant manager would attempt to build the invalidated // DefUse manager during inlining which could cause an assert because - // the function is in an inconsistant state. This test verifies that + // the function is in an inconsistent state. This test verifies that // CreateDebugInlinedAt detects that the DefUse manager is disabled // and creates a duplicate constant safely without the Constant manager. // diff --git a/test/opt/inst_bindless_check_test.cpp b/test/opt/inst_bindless_check_test.cpp index 1a42329b..4c271dee 100644 --- a/test/opt/inst_bindless_check_test.cpp +++ b/test/opt/inst_bindless_check_test.cpp @@ -9984,8 +9984,8 @@ TEST_F(InstBindlessTest, SamplerBufferConstructorOOBFetch) { // // Compute shader // Geometry shader -// Tesselation control shader -// Tesselation eval shader +// Tessellation control shader +// Tessellation eval shader // OpImage // SampledImage variable diff --git a/test/opt/inst_debug_printf_test.cpp b/test/opt/inst_debug_printf_test.cpp index 8123ffbb..c5fd6799 100644 --- a/test/opt/inst_debug_printf_test.cpp +++ b/test/opt/inst_debug_printf_test.cpp @@ -206,8 +206,8 @@ OpFunctionEnd // // Compute shader // Geometry shader -// Tesselation control shader -// Tesselation eval shader +// Tessellation control shader +// Tessellation eval shader // Vertex shader } // namespace diff --git a/test/opt/instruction_test.cpp b/test/opt/instruction_test.cpp index c5b92efb..2a48134d 100644 --- a/test/opt/instruction_test.cpp +++ b/test/opt/instruction_test.cpp @@ -62,12 +62,6 @@ TEST(InstructionTest, CreateWithOpcodeAndNoOperands) { 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)); diff --git a/test/opt/ir_context_test.cpp b/test/opt/ir_context_test.cpp index b6866d01..ece04796 100644 --- a/test/opt/ir_context_test.cpp +++ b/test/opt/ir_context_test.cpp @@ -90,6 +90,21 @@ TEST_F(IRContextTest, IndividualValidAfterBuild) { } } +TEST_F(IRContextTest, DontRebuildValidAnalysis) { + std::unique_ptr<Module> module(new Module()); + IRContext localContext(SPV_ENV_UNIVERSAL_1_2, std::move(module), + spvtools::MessageConsumer()); + + auto* oldCfg = localContext.cfg(); + auto* oldDefUse = localContext.get_def_use_mgr(); + localContext.BuildInvalidAnalyses(IRContext::kAnalysisCFG | + IRContext::kAnalysisDefUse); + auto* newCfg = localContext.cfg(); + auto* newDefUse = localContext.get_def_use_mgr(); + EXPECT_EQ(oldCfg, newCfg); + EXPECT_EQ(oldDefUse, newDefUse); +} + TEST_F(IRContextTest, AllValidAfterBuild) { std::unique_ptr<Module> module = MakeUnique<Module>(); IRContext localContext(SPV_ENV_UNIVERSAL_1_2, std::move(module), diff --git a/test/opt/loop_optimizations/loop_descriptions.cpp b/test/opt/loop_optimizations/loop_descriptions.cpp index 4d2f989a..b3f4f440 100644 --- a/test/opt/loop_optimizations/loop_descriptions.cpp +++ b/test/opt/loop_optimizations/loop_descriptions.cpp @@ -298,7 +298,7 @@ TEST_F(PassClassTest, NoLoop) { /* Generated from following GLSL with latch block artificially inserted to be -seperate from continue. +separate from continue. #version 430 void main(void) { float x[10]; diff --git a/test/opt/loop_optimizations/loop_fission.cpp b/test/opt/loop_optimizations/loop_fission.cpp index 55b9c263..bc3ec39b 100644 --- a/test/opt/loop_optimizations/loop_fission.cpp +++ b/test/opt/loop_optimizations/loop_fission.cpp @@ -692,7 +692,7 @@ SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); SinglePassRunAndCheck<LoopFissionPass>(source, expected, true); // By passing 1 as argument we are using the constructor which makes the -// critera to split the loop be if the registers in the loop exceede 1. By +// criteria to split the loop be if the registers in the loop exceede 1. By // using this constructor we are also enabling multiple passes (disabled by // default). SinglePassRunAndCheck<LoopFissionPass>(source, expected_multiple_passes, true, diff --git a/test/opt/loop_optimizations/unroll_simple.cpp b/test/opt/loop_optimizations/unroll_simple.cpp index ac0dfde7..b72305c8 100644 --- a/test/opt/loop_optimizations/unroll_simple.cpp +++ b/test/opt/loop_optimizations/unroll_simple.cpp @@ -886,7 +886,7 @@ OpFunctionEnd LoopUnroller loop_unroller; SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); // By unrolling by a factor that doesn't divide evenly into the number of loop - // iterations we perfom an additional transform when partially unrolling to + // iterations we perform an additional transform when partially unrolling to // account for the remainder. SinglePassRunAndCheck<PartialUnrollerTestPass<3>>(text, output, false); } @@ -3118,7 +3118,7 @@ OpFunctionEnd /* Generated from following GLSL with latch block artificially inserted to be -seperate from continue. +separate from continue. #version 430 void main(void) { float x[10]; diff --git a/test/opt/module_test.cpp b/test/opt/module_test.cpp index a3c2eed7..17a13650 100644 --- a/test/opt/module_test.cpp +++ b/test/opt/module_test.cpp @@ -52,7 +52,7 @@ inline std::unique_ptr<IRContext> BuildModule(std::string text) { } TEST(ModuleTest, ComputeIdBound) { - // Emtpy module case. + // Empty module case. EXPECT_EQ(1u, BuildModule("")->module()->ComputeIdBound()); // Sensitive to result id EXPECT_EQ(2u, BuildModule("%void = OpTypeVoid")->module()->ComputeIdBound()); diff --git a/test/opt/optimizer_test.cpp b/test/opt/optimizer_test.cpp index a51638a1..0171c09b 100644 --- a/test/opt/optimizer_test.cpp +++ b/test/opt/optimizer_test.cpp @@ -147,7 +147,7 @@ TEST(Optimizer, CanRegisterPassesFromFlags) { std::vector<std::string> pass_flags = { "--strip-debug", - "--strip-reflect", + "--strip-nonsemantic", "--set-spec-const-default-value=23:42 21:12", "--if-conversion", "--freeze-spec-const", diff --git a/test/opt/pass_manager_test.cpp b/test/opt/pass_manager_test.cpp index 22d5e22e..4f36d5b2 100644 --- a/test/opt/pass_manager_test.cpp +++ b/test/opt/pass_manager_test.cpp @@ -30,7 +30,7 @@ namespace { using spvtest::GetIdBound; using ::testing::Eq; -// A null pass whose construtors accept arguments +// A null pass whose constructors accept arguments class NullPassWithArgs : public NullPass { public: NullPassWithArgs(uint32_t) {} diff --git a/test/opt/remove_dontinline_test.cpp b/test/opt/remove_dontinline_test.cpp new file mode 100644 index 00000000..c5425e8c --- /dev/null +++ b/test/opt/remove_dontinline_test.cpp @@ -0,0 +1,127 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <vector> + +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using StrengthReductionBasicTest = PassTest<::testing::Test>; + +TEST_F(StrengthReductionBasicTest, ClearDontInline) { + const std::vector<const char*> text = { + // clang-format off + "OpCapability Shader", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Vertex %main \"main\"", + "%void = OpTypeVoid", + "%4 = OpTypeFunction %void", +"; CHECK: OpFunction %void None", + "%main = OpFunction %void DontInline %4", + "%8 = OpLabel", + "OpReturn", + "OpFunctionEnd" + // clang-format on + }; + + SinglePassRunAndMatch<RemoveDontInline>(JoinAllInsts(text), true); +} + +TEST_F(StrengthReductionBasicTest, LeaveUnchanged1) { + const std::vector<const char*> text = { + // clang-format off + "OpCapability Shader", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Vertex %main \"main\"", + "%void = OpTypeVoid", + "%4 = OpTypeFunction %void", + "%main = OpFunction %void None %4", + "%8 = OpLabel", + "OpReturn", + "OpFunctionEnd" + // clang-format on + }; + + EXPECT_EQ(Pass::Status::SuccessWithoutChange, + std::get<1>(SinglePassRunAndDisassemble<RemoveDontInline>( + JoinAllInsts(text), false, true))); +} + +TEST_F(StrengthReductionBasicTest, LeaveUnchanged2) { + const std::vector<const char*> text = { + // clang-format off + "OpCapability Shader", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Vertex %main \"main\"", + "%void = OpTypeVoid", + "%4 = OpTypeFunction %void", + "%main = OpFunction %void Inline %4", + "%8 = OpLabel", + "OpReturn", + "OpFunctionEnd" + // clang-format on + }; + + EXPECT_EQ(Pass::Status::SuccessWithoutChange, + std::get<1>(SinglePassRunAndDisassemble<RemoveDontInline>( + JoinAllInsts(text), false, true))); +} + +TEST_F(StrengthReductionBasicTest, ClearMultipleDontInline) { + const std::vector<const char*> text = { + // clang-format off + "OpCapability Shader", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Vertex %main1 \"main1\"", + "OpEntryPoint Vertex %main2 \"main2\"", + "OpEntryPoint Vertex %main3 \"main3\"", + "OpEntryPoint Vertex %main4 \"main4\"", + "%void = OpTypeVoid", + "%4 = OpTypeFunction %void", + "; CHECK: OpFunction %void None", + "%main1 = OpFunction %void DontInline %4", + "%8 = OpLabel", + "OpReturn", + "OpFunctionEnd", + "; CHECK: OpFunction %void Inline", + "%main2 = OpFunction %void Inline %4", + "%9 = OpLabel", + "OpReturn", + "OpFunctionEnd", + "; CHECK: OpFunction %void Pure", + "%main3 = OpFunction %void DontInline|Pure %4", + "%10 = OpLabel", + "OpReturn", + "OpFunctionEnd", + "; CHECK: OpFunction %void None", + "%main4 = OpFunction %void None %4", + "%11 = OpLabel", + "OpReturn", + "OpFunctionEnd" + // clang-format on + }; + + SinglePassRunAndMatch<RemoveDontInline>(JoinAllInsts(text), true); +} +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/test/opt/replace_desc_array_access_using_var_index_test.cpp b/test/opt/replace_desc_array_access_using_var_index_test.cpp index ca625812..9900304b 100644 --- a/test/opt/replace_desc_array_access_using_var_index_test.cpp +++ b/test/opt/replace_desc_array_access_using_var_index_test.cpp @@ -406,6 +406,94 @@ TEST_F(ReplaceDescArrayAccessUsingVarIndexTest, SinglePassRunAndMatch<ReplaceDescArrayAccessUsingVarIndex>(text, true); } +TEST_F(ReplaceDescArrayAccessUsingVarIndexTest, + ReplaceAccessChainToTextureArrayWithNonUniformIndex) { + const std::string text = R"( + OpCapability Shader + OpCapability ShaderNonUniform + OpCapability SampledImageArrayNonUniformIndexing + OpExtension "SPV_EXT_descriptor_indexing" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %PSMain "PSMain" %in_var_TEXCOORD0 %in_var_MATERIAL_ID %out_var_SV_TARGET + OpExecutionMode %PSMain OriginUpperLeft + OpSource HLSL 610 + OpName %type_sampler "type.sampler" + OpName %sampler_ "sampler_" + OpName %type_2d_image "type.2d.image" + OpName %texture_2d "texture_2d" + OpName %in_var_TEXCOORD0 "in.var.TEXCOORD0" + OpName %in_var_MATERIAL_ID "in.var.MATERIAL_ID" + OpName %out_var_SV_TARGET "out.var.SV_TARGET" + OpName %PSMain "PSMain" + OpName %type_sampled_image "type.sampled.image" + OpDecorate %in_var_MATERIAL_ID Flat + OpDecorate %in_var_TEXCOORD0 Location 0 + OpDecorate %in_var_MATERIAL_ID Location 1 + OpDecorate %out_var_SV_TARGET Location 0 + OpDecorate %sampler_ DescriptorSet 1 + OpDecorate %sampler_ Binding 1 + OpDecorate %texture_2d DescriptorSet 0 + OpDecorate %texture_2d Binding 0 + +; CHECK: OpDecorate [[v0:%\w+]] NonUniform +; CHECK: OpDecorate [[v1:%\w+]] NonUniform +; CHECK: OpDecorate [[v2:%\w+]] NonUniform +; CHECK: OpDecorate [[v3:%\w+]] NonUniform + + OpDecorate %10 NonUniform + OpDecorate %11 NonUniform + OpDecorate %12 NonUniform + OpDecorate %13 NonUniform +%type_sampler = OpTypeSampler +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler + %uint = OpTypeInt 32 0 + %uint_4 = OpConstant %uint 4 + %float = OpTypeFloat 32 +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_arr_type_2d_image_uint_4 = OpTypeArray %type_2d_image %uint_4 +%_ptr_UniformConstant__arr_type_2d_image_uint_4 = OpTypePointer UniformConstant %_arr_type_2d_image_uint_4 + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%_ptr_Input_uint = OpTypePointer Input %uint + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %26 = OpTypeFunction %void +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image +%type_sampled_image = OpTypeSampledImage %type_2d_image + %sampler_ = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant + %texture_2d = OpVariable %_ptr_UniformConstant__arr_type_2d_image_uint_4 UniformConstant +%in_var_TEXCOORD0 = OpVariable %_ptr_Input_v2float Input +%in_var_MATERIAL_ID = OpVariable %_ptr_Input_uint Input +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output + +; CHECK: %uint_0 = OpConstant %uint 0 +; CHECK: %uint_1 = OpConstant %uint 1 +; CHECK: %uint_2 = OpConstant %uint 2 +; CHECK: %uint_3 = OpConstant %uint 3 + + %PSMain = OpFunction %void None %26 + %28 = OpLabel + %29 = OpLoad %v2float %in_var_TEXCOORD0 + %30 = OpLoad %uint %in_var_MATERIAL_ID +; CHECK: [[v0]] = OpCopyObject %uint {{%\w+}} + %10 = OpCopyObject %uint %30 +; CHECK: [[v1]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %texture_2d [[v0]] + %11 = OpAccessChain %_ptr_UniformConstant_type_2d_image %texture_2d %10 +; CHECK: [[v2]] = OpLoad %type_2d_image [[v1]] + %12 = OpLoad %type_2d_image %11 + %31 = OpLoad %type_sampler %sampler_ +; CHECK: [[v3]] = OpSampledImage %type_sampled_image [[v2]] {{%\w+}} + %13 = OpSampledImage %type_sampled_image %12 %31 + %32 = OpImageSampleImplicitLod %v4float %13 %29 None + OpStore %out_var_SV_TARGET %32 + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch<ReplaceDescArrayAccessUsingVarIndex>(text, true); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/test/opt/scalar_replacement_test.cpp b/test/opt/scalar_replacement_test.cpp index 8cb888c9..7db997d4 100644 --- a/test/opt/scalar_replacement_test.cpp +++ b/test/opt/scalar_replacement_test.cpp @@ -470,9 +470,9 @@ TEST_F(ScalarReplacementTest, NonUniformCompositeInitialization) { ; CHECK: [[const_array:%\w+]] = OpConstantComposite [[array]] ; CHECK: [[const_matrix:%\w+]] = OpConstantNull [[matrix]] ; CHECK: [[const_struct1:%\w+]] = OpConstantComposite [[struct1]] -; CHECK: OpConstantNull [[uint]] -; CHECK: OpConstantNull [[vector]] -; CHECK: OpConstantNull [[long]] +; CHECK: OpUndef [[uint]] +; CHECK: OpUndef [[vector]] +; CHECK: OpUndef [[long]] ; CHECK: OpFunction ; CHECK-NOT: OpVariable [[struct2_ptr]] Function ; CHECK: OpVariable [[uint_ptr]] Function @@ -654,11 +654,10 @@ TEST_F(ScalarReplacementTest, ReplaceWholeLoadCopyMemoryAccess) { ; CHECK: [[uint:%\w+]] = OpTypeInt 32 0 ; CHECK: [[struct1:%\w+]] = OpTypeStruct [[uint]] [[uint]] ; CHECK: [[uint_ptr:%\w+]] = OpTypePointer Function [[uint]] -; CHECK: [[const:%\w+]] = OpConstant [[uint]] 0 -; CHECK: [[null:%\w+]] = OpConstantNull [[uint]] +; CHECK: [[undef:%\w+]] = OpUndef [[uint]] ; CHECK: [[var0:%\w+]] = OpVariable [[uint_ptr]] Function ; CHECK: [[l0:%\w+]] = OpLoad [[uint]] [[var0]] Nontemporal -; CHECK: OpCompositeConstruct [[struct1]] [[l0]] [[null]] +; CHECK: OpCompositeConstruct [[struct1]] [[l0]] [[undef]] ; OpCapability Shader OpCapability Linkage @@ -1267,16 +1266,16 @@ TEST_F(ScalarReplacementTest, ReplaceWholeLoadAndStore) { ; CHECK: [[struct1:%\w+]] = OpTypeStruct [[uint]] [[uint]] ; CHECK: [[uint_ptr:%\w+]] = OpTypePointer Function [[uint]] ; CHECK: [[const:%\w+]] = OpConstant [[uint]] 0 -; CHECK: [[null:%\w+]] = OpConstantNull [[uint]] +; CHECK: [[undef:%\w+]] = OpUndef [[uint]] ; CHECK: [[var0:%\w+]] = OpVariable [[uint_ptr]] Function ; CHECK: [[var1:%\w+]] = OpVariable [[uint_ptr]] Function ; CHECK-NOT: OpVariable ; CHECK: [[l0:%\w+]] = OpLoad [[uint]] [[var0]] -; CHECK: [[c0:%\w+]] = OpCompositeConstruct [[struct1]] [[l0]] [[null]] +; CHECK: [[c0:%\w+]] = OpCompositeConstruct [[struct1]] [[l0]] [[undef]] ; CHECK: [[e0:%\w+]] = OpCompositeExtract [[uint]] [[c0]] 0 ; CHECK: OpStore [[var1]] [[e0]] ; CHECK: [[l1:%\w+]] = OpLoad [[uint]] [[var1]] -; CHECK: [[c1:%\w+]] = OpCompositeConstruct [[struct1]] [[l1]] [[null]] +; CHECK: [[c1:%\w+]] = OpCompositeConstruct [[struct1]] [[l1]] [[undef]] ; CHECK: [[e1:%\w+]] = OpCompositeExtract [[uint]] [[c1]] 0 ; OpCapability Shader @@ -1314,7 +1313,7 @@ TEST_F(ScalarReplacementTest, ReplaceWholeLoadAndStore2) { ; CHECK: [[struct1:%\w+]] = OpTypeStruct [[uint]] [[uint]] ; CHECK: [[uint_ptr:%\w+]] = OpTypePointer Function [[uint]] ; CHECK: [[const:%\w+]] = OpConstant [[uint]] 0 -; CHECK: [[null:%\w+]] = OpConstantNull [[uint]] +; CHECK: [[undef:%\w+]] = OpUndef [[uint]] ; CHECK: [[var1:%\w+]] = OpVariable [[uint_ptr]] Function ; CHECK: [[var0a:%\w+]] = OpVariable [[uint_ptr]] Function ; CHECK: [[var0b:%\w+]] = OpVariable [[uint_ptr]] Function @@ -1325,7 +1324,7 @@ TEST_F(ScalarReplacementTest, ReplaceWholeLoadAndStore2) { ; CHECK: [[e0:%\w+]] = OpCompositeExtract [[uint]] [[c0]] 0 ; CHECK: OpStore [[var1]] [[e0]] ; CHECK: [[l1:%\w+]] = OpLoad [[uint]] [[var1]] -; CHECK: [[c1:%\w+]] = OpCompositeConstruct [[struct1]] [[l1]] [[null]] +; CHECK: [[c1:%\w+]] = OpCompositeConstruct [[struct1]] [[l1]] [[undef]] ; CHECK: [[e1:%\w+]] = OpCompositeExtract [[uint]] [[c1]] 0 ; OpCapability Shader @@ -1362,14 +1361,14 @@ TEST_F(ScalarReplacementTest, CreateAmbiguousNullConstant1) { ; CHECK: [[struct1:%\w+]] = OpTypeStruct [[uint]] [[struct_member:%\w+]] ; CHECK: [[uint_ptr:%\w+]] = OpTypePointer Function [[uint]] ; CHECK: [[const:%\w+]] = OpConstant [[uint]] 0 -; CHECK: [[null:%\w+]] = OpConstantNull [[struct_member]] +; CHECK: [[undef:%\w+]] = OpUndef [[struct_member]] ; CHECK: [[var0a:%\w+]] = OpVariable [[uint_ptr]] Function ; CHECK: [[var1:%\w+]] = OpVariable [[uint_ptr]] Function ; CHECK: [[var0b:%\w+]] = OpVariable [[uint_ptr]] Function ; CHECK-NOT: OpVariable ; CHECK: OpStore [[var1]] ; CHECK: [[l1:%\w+]] = OpLoad [[uint]] [[var1]] -; CHECK: [[c1:%\w+]] = OpCompositeConstruct [[struct1]] [[l1]] [[null]] +; CHECK: [[c1:%\w+]] = OpCompositeConstruct [[struct1]] [[l1]] [[undef]] ; CHECK: [[e1:%\w+]] = OpCompositeExtract [[uint]] [[c1]] 0 ; OpCapability Shader @@ -1444,13 +1443,13 @@ TEST_F(ScalarReplacementTest, CreateAmbiguousNullConstant2) { ; CHECK: [[struct1:%\w+]] = OpTypeStruct [[uint]] [[struct_member:%\w+]] ; CHECK: [[uint_ptr:%\w+]] = OpTypePointer Function [[uint]] ; CHECK: [[const:%\w+]] = OpConstant [[uint]] 0 -; CHECK: [[null:%\w+]] = OpConstantNull [[struct_member]] +; CHECK: [[undef:%\w+]] = OpUndef [[struct_member]] ; CHECK: [[var0a:%\w+]] = OpVariable [[uint_ptr]] Function ; CHECK: [[var1:%\w+]] = OpVariable [[uint_ptr]] Function ; CHECK: [[var0b:%\w+]] = OpVariable [[uint_ptr]] Function ; CHECK: OpStore [[var1]] ; CHECK: [[l1:%\w+]] = OpLoad [[uint]] [[var1]] -; CHECK: [[c1:%\w+]] = OpCompositeConstruct [[struct1]] [[l1]] [[null]] +; CHECK: [[c1:%\w+]] = OpCompositeConstruct [[struct1]] [[l1]] [[undef]] ; CHECK: [[e1:%\w+]] = OpCompositeExtract [[uint]] [[c1]] 0 ; OpCapability Shader @@ -2263,6 +2262,40 @@ OpFunctionEnd SinglePassRunAndCheck<ScalarReplacementPass>(text, text, false); } +TEST_F(ScalarReplacementTest, UndefImageMember) { + // Test that scalar replacement creates an undef for a type that cannot have + // and OpConstantNull. + const std::string text = R"( +; CHECK: [[image_type:%\w+]] = OpTypeSampledImage {{%\w+}} +; CHECK: [[struct_type:%\w+]] = OpTypeStruct [[image_type]] +; CHECK: [[undef:%\w+]] = OpUndef [[image_type]] +; CHECK: {{%\w+}} = OpCompositeConstruct [[struct_type]] [[undef]] + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + %void = OpTypeVoid + %4 = OpTypeFunction %void + %float = OpTypeFloat 32 + %6 = OpTypeImage %float 2D 0 0 0 1 Unknown + %7 = OpTypeSampledImage %6 + %_struct_8 = OpTypeStruct %7 + %9 = OpTypeFunction %_struct_8 + %10 = OpUndef %_struct_8 +%_ptr_Function__struct_8 = OpTypePointer Function %_struct_8 + %2 = OpFunction %void None %4 + %11 = OpLabel + %16 = OpVariable %_ptr_Function__struct_8 Function + OpStore %16 %10 + %12 = OpLoad %_struct_8 %16 + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch<ScalarReplacementPass>(text, true); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/test/opt/set_spec_const_default_value_test.cpp b/test/opt/set_spec_const_default_value_test.cpp index f1dd50ee..10f805b6 100644 --- a/test/opt/set_spec_const_default_value_test.cpp +++ b/test/opt/set_spec_const_default_value_test.cpp @@ -618,7 +618,7 @@ INSTANTIATE_TEST_SUITE_P( {"", SpecIdToValueBitPatternMap{}, ""}, // 1. Empty with non-empty values to set. {"", SpecIdToValueBitPatternMap{{1, {100}}, {2, {200}}}, ""}, - // 2. Baisc bool type. + // 2. Basic bool type. { // code "OpDecorate %1 SpecId 100\n" diff --git a/test/opt/spread_volatile_semantics_test.cpp b/test/opt/spread_volatile_semantics_test.cpp new file mode 100644 index 00000000..fdabd923 --- /dev/null +++ b/test/opt/spread_volatile_semantics_test.cpp @@ -0,0 +1,1138 @@ +// Copyright (c) 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <string> + +#include "gmock/gmock.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +struct ExecutionModelAndBuiltIn { + const char* execution_model; + const char* built_in; + const bool use_v4uint; +}; + +using AddVolatileDecorationTest = + PassTest<::testing::TestWithParam<ExecutionModelAndBuiltIn>>; + +TEST_P(AddVolatileDecorationTest, InMain) { + const auto& tc = GetParam(); + const std::string execution_model(tc.execution_model); + const std::string built_in(tc.built_in); + const std::string var_type = + tc.use_v4uint ? "%_ptr_Input_v4uint" : "%_ptr_Input_uint"; + const std::string var_load_type = tc.use_v4uint ? "%v4uint" : "%uint"; + + const std::string text = + std::string(R"(OpCapability RuntimeDescriptorArray +OpCapability RayTracingKHR +OpCapability SubgroupBallotKHR +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_KHR_ray_tracing" +OpExtension "SPV_KHR_shader_ballot" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint )") + + execution_model + std::string(R"( %main "main" %var +OpSource GLSL 460 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpSourceExtension "GL_KHR_ray_tracing" +OpName %main "main" +OpName %StorageBuffer "StorageBuffer" +OpMemberName %StorageBuffer 0 "index" +OpMemberName %StorageBuffer 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %StorageBuffer 0 Offset 0 +OpMemberDecorate %StorageBuffer 1 Offset 4 +OpDecorate %StorageBuffer BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable +)") + std::string(R"( +; CHECK: OpDecorate [[var:%\w+]] BuiltIn )") + + built_in + std::string(R"( +; CHECK: OpDecorate [[var]] Volatile +OpDecorate %var BuiltIn )") + built_in + std::string(R"( +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%StorageBuffer = OpTypeStruct %uint %float +%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer +%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%_ptr_Input_uint = OpTypePointer Input %uint +%v4uint = OpTypeVector %uint 4 +%_ptr_Input_v4uint = OpTypePointer Input %v4uint +%var = OpVariable )") + + var_type + std::string(R"( Input +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%25 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%main = OpFunction %void None %3 +%5 = OpLabel +%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%20 = OpLoad %uint %19 +%load = OpLoad )") + var_load_type + std::string(R"( %var +%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20 +%23 = OpLoad %13 %22 +%27 = OpImageRead %v4float %23 %25 +%29 = OpCompositeExtract %float %27 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +OpStore %31 %29 +OpReturn +OpFunctionEnd +)"); + + SinglePassRunAndMatch<SpreadVolatileSemantics>(text, true); +} + +INSTANTIATE_TEST_SUITE_P( + AddVolatileDecoration, AddVolatileDecorationTest, + ::testing::ValuesIn(std::vector<ExecutionModelAndBuiltIn>{ + {"RayGenerationKHR", "SubgroupSize", false}, + {"RayGenerationKHR", "SubgroupLocalInvocationId", false}, + {"RayGenerationKHR", "SubgroupEqMask", true}, + {"ClosestHitKHR", "SubgroupLocalInvocationId", true}, + {"IntersectionKHR", "SubgroupEqMask", true}, + {"MissKHR", "SubgroupGeMask", true}, + {"CallableKHR", "SubgroupGtMask", true}, + {"RayGenerationKHR", "SubgroupLeMask", true}, + })); + +using SetLoadVolatileTest = + PassTest<::testing::TestWithParam<ExecutionModelAndBuiltIn>>; + +TEST_P(SetLoadVolatileTest, InMain) { + const auto& tc = GetParam(); + const std::string execution_model(tc.execution_model); + const std::string built_in(tc.built_in); + + const std::string var_type = + tc.use_v4uint ? "%_ptr_Input_v4uint" : "%_ptr_Input_uint"; + const std::string var_value = tc.use_v4uint ? std::string(R"( +; CHECK: [[ptr:%\w+]] = OpAccessChain %_ptr_Input_uint [[var]] %int_0 +; CHECK: OpLoad {{%\w+}} [[ptr]] Volatile +%ptr = OpAccessChain %_ptr_Input_uint %var %int_0 +%var_value = OpLoad %uint %ptr)") + : std::string(R"( +; CHECK: OpLoad {{%\w+}} [[var]] Volatile +%var_value = OpLoad %uint %var)"); + + const std::string text = std::string(R"(OpCapability RuntimeDescriptorArray +OpCapability RayTracingKHR +OpCapability SubgroupBallotKHR +OpCapability VulkanMemoryModel +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_KHR_ray_tracing" +OpExtension "SPV_KHR_shader_ballot" +OpMemoryModel Logical Vulkan +OpEntryPoint )") + execution_model + + std::string(R"( %main "main" %var +OpName %main "main" +OpName %StorageBuffer "StorageBuffer" +OpMemberName %StorageBuffer 0 "index" +OpMemberName %StorageBuffer 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %StorageBuffer 0 Offset 0 +OpMemberDecorate %StorageBuffer 1 Offset 4 +OpDecorate %StorageBuffer BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable +)") + std::string(R"( +; CHECK: OpDecorate [[var:%\w+]] BuiltIn )") + + built_in + std::string(R"( +OpDecorate %var BuiltIn )") + built_in + + std::string(R"( +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%StorageBuffer = OpTypeStruct %uint %float +%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer +%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%_ptr_Input_uint = OpTypePointer Input %uint +%v4uint = OpTypeVector %uint 4 +%_ptr_Input_v4uint = OpTypePointer Input %v4uint +%var = OpVariable )") + var_type + + std::string(R"( Input +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%25 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%main = OpFunction %void None %3 +%5 = OpLabel +%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%20 = OpLoad %uint %19 +)") + var_value + std::string(R"( +%test = OpIAdd %uint %var_value %20 +%22 = OpAccessChain %_ptr_UniformConstant_13 %images %test +%23 = OpLoad %13 %22 +%27 = OpImageRead %v4float %23 %25 +%29 = OpCompositeExtract %float %27 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +OpStore %31 %29 +OpReturn +OpFunctionEnd +)"); + + SinglePassRunAndMatch<SpreadVolatileSemantics>(text, true); +} + +INSTANTIATE_TEST_SUITE_P( + SetLoadVolatile, SetLoadVolatileTest, + ::testing::ValuesIn(std::vector<ExecutionModelAndBuiltIn>{ + {"RayGenerationKHR", "SubgroupSize", false}, + {"RayGenerationKHR", "SubgroupLocalInvocationId", false}, + {"RayGenerationKHR", "SubgroupEqMask", true}, + {"ClosestHitKHR", "SubgroupLocalInvocationId", true}, + {"IntersectionKHR", "SubgroupEqMask", true}, + {"MissKHR", "SubgroupGeMask", true}, + {"CallableKHR", "SubgroupGtMask", true}, + {"RayGenerationKHR", "SubgroupLeMask", true}, + })); + +using VolatileSpreadTest = PassTest<::testing::Test>; + +TEST_F(VolatileSpreadTest, SpreadVolatileForHelperInvocation) { + const std::string text = + R"( +OpCapability Shader +OpCapability DemoteToHelperInvocation +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %var +OpExecutionMode %main OriginUpperLeft + +; CHECK: OpDecorate [[var:%\w+]] BuiltIn HelperInvocation +; CHECK: OpDecorate [[var]] Volatile +OpDecorate %var BuiltIn HelperInvocation + +%bool = OpTypeBool +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%_ptr_Input_bool = OpTypePointer Input %bool +%var = OpVariable %_ptr_Input_bool Input +%main = OpFunction %void None %void_fn +%entry = OpLabel +%load = OpLoad %bool %var +OpDemoteToHelperInvocation +OpReturn +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_UNIVERSAL_1_6); + SinglePassRunAndMatch<SpreadVolatileSemantics>(text, true); +} + +TEST_F(VolatileSpreadTest, MultipleExecutionModel) { + const std::string text = + R"( +OpCapability RuntimeDescriptorArray +OpCapability RayTracingKHR +OpCapability SubgroupBallotKHR +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_KHR_ray_tracing" +OpExtension "SPV_KHR_shader_ballot" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint RayGenerationKHR %RayGeneration "RayGeneration" %var +OpEntryPoint GLCompute %compute "Compute" %gl_LocalInvocationIndex +OpExecutionMode %compute LocalSize 16 16 1 +OpSource GLSL 460 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpSourceExtension "GL_KHR_ray_tracing" +OpName %RayGeneration "RayGeneration" +OpName %StorageBuffer "StorageBuffer" +OpMemberName %StorageBuffer 0 "index" +OpMemberName %StorageBuffer 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %StorageBuffer 0 Offset 0 +OpMemberDecorate %StorageBuffer 1 Offset 4 +OpDecorate %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex +OpDecorate %StorageBuffer BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable + +; CHECK: OpEntryPoint RayGenerationNV {{%\w+}} "RayGeneration" [[var:%\w+]] +; CHECK: OpDecorate [[var]] BuiltIn SubgroupSize +; CHECK: OpDecorate [[var]] Volatile +; CHECK-NOT: OpDecorate {{%\w+}} Volatile +OpDecorate %var BuiltIn SubgroupSize + +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%StorageBuffer = OpTypeStruct %uint %float +%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer +%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%_ptr_Input_uint = OpTypePointer Input %uint +%var = OpVariable %_ptr_Input_uint Input +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%25 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%gl_LocalInvocationIndex = OpVariable %_ptr_Input_uint Input +%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint +%shared = OpVariable %_ptr_Workgroup_uint Workgroup + +%RayGeneration = OpFunction %void None %3 +%5 = OpLabel +%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%20 = OpLoad %uint %var +%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20 +%23 = OpLoad %13 %22 +%27 = OpImageRead %v4float %23 %25 +%29 = OpCompositeExtract %float %27 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +OpStore %31 %29 +OpReturn +OpFunctionEnd + +%compute = OpFunction %void None %3 +%66 = OpLabel +%62 = OpLoad %uint %gl_LocalInvocationIndex +%61 = OpAtomicIAdd %uint %shared %uint_1 %uint_0 %62 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch<SpreadVolatileSemantics>(text, true); +} + +TEST_F(VolatileSpreadTest, VarUsedInMultipleEntryPoints) { + const std::string text = + R"( +OpCapability RuntimeDescriptorArray +OpCapability RayTracingKHR +OpCapability SubgroupBallotKHR +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_KHR_ray_tracing" +OpExtension "SPV_KHR_shader_ballot" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint RayGenerationKHR %RayGeneration "RayGeneration" %var +OpEntryPoint ClosestHitKHR %ClosestHit "ClosestHit" %var +OpSource GLSL 460 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpSourceExtension "GL_KHR_ray_tracing" +OpName %RayGeneration "RayGeneration" +OpName %ClosestHit "ClosestHit" +OpName %StorageBuffer "StorageBuffer" +OpMemberName %StorageBuffer 0 "index" +OpMemberName %StorageBuffer 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %StorageBuffer 0 Offset 0 +OpMemberDecorate %StorageBuffer 1 Offset 4 +OpDecorate %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex +OpDecorate %StorageBuffer BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable + +; CHECK: OpEntryPoint RayGenerationNV {{%\w+}} "RayGeneration" [[var:%\w+]] +; CHECK: OpEntryPoint ClosestHitNV {{%\w+}} "ClosestHit" [[var]] +; CHECK: OpDecorate [[var]] BuiltIn SubgroupSize +; CHECK: OpDecorate [[var]] Volatile +; CHECK-NOT: OpDecorate {{%\w+}} Volatile +OpDecorate %var BuiltIn SubgroupSize + +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%StorageBuffer = OpTypeStruct %uint %float +%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer +%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%_ptr_Input_uint = OpTypePointer Input %uint +%var = OpVariable %_ptr_Input_uint Input +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%25 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%gl_LocalInvocationIndex = OpVariable %_ptr_Input_uint Input +%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint +%shared = OpVariable %_ptr_Workgroup_uint Workgroup + +%RayGeneration = OpFunction %void None %3 +%5 = OpLabel +%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%20 = OpLoad %uint %var +%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20 +%23 = OpLoad %13 %22 +%27 = OpImageRead %v4float %23 %25 +%29 = OpCompositeExtract %float %27 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +OpStore %31 %29 +OpReturn +OpFunctionEnd + +%ClosestHit = OpFunction %void None %3 +%45 = OpLabel +%49 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%40 = OpLoad %uint %var +%42 = OpAccessChain %_ptr_UniformConstant_13 %images %40 +%43 = OpLoad %13 %42 +%47 = OpImageRead %v4float %43 %25 +%59 = OpCompositeExtract %float %47 0 +%51 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +OpStore %51 %59 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch<SpreadVolatileSemantics>(text, true); +} + +class VolatileSpreadErrorTest : public PassTest<::testing::Test> { + public: + VolatileSpreadErrorTest() + : consumer_([this](spv_message_level_t level, const char*, + const spv_position_t& position, const char* message) { + if (!error_message_.empty()) error_message_ += "\n"; + switch (level) { + case SPV_MSG_FATAL: + case SPV_MSG_INTERNAL_ERROR: + case SPV_MSG_ERROR: + error_message_ += "ERROR"; + break; + case SPV_MSG_WARNING: + error_message_ += "WARNING"; + break; + case SPV_MSG_INFO: + error_message_ += "INFO"; + break; + case SPV_MSG_DEBUG: + error_message_ += "DEBUG"; + break; + } + error_message_ += + ": " + std::to_string(position.index) + ": " + message; + }) {} + + Pass::Status RunPass(const std::string& text) { + std::unique_ptr<IRContext> context_ = + spvtools::BuildModule(SPV_ENV_UNIVERSAL_1_2, consumer_, text); + if (!context_.get()) return Pass::Status::Failure; + + PassManager manager; + manager.SetMessageConsumer(consumer_); + manager.AddPass<SpreadVolatileSemantics>(); + + return manager.Run(context_.get()); + } + + std::string GetErrorMessage() const { return error_message_; } + + void TearDown() override { error_message_.clear(); } + + private: + spvtools::MessageConsumer consumer_; + std::string error_message_; +}; + +TEST_F(VolatileSpreadErrorTest, VarUsedInMultipleExecutionModelError) { + const std::string text = + R"( +OpCapability RuntimeDescriptorArray +OpCapability RayTracingKHR +OpCapability SubgroupBallotKHR +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_KHR_ray_tracing" +OpExtension "SPV_KHR_shader_ballot" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint RayGenerationKHR %RayGeneration "RayGeneration" %var +OpEntryPoint GLCompute %compute "Compute" %gl_LocalInvocationIndex %var +OpExecutionMode %compute LocalSize 16 16 1 +OpSource GLSL 460 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpSourceExtension "GL_KHR_ray_tracing" +OpName %RayGeneration "RayGeneration" +OpName %StorageBuffer "StorageBuffer" +OpMemberName %StorageBuffer 0 "index" +OpMemberName %StorageBuffer 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %StorageBuffer 0 Offset 0 +OpMemberDecorate %StorageBuffer 1 Offset 4 +OpDecorate %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex +OpDecorate %StorageBuffer BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable +OpDecorate %var BuiltIn SubgroupSize +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%StorageBuffer = OpTypeStruct %uint %float +%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer +%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%_ptr_Input_uint = OpTypePointer Input %uint +%var = OpVariable %_ptr_Input_uint Input +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%25 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%gl_LocalInvocationIndex = OpVariable %_ptr_Input_uint Input +%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint +%shared = OpVariable %_ptr_Workgroup_uint Workgroup + +%RayGeneration = OpFunction %void None %3 +%5 = OpLabel +%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%20 = OpLoad %uint %var +%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20 +%23 = OpLoad %13 %22 +%27 = OpImageRead %v4float %23 %25 +%29 = OpCompositeExtract %float %27 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +OpStore %31 %29 +OpReturn +OpFunctionEnd + +%compute = OpFunction %void None %3 +%66 = OpLabel +%62 = OpLoad %uint %gl_LocalInvocationIndex +%63 = OpLoad %uint %var +%64 = OpIAdd %uint %62 %63 +%61 = OpAtomicIAdd %uint %shared %uint_1 %uint_0 %64 +OpReturn +OpFunctionEnd +)"; + + EXPECT_EQ(RunPass(text), Pass::Status::Failure); + const char expected_error[] = + "ERROR: 0: Variable is a target for Volatile semantics for an entry " + "point, but it is not for another entry point"; + EXPECT_STREQ(GetErrorMessage().substr(0, sizeof(expected_error) - 1).c_str(), + expected_error); +} + +TEST_F(VolatileSpreadErrorTest, + VarUsedInMultipleReverseOrderExecutionModelError) { + const std::string text = + R"( +OpCapability RuntimeDescriptorArray +OpCapability RayTracingKHR +OpCapability SubgroupBallotKHR +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_KHR_ray_tracing" +OpExtension "SPV_KHR_shader_ballot" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %compute "Compute" %gl_LocalInvocationIndex %var +OpEntryPoint RayGenerationKHR %RayGeneration "RayGeneration" %var +OpExecutionMode %compute LocalSize 16 16 1 +OpSource GLSL 460 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpSourceExtension "GL_KHR_ray_tracing" +OpName %RayGeneration "RayGeneration" +OpName %StorageBuffer "StorageBuffer" +OpMemberName %StorageBuffer 0 "index" +OpMemberName %StorageBuffer 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %StorageBuffer 0 Offset 0 +OpMemberDecorate %StorageBuffer 1 Offset 4 +OpDecorate %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex +OpDecorate %StorageBuffer BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable +OpDecorate %var BuiltIn SubgroupSize +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%StorageBuffer = OpTypeStruct %uint %float +%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer +%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%_ptr_Input_uint = OpTypePointer Input %uint +%var = OpVariable %_ptr_Input_uint Input +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%25 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%gl_LocalInvocationIndex = OpVariable %_ptr_Input_uint Input +%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint +%shared = OpVariable %_ptr_Workgroup_uint Workgroup + +%RayGeneration = OpFunction %void None %3 +%5 = OpLabel +%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%20 = OpLoad %uint %var +%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20 +%23 = OpLoad %13 %22 +%27 = OpImageRead %v4float %23 %25 +%29 = OpCompositeExtract %float %27 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +OpStore %31 %29 +OpReturn +OpFunctionEnd + +%compute = OpFunction %void None %3 +%66 = OpLabel +%62 = OpLoad %uint %gl_LocalInvocationIndex +%63 = OpLoad %uint %var +%64 = OpIAdd %uint %62 %63 +%61 = OpAtomicIAdd %uint %shared %uint_1 %uint_0 %64 +OpReturn +OpFunctionEnd +)"; + + EXPECT_EQ(RunPass(text), Pass::Status::Failure); + const char expected_error[] = + "ERROR: 0: Variable is a target for Volatile semantics for an entry " + "point, but it is not for another entry point"; + EXPECT_STREQ(GetErrorMessage().substr(0, sizeof(expected_error) - 1).c_str(), + expected_error); +} + +TEST_F(VolatileSpreadErrorTest, FunctionNotInlined) { + const std::string text = + R"( +OpCapability RuntimeDescriptorArray +OpCapability RayTracingKHR +OpCapability SubgroupBallotKHR +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_KHR_ray_tracing" +OpExtension "SPV_KHR_shader_ballot" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint RayGenerationKHR %RayGeneration "RayGeneration" %var +OpEntryPoint ClosestHitKHR %ClosestHit "ClosestHit" %var +OpSource GLSL 460 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpSourceExtension "GL_KHR_ray_tracing" +OpName %RayGeneration "RayGeneration" +OpName %ClosestHit "ClosestHit" +OpName %StorageBuffer "StorageBuffer" +OpMemberName %StorageBuffer 0 "index" +OpMemberName %StorageBuffer 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %StorageBuffer 0 Offset 0 +OpMemberDecorate %StorageBuffer 1 Offset 4 +OpDecorate %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex +OpDecorate %StorageBuffer BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable +OpDecorate %var BuiltIn SubgroupSize +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%StorageBuffer = OpTypeStruct %uint %float +%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer +%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%_ptr_Input_uint = OpTypePointer Input %uint +%var = OpVariable %_ptr_Input_uint Input +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%25 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%gl_LocalInvocationIndex = OpVariable %_ptr_Input_uint Input +%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint +%shared = OpVariable %_ptr_Workgroup_uint Workgroup + +%RayGeneration = OpFunction %void None %3 +%5 = OpLabel +%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%20 = OpLoad %uint %19 +%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20 +%23 = OpLoad %13 %22 +%27 = OpImageRead %v4float %23 %25 +%29 = OpCompositeExtract %float %27 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +OpStore %31 %29 +OpReturn +OpFunctionEnd + +%NotInlined = OpFunction %void None %3 +%32 = OpLabel +OpReturn +OpFunctionEnd + +%ClosestHit = OpFunction %void None %3 +%45 = OpLabel +%49 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%40 = OpLoad %uint %49 +%42 = OpAccessChain %_ptr_UniformConstant_13 %images %40 +%43 = OpLoad %13 %42 +%47 = OpImageRead %v4float %43 %25 +%59 = OpCompositeExtract %float %47 0 +%51 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +OpStore %51 %59 +OpReturn +OpFunctionEnd +)"; + + EXPECT_EQ(RunPass(text), Pass::Status::Failure); + const char expected_error[] = + "ERROR: 0: Functions of SPIR-V for spread-volatile-semantics pass " + "input must be inlined except entry points"; + EXPECT_STREQ(GetErrorMessage().substr(0, sizeof(expected_error) - 1).c_str(), + expected_error); +} + +TEST_F(VolatileSpreadErrorTest, VarNotUsedInEntryPointForVolatile) { + const std::string text = + R"( +OpCapability RuntimeDescriptorArray +OpCapability RayTracingKHR +OpCapability SubgroupBallotKHR +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_KHR_ray_tracing" +OpExtension "SPV_KHR_shader_ballot" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint RayGenerationKHR %RayGeneration "RayGeneration" %var +OpEntryPoint GLCompute %compute "Compute" %gl_LocalInvocationIndex %var +OpExecutionMode %compute LocalSize 16 16 1 +OpSource GLSL 460 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpSourceExtension "GL_KHR_ray_tracing" +OpName %RayGeneration "RayGeneration" +OpName %StorageBuffer "StorageBuffer" +OpMemberName %StorageBuffer 0 "index" +OpMemberName %StorageBuffer 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %StorageBuffer 0 Offset 0 +OpMemberDecorate %StorageBuffer 1 Offset 4 +OpDecorate %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex +OpDecorate %StorageBuffer BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable + +; CHECK-NOT: OpDecorate {{%\w+}} Volatile + +OpDecorate %var BuiltIn SubgroupSize +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%StorageBuffer = OpTypeStruct %uint %float +%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer +%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%_ptr_Input_uint = OpTypePointer Input %uint +%var = OpVariable %_ptr_Input_uint Input +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%25 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%gl_LocalInvocationIndex = OpVariable %_ptr_Input_uint Input +%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint +%shared = OpVariable %_ptr_Workgroup_uint Workgroup + +%RayGeneration = OpFunction %void None %3 +%5 = OpLabel +%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%20 = OpLoad %uint %19 +%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20 +%23 = OpLoad %13 %22 +%27 = OpImageRead %v4float %23 %25 +%29 = OpCompositeExtract %float %27 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +OpStore %31 %29 +OpReturn +OpFunctionEnd + +%compute = OpFunction %void None %3 +%66 = OpLabel +%62 = OpLoad %uint %gl_LocalInvocationIndex +%63 = OpLoad %uint %var +%64 = OpIAdd %uint %62 %63 +%61 = OpAtomicIAdd %uint %shared %uint_1 %uint_0 %64 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch<SpreadVolatileSemantics>(text, true); +} + +TEST_F(VolatileSpreadTest, RecursivelySpreadVolatile) { + const std::string text = + R"( +OpCapability RuntimeDescriptorArray +OpCapability RayTracingKHR +OpCapability SubgroupBallotKHR +OpCapability VulkanMemoryModel +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_KHR_ray_tracing" +OpExtension "SPV_KHR_shader_ballot" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical Vulkan +OpEntryPoint RayGenerationKHR %RayGeneration "RayGeneration" %var0 %var1 +OpSource GLSL 460 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpSourceExtension "GL_KHR_ray_tracing" +OpName %RayGeneration "RayGeneration" +OpName %StorageBuffer "StorageBuffer" +OpMemberName %StorageBuffer 0 "index" +OpMemberName %StorageBuffer 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %StorageBuffer 0 Offset 0 +OpMemberDecorate %StorageBuffer 1 Offset 4 +OpDecorate %StorageBuffer BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable + +; CHECK: OpDecorate [[var0:%\w+]] BuiltIn SubgroupEqMask +; CHECK: OpDecorate [[var1:%\w+]] BuiltIn SubgroupGeMask +OpDecorate %var0 BuiltIn SubgroupEqMask +OpDecorate %var1 BuiltIn SubgroupGeMask + +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%StorageBuffer = OpTypeStruct %uint %float +%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer +%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%v4uint = OpTypeVector %uint 4 +%_ptr_Input_v4uint = OpTypePointer Input %v4uint +%_ptr_Input_uint = OpTypePointer Input %uint +%var0 = OpVariable %_ptr_Input_v4uint Input +%var1 = OpVariable %_ptr_Input_v4uint Input +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%25 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%_ptr_Uniform_float = OpTypePointer Uniform %float + +%RayGeneration = OpFunction %void None %3 +%5 = OpLabel + +; CHECK: [[ptr0:%\w+]] = OpAccessChain %_ptr_Input_uint [[var0]] %int_0 +; CHECK: OpLoad {{%\w+}} [[ptr0]] Volatile +%19 = OpAccessChain %_ptr_Input_uint %var0 %int_0 +%20 = OpLoad %uint %19 + +%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20 +%23 = OpLoad %13 %22 +%27 = OpImageRead %v4float %23 %25 +%29 = OpCompositeExtract %float %27 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %uint_1 + +; CHECK: OpLoad {{%\w+}} [[ptr0]] Volatile +%24 = OpLoad %uint %19 + +; CHECK: [[var2:%\w+]] = OpCopyObject %_ptr_Input_v4uint [[var0]] +; CHECK: [[ptr2:%\w+]] = OpAccessChain %_ptr_Input_uint [[var2]] %int_1 +; CHECK: OpLoad {{%\w+}} [[ptr2]] Volatile +%18 = OpCopyObject %_ptr_Input_v4uint %var0 +%21 = OpAccessChain %_ptr_Input_uint %18 %int_1 +%26 = OpLoad %uint %21 + +%28 = OpIAdd %uint %24 %26 +%30 = OpConvertUToF %float %28 + +; CHECK: [[ptr1:%\w+]] = OpAccessChain %_ptr_Input_uint [[var1]] %int_1 +; CHECK: OpLoad {{%\w+}} [[ptr1]] Volatile +%32 = OpAccessChain %_ptr_Input_uint %var1 %int_1 +%33 = OpLoad %uint %32 + +%34 = OpConvertUToF %float %33 +%35 = OpFAdd %float %34 %30 +%36 = OpFAdd %float %35 %29 +OpStore %31 %36 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch<SpreadVolatileSemantics>(text, true); +} + +TEST_F(VolatileSpreadTest, SpreadVolatileOnlyForTargetEntryPoints) { + const std::string text = + R"( +OpCapability RuntimeDescriptorArray +OpCapability RayTracingKHR +OpCapability SubgroupBallotKHR +OpCapability VulkanMemoryModel +OpCapability VulkanMemoryModelDeviceScopeKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_KHR_ray_tracing" +OpExtension "SPV_KHR_shader_ballot" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical Vulkan +OpEntryPoint RayGenerationKHR %RayGeneration "RayGeneration" %var0 %var1 +OpEntryPoint GLCompute %compute "Compute" %var0 %var1 +OpExecutionMode %compute LocalSize 16 16 1 +OpSource GLSL 460 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpSourceExtension "GL_KHR_ray_tracing" +OpName %RayGeneration "RayGeneration" +OpName %StorageBuffer "StorageBuffer" +OpMemberName %StorageBuffer 0 "index" +OpMemberName %StorageBuffer 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %StorageBuffer 0 Offset 0 +OpMemberDecorate %StorageBuffer 1 Offset 4 +OpDecorate %StorageBuffer BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable + +; CHECK: OpDecorate [[var0:%\w+]] BuiltIn SubgroupEqMask +; CHECK: OpDecorate [[var1:%\w+]] BuiltIn SubgroupGeMask +OpDecorate %var0 BuiltIn SubgroupEqMask +OpDecorate %var1 BuiltIn SubgroupGeMask + +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%StorageBuffer = OpTypeStruct %uint %float +%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer +%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%v4uint = OpTypeVector %uint 4 +%_ptr_Input_v4uint = OpTypePointer Input %v4uint +%_ptr_Input_uint = OpTypePointer Input %uint +%var0 = OpVariable %_ptr_Input_v4uint Input +%var1 = OpVariable %_ptr_Input_v4uint Input +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%25 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint +%shared = OpVariable %_ptr_Workgroup_uint Workgroup + +%RayGeneration = OpFunction %void None %3 +%5 = OpLabel + +; CHECK: [[ptr0:%\w+]] = OpAccessChain %_ptr_Input_uint [[var0]] %int_0 +; CHECK: OpLoad {{%\w+}} [[ptr0]] Volatile +%19 = OpAccessChain %_ptr_Input_uint %var0 %int_0 +%20 = OpLoad %uint %19 + +%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20 +%23 = OpLoad %13 %22 +%27 = OpImageRead %v4float %23 %25 +%29 = OpCompositeExtract %float %27 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %uint_1 + +; CHECK: OpLoad {{%\w+}} [[ptr0]] Volatile +%24 = OpLoad %uint %19 + +; CHECK: [[var2:%\w+]] = OpCopyObject %_ptr_Input_v4uint [[var0]] +; CHECK: [[ptr2:%\w+]] = OpAccessChain %_ptr_Input_uint [[var2]] %int_1 +; CHECK: OpLoad {{%\w+}} [[ptr2]] Volatile +%18 = OpCopyObject %_ptr_Input_v4uint %var0 +%21 = OpAccessChain %_ptr_Input_uint %18 %int_1 +%26 = OpLoad %uint %21 + +%28 = OpIAdd %uint %24 %26 +%30 = OpConvertUToF %float %28 + +; CHECK: [[ptr1:%\w+]] = OpAccessChain %_ptr_Input_uint [[var1]] %int_1 +; CHECK: OpLoad {{%\w+}} [[ptr1]] Volatile +%32 = OpAccessChain %_ptr_Input_uint %var1 %int_1 +%33 = OpLoad %uint %32 + +%34 = OpConvertUToF %float %33 +%35 = OpFAdd %float %34 %30 +%36 = OpFAdd %float %35 %29 +OpStore %31 %36 +OpReturn +OpFunctionEnd + +%compute = OpFunction %void None %3 +%66 = OpLabel + +; CHECK-NOT: OpLoad {{%\w+}} {{%\w+}} Volatile +%62 = OpLoad %v4uint %var0 +%63 = OpLoad %v4uint %var1 +%64 = OpIAdd %v4uint %62 %63 +%65 = OpCompositeExtract %uint %64 0 +%61 = OpAtomicIAdd %uint %shared %uint_1 %uint_0 %65 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch<SpreadVolatileSemantics>(text, true); +} + +TEST_F(VolatileSpreadTest, SkipIfItHasNoExecutionModel) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%4 = OpFunction %2 None %3 +%5 = OpLabel +OpReturn +OpFunctionEnd +)"; + + Pass::Status status; + std::tie(std::ignore, status) = + SinglePassRunToBinary<SpreadVolatileSemantics>(text, + /* skip_nop = */ false); + EXPECT_EQ(status, Pass::Status::SuccessWithoutChange); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/test/opt/strip_reflect_info_test.cpp b/test/opt/strip_nonsemantic_info_test.cpp index f3fc115a..3aacffa3 100644 --- a/test/opt/strip_reflect_info_test.cpp +++ b/test/opt/strip_nonsemantic_info_test.cpp @@ -13,10 +13,9 @@ // limitations under the License. #include <string> -#include "gmock/gmock.h" +#include "gmock/gmock.h" #include "spirv-tools/optimizer.hpp" - #include "test/opt/pass_fixture.h" #include "test/opt/pass_utils.h" @@ -24,7 +23,6 @@ namespace spvtools { namespace opt { namespace { -using StripLineReflectInfoTest = PassTest<::testing::Test>; using StripNonSemanticInfoTest = PassTest<::testing::Test>; // This test acts as an end-to-end code example on how to strip @@ -33,7 +31,7 @@ using StripNonSemanticInfoTest = PassTest<::testing::Test>; // option -fhlsl_functionality1 to insert reflection information, // but then want to filter out the extra instructions before sending // it to a driver that does not implement VK_GOOGLE_hlsl_functionality1. -TEST_F(StripLineReflectInfoTest, StripReflectEnd2EndExample) { +TEST_F(StripNonSemanticInfoTest, StripReflectEnd2EndExample) { // This is a non-sensical example, but exercises the instructions. std::string before = R"(OpCapability Shader OpCapability Linkage @@ -49,11 +47,11 @@ OpDecorateStringGOOGLE %void HlslSemanticGOOGLE "my goodness" std::vector<uint32_t> binary_in; tools.Assemble(before, &binary_in); - // Instantiate the optimizer, and run the strip-reflection-info + // Instantiate the optimizer, and run the strip-nonsemantic-info // pass over the |binary_in| module, and place the modified module // into |binary_out|. spvtools::Optimizer optimizer(SPV_ENV_UNIVERSAL_1_1); - optimizer.RegisterPass(spvtools::CreateStripReflectInfoPass()); + optimizer.RegisterPass(spvtools::CreateStripNonSemanticInfoPass()); std::vector<uint32_t> binary_out; optimizer.Run(binary_in.data(), binary_in.size(), &binary_out); @@ -71,7 +69,7 @@ OpMemoryModel Logical Simple // This test is functionally the same as the end-to-end test above, // but uses the test SinglePassRunAndCheck test fixture instead. -TEST_F(StripLineReflectInfoTest, StripHlslSemantic) { +TEST_F(StripNonSemanticInfoTest, StripHlslSemantic) { // This is a non-sensical example, but exercises the instructions. std::string before = R"(OpCapability Shader OpCapability Linkage @@ -90,10 +88,10 @@ OpMemoryModel Logical Simple %float = OpTypeFloat 32 )"; - SinglePassRunAndCheck<StripReflectInfoPass>(before, after, false); + SinglePassRunAndCheck<StripNonSemanticInfoPass>(before, after, false); } -TEST_F(StripLineReflectInfoTest, StripHlslCounterBuffer) { +TEST_F(StripNonSemanticInfoTest, StripHlslCounterBuffer) { std::string before = R"(OpCapability Shader OpCapability Linkage OpExtension "SPV_GOOGLE_hlsl_functionality1" @@ -109,10 +107,10 @@ OpMemoryModel Logical Simple %float = OpTypeFloat 32 )"; - SinglePassRunAndCheck<StripReflectInfoPass>(before, after, false); + SinglePassRunAndCheck<StripNonSemanticInfoPass>(before, after, false); } -TEST_F(StripLineReflectInfoTest, StripHlslSemanticOnMember) { +TEST_F(StripNonSemanticInfoTest, StripHlslSemanticOnMember) { // This is a non-sensical example, but exercises the instructions. std::string before = R"(OpCapability Shader OpCapability Linkage @@ -130,7 +128,7 @@ OpMemoryModel Logical Simple %_struct_3 = OpTypeStruct %float )"; - SinglePassRunAndCheck<StripReflectInfoPass>(before, after, false); + SinglePassRunAndCheck<StripNonSemanticInfoPass>(before, after, false); } TEST_F(StripNonSemanticInfoTest, StripNonSemanticImport) { @@ -144,7 +142,7 @@ OpExtension "SPV_KHR_non_semantic_info" OpMemoryModel Logical GLSL450 )"; - SinglePassRunAndMatch<StripReflectInfoPass>(text, true); + SinglePassRunAndMatch<StripNonSemanticInfoPass>(text, true); } TEST_F(StripNonSemanticInfoTest, StripNonSemanticGlobal) { @@ -159,7 +157,7 @@ OpMemoryModel Logical GLSL450 %1 = OpExtInst %void %ext 1 )"; - SinglePassRunAndMatch<StripReflectInfoPass>(text, true); + SinglePassRunAndMatch<StripNonSemanticInfoPass>(text, true); } TEST_F(StripNonSemanticInfoTest, StripNonSemanticInFunction) { @@ -179,7 +177,7 @@ OpReturn OpFunctionEnd )"; - SinglePassRunAndMatch<StripReflectInfoPass>(text, true); + SinglePassRunAndMatch<StripNonSemanticInfoPass>(text, true); } TEST_F(StripNonSemanticInfoTest, StripNonSemanticAfterFunction) { @@ -199,7 +197,7 @@ OpFunctionEnd %1 = OpExtInst %void %ext 1 %foo )"; - SinglePassRunAndMatch<StripReflectInfoPass>(text, true); + SinglePassRunAndMatch<StripNonSemanticInfoPass>(text, true); } TEST_F(StripNonSemanticInfoTest, StripNonSemanticBetweenFunctions) { @@ -223,7 +221,74 @@ OpReturn OpFunctionEnd )"; - SinglePassRunAndMatch<StripReflectInfoPass>(text, true); + SinglePassRunAndMatch<StripNonSemanticInfoPass>(text, true); +} + +// Make sure that strip reflect does not remove the debug info (OpString and +// OpLine). +TEST_F(StripNonSemanticInfoTest, DontStripDebug) { + std::string text = R"(OpCapability Shader +OpMemoryModel Logical Simple +OpEntryPoint Fragment %1 "main" +OpExecutionMode %1 OriginUpperLeft +%2 = OpString "file" +%void = OpTypeVoid +%4 = OpTypeFunction %void +%1 = OpFunction %void None %4 +%5 = OpLabel +OpLine %2 1 1 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck<StripNonSemanticInfoPass>(text, text, false); +} + +TEST_F(StripNonSemanticInfoTest, RemovedNonSemanticDebugInfo) { + const std::string text = R"( +;CHECK-NOT: OpExtension "SPV_KHR_non_semantic_info +;CHECK-NOT: OpExtInstImport "NonSemantic.Shader.DebugInfo.100 +;CHECK-NOT: OpExtInst %void {{%\w+}} DebugSource +;CHECK-NOT: OpExtInst %void {{%\w+}} DebugLine + OpCapability Shader + OpExtension "SPV_KHR_non_semantic_info" + %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %PSMain "PSMain" %in_var_COLOR %out_var_SV_TARGET + OpExecutionMode %PSMain OriginUpperLeft + %5 = OpString "t.hlsl" + %6 = OpString "float" + %7 = OpString "color" + %8 = OpString "PSInput" + %9 = OpString "PSMain" + %10 = OpString "" + %11 = OpString "input" + OpName %in_var_COLOR "in.var.COLOR" + OpName %out_var_SV_TARGET "out.var.SV_TARGET" + OpName %PSMain "PSMain" + OpDecorate %in_var_COLOR Location 0 + OpDecorate %out_var_SV_TARGET Location 0 + %uint = OpTypeInt 32 0 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %uint_1 = OpConstant %uint 1 + %uint_9 = OpConstant %uint 9 + %21 = OpTypeFunction %void +%in_var_COLOR = OpVariable %_ptr_Input_v4float Input +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output + %13 = OpExtInst %void %1 DebugSource %5 + %PSMain = OpFunction %void None %21 + %22 = OpLabel + %23 = OpLoad %v4float %in_var_COLOR + OpStore %out_var_SV_TARGET %23 + %24 = OpExtInst %void %1 DebugLine %13 %uint_9 %uint_9 %uint_1 %uint_1 + OpReturn + OpFunctionEnd +)"; + SinglePassRunAndMatch<StripNonSemanticInfoPass>(text, true); } } // namespace diff --git a/test/opt/type_manager_test.cpp b/test/opt/type_manager_test.cpp index fdae2efc..df216bc9 100644 --- a/test/opt/type_manager_test.cpp +++ b/test/opt/type_manager_test.cpp @@ -167,10 +167,25 @@ std::vector<std::unique_ptr<Type>> GenerateAllTypes() { types.emplace_back(new NamedBarrier()); types.emplace_back(new AccelerationStructureNV()); types.emplace_back(new CooperativeMatrixNV(f32, 24, 24, 24)); + types.emplace_back(new RayQueryKHR()); return types; } +TEST(TypeManager, GenerateAllTypesGeneratesAllTypes) { + std::set<Type::Kind> generated_types; + for (auto& type : GenerateAllTypes()) { + generated_types.insert(type->kind()); + } + + std::vector<Type::Kind> all_types; + for (uint32_t kind = 0; kind != Type::Kind::kLast; ++kind) { + all_types.push_back(static_cast<Type::Kind>(kind)); + } + + EXPECT_THAT(generated_types, testing::UnorderedElementsAreArray(all_types)); +} + TEST(TypeManager, TypeStrings) { const std::string text = R"( OpDecorate %spec_const_with_id SpecId 99 @@ -1065,6 +1080,7 @@ TEST(TypeManager, GetTypeInstructionAllTypes) { ; CHECK: OpTypeNamedBarrier ; CHECK: OpTypeAccelerationStructureKHR ; CHECK: OpTypeCooperativeMatrixNV [[f32]] [[uint24]] [[uint24]] [[uint24]] +; CHECK: OpTypeRayQueryKHR OpCapability Shader OpCapability Int64 OpCapability Linkage diff --git a/test/opt/unify_const_test.cpp b/test/opt/unify_const_test.cpp index 6ed21734..0d7c30b0 100644 --- a/test/opt/unify_const_test.cpp +++ b/test/opt/unify_const_test.cpp @@ -263,7 +263,7 @@ TEST_F(UnifyFrontEndConstantSingleTest, UnifyWithDecorationOnTypes) { // decorated flat struct "%flat_d = OpTypeStruct %int %float", "%_pf_flat_d = OpTypePointer Function %flat_d", - // perserved contants. %flat_1 and %flat_d has same members, but + // preserved constants. %flat_1 and %flat_d has same members, but // their type are different in decorations, so they should not be // used to replace each other. "%int_1 = OpConstant %int 1", @@ -682,7 +682,7 @@ INSTANTIATE_TEST_SUITE_P( // zero-valued composite constant built from zero-valued constant // component. inner_zero should not be replace by null_inner. "%inner_zero = OpConstantComposite %inner_struct %bool_zero %float_zero", - // zero-valued composite contant built from zero-valued constants + // zero-valued composite constant built from zero-valued constants // and null constants. "%outer_zero = OpConstantComposite %outer_struct %inner_zero %int_null %double_null", // outer_struct type null constant, it should not be replaced by @@ -820,7 +820,7 @@ INSTANTIATE_TEST_SUITE_P( { "%spec_signed_add_duplicate = OpSpecConstantOp %int IAdd %spec_signed_1 %spec_signed_2", }, - // use duplicated contants in main + // use duplicated constants in main { "%int_var = OpVariable %_pf_int Function", "OpStore %int_var %spec_signed_add_duplicate", diff --git a/test/target_env_test.cpp b/test/target_env_test.cpp index 4acb8ff2..7917cbfb 100644 --- a/test/target_env_test.cpp +++ b/test/target_env_test.cpp @@ -135,7 +135,8 @@ INSTANTIATE_TEST_SUITE_P( {VK(1, 0), SPV(1, 3), true, SPV_ENV_VULKAN_1_1}, {VK(1, 0), SPV(1, 4), true, SPV_ENV_VULKAN_1_1_SPIRV_1_4}, {VK(1, 0), SPV(1, 5), true, SPV_ENV_VULKAN_1_2}, - {VK(1, 0), SPV(1, 6), false, SPV_ENV_UNIVERSAL_1_0}, + {VK(1, 0), SPV(1, 6), true, SPV_ENV_VULKAN_1_3}, + {VK(1, 0), SPV(1, 7), false, SPV_ENV_UNIVERSAL_1_0}, // Vulkan 1.1 cases {VK(1, 1), SPV(1, 0), true, SPV_ENV_VULKAN_1_1}, {VK(1, 1), SPV(1, 1), true, SPV_ENV_VULKAN_1_1}, @@ -143,7 +144,8 @@ INSTANTIATE_TEST_SUITE_P( {VK(1, 1), SPV(1, 3), true, SPV_ENV_VULKAN_1_1}, {VK(1, 1), SPV(1, 4), true, SPV_ENV_VULKAN_1_1_SPIRV_1_4}, {VK(1, 1), SPV(1, 5), true, SPV_ENV_VULKAN_1_2}, - {VK(1, 1), SPV(1, 6), false, SPV_ENV_UNIVERSAL_1_0}, + {VK(1, 1), SPV(1, 6), true, SPV_ENV_VULKAN_1_3}, + {VK(1, 1), SPV(1, 7), false, SPV_ENV_UNIVERSAL_1_0}, // Vulkan 1.2 cases {VK(1, 2), SPV(1, 0), true, SPV_ENV_VULKAN_1_2}, {VK(1, 2), SPV(1, 1), true, SPV_ENV_VULKAN_1_2}, @@ -151,9 +153,17 @@ INSTANTIATE_TEST_SUITE_P( {VK(1, 2), SPV(1, 3), true, SPV_ENV_VULKAN_1_2}, {VK(1, 2), SPV(1, 4), true, SPV_ENV_VULKAN_1_2}, {VK(1, 2), SPV(1, 5), true, SPV_ENV_VULKAN_1_2}, - {VK(1, 2), SPV(1, 6), false, SPV_ENV_UNIVERSAL_1_0}, + {VK(1, 2), SPV(1, 6), true, SPV_ENV_VULKAN_1_3}, + {VK(1, 2), SPV(1, 7), false, SPV_ENV_UNIVERSAL_1_0}, // Vulkan 1.3 cases - {VK(1, 3), SPV(1, 0), false, SPV_ENV_UNIVERSAL_1_0}, + {VK(1, 3), SPV(1, 0), true, SPV_ENV_VULKAN_1_3}, + {VK(1, 3), SPV(1, 1), true, SPV_ENV_VULKAN_1_3}, + {VK(1, 3), SPV(1, 2), true, SPV_ENV_VULKAN_1_3}, + {VK(1, 3), SPV(1, 3), true, SPV_ENV_VULKAN_1_3}, + {VK(1, 3), SPV(1, 4), true, SPV_ENV_VULKAN_1_3}, + {VK(1, 3), SPV(1, 5), true, SPV_ENV_VULKAN_1_3}, + {VK(1, 3), SPV(1, 6), true, SPV_ENV_VULKAN_1_3}, + {VK(1, 3), SPV(1, 7), false, SPV_ENV_UNIVERSAL_1_0}, // Vulkan 2.0 cases {VK(2, 0), SPV(1, 0), false, SPV_ENV_UNIVERSAL_1_0}, // Vulkan 99.0 cases diff --git a/test/test_fixture.h b/test/test_fixture.h index 0c5bfc9c..029fc854 100644 --- a/test/test_fixture.h +++ b/test/test_fixture.h @@ -15,6 +15,7 @@ #ifndef TEST_TEST_FIXTURE_H_ #define TEST_TEST_FIXTURE_H_ +#include <algorithm> #include <string> #include <vector> @@ -91,12 +92,26 @@ class TextToBinaryTestBase : public T { return diagnostic->error; } + // Potentially flip the words in the binary representation to the other + // endianness + template <class It> + void MaybeFlipWords(bool flip_words, It begin, It end) { + SCOPED_TRACE(flip_words ? "Flipped Endianness" : "Normal Endianness"); + if (flip_words) { + std::transform(begin, end, begin, [](const uint32_t raw_word) { + return spvFixWord(raw_word, I32_ENDIAN_HOST == I32_ENDIAN_BIG + ? SPV_ENDIANNESS_LITTLE + : SPV_ENDIANNESS_BIG); + }); + } + } + // Encodes SPIR-V text into binary and then decodes the binary using // given options. Returns the decoded text. std::string EncodeAndDecodeSuccessfully( const std::string& txt, uint32_t disassemble_options = SPV_BINARY_TO_TEXT_OPTION_NONE, - spv_target_env env = SPV_ENV_UNIVERSAL_1_0) { + spv_target_env env = SPV_ENV_UNIVERSAL_1_0, bool flip_words = false) { DestroyBinary(); DestroyDiagnostic(); ScopedContext context(env); @@ -110,6 +125,8 @@ class TextToBinaryTestBase : public T { EXPECT_EQ(SPV_SUCCESS, error); if (!binary) return ""; + MaybeFlipWords(flip_words, binary->code, binary->code + binary->wordCount); + spv_text decoded_text; error = spvBinaryToText(context.context, binary->code, binary->wordCount, disassemble_options, &decoded_text, &diagnostic); diff --git a/test/text_to_binary.annotation_test.cpp b/test/text_to_binary.annotation_test.cpp index 61bdf64c..76776de9 100644 --- a/test/text_to_binary.annotation_test.cpp +++ b/test/text_to_binary.annotation_test.cpp @@ -398,7 +398,8 @@ TEST_F(TextToBinaryTest, GroupMemberDecorateGoodTwoTargets) { TEST_F(TextToBinaryTest, GroupMemberDecorateMissingGroupId) { EXPECT_THAT(CompileFailure("OpGroupMemberDecorate"), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpGroupMemberDecorate instruction, but " + "found the end of the stream.")); } TEST_F(TextToBinaryTest, GroupMemberDecorateInvalidGroupId) { @@ -413,7 +414,8 @@ TEST_F(TextToBinaryTest, GroupMemberDecorateInvalidTargetId) { TEST_F(TextToBinaryTest, GroupMemberDecorateMissingTargetMemberNumber) { EXPECT_THAT(CompileFailure("OpGroupMemberDecorate %group %id0"), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpGroupMemberDecorate instruction, but " + "found the end of the stream.")); } TEST_F(TextToBinaryTest, GroupMemberDecorateInvalidTargetMemberNumber) { @@ -428,7 +430,8 @@ TEST_F(TextToBinaryTest, GroupMemberDecorateInvalidSecondTargetId) { TEST_F(TextToBinaryTest, GroupMemberDecorateMissingSecondTargetMemberNumber) { EXPECT_THAT(CompileFailure("OpGroupMemberDecorate %group %id0 42 %id1"), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpGroupMemberDecorate instruction, but " + "found the end of the stream.")); } TEST_F(TextToBinaryTest, GroupMemberDecorateInvalidSecondTargetMemberNumber) { diff --git a/test/text_to_binary.barrier_test.cpp b/test/text_to_binary.barrier_test.cpp index 545d26ff..f1cb4fbe 100644 --- a/test/text_to_binary.barrier_test.cpp +++ b/test/text_to_binary.barrier_test.cpp @@ -44,7 +44,8 @@ TEST_F(OpMemoryBarrier, Good) { TEST_F(OpMemoryBarrier, BadMissingScopeId) { const std::string input = "OpMemoryBarrier\n"; EXPECT_THAT(CompileFailure(input), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpMemoryBarrier instruction, but found " + "the end of the stream.")); } TEST_F(OpMemoryBarrier, BadInvalidScopeId) { @@ -55,7 +56,8 @@ TEST_F(OpMemoryBarrier, BadInvalidScopeId) { TEST_F(OpMemoryBarrier, BadMissingMemorySemanticsId) { const std::string input = "OpMemoryBarrier %scope\n"; EXPECT_THAT(CompileFailure(input), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpMemoryBarrier instruction, but found " + "the end of the stream.")); } TEST_F(OpMemoryBarrier, BadInvalidMemorySemanticsId) { @@ -92,13 +94,16 @@ TEST_F(NamedMemoryBarrierTest, OpcodeAssemblesInV10) { TEST_F(NamedMemoryBarrierTest, ArgumentCount) { EXPECT_THAT(CompileFailure("OpMemoryNamedBarrier", SPV_ENV_UNIVERSAL_1_1), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpMemoryNamedBarrier instruction, but " + "found the end of the stream.")); EXPECT_THAT( CompileFailure("OpMemoryNamedBarrier %bar", SPV_ENV_UNIVERSAL_1_1), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpMemoryNamedBarrier instruction, but found the " + "end of the stream.")); EXPECT_THAT( CompileFailure("OpMemoryNamedBarrier %bar %scope", SPV_ENV_UNIVERSAL_1_1), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpMemoryNamedBarrier instruction, but found the " + "end of the stream.")); EXPECT_THAT( CompiledInstructions("OpMemoryNamedBarrier %bar %scope %semantics", SPV_ENV_UNIVERSAL_1_1), @@ -151,10 +156,12 @@ TEST_F(NamedBarrierInitializeTest, OpcodeAssemblesInV10) { TEST_F(NamedBarrierInitializeTest, ArgumentCount) { EXPECT_THAT( CompileFailure("%bar = OpNamedBarrierInitialize", SPV_ENV_UNIVERSAL_1_1), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpNamedBarrierInitialize instruction, but found " + "the end of the stream.")); EXPECT_THAT(CompileFailure("%bar = OpNamedBarrierInitialize %ype", SPV_ENV_UNIVERSAL_1_1), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpNamedBarrierInitialize instruction, " + "but found the end of the stream.")); EXPECT_THAT( CompiledInstructions("%bar = OpNamedBarrierInitialize %type %count", SPV_ENV_UNIVERSAL_1_1), diff --git a/test/text_to_binary.control_flow_test.cpp b/test/text_to_binary.control_flow_test.cpp index 3e117b8f..472cb6da 100644 --- a/test/text_to_binary.control_flow_test.cpp +++ b/test/text_to_binary.control_flow_test.cpp @@ -163,7 +163,8 @@ TEST_F(TextToBinaryTest, SwitchGoodTwoTargets) { TEST_F(TextToBinaryTest, SwitchBadMissingSelector) { EXPECT_THAT(CompileFailure("OpSwitch"), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpSwitch instruction, but found the end " + "of the stream.")); } TEST_F(TextToBinaryTest, SwitchBadInvalidSelector) { @@ -173,7 +174,8 @@ TEST_F(TextToBinaryTest, SwitchBadInvalidSelector) { TEST_F(TextToBinaryTest, SwitchBadMissingDefault) { EXPECT_THAT(CompileFailure("OpSwitch %selector"), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpSwitch instruction, but found the end " + "of the stream.")); } TEST_F(TextToBinaryTest, SwitchBadInvalidDefault) { @@ -195,7 +197,8 @@ TEST_F(TextToBinaryTest, SwitchBadMissingTarget) { EXPECT_THAT(CompileFailure("%1 = OpTypeInt 32 0\n" "%2 = OpConstant %1 52\n" "OpSwitch %2 %default 12"), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpSwitch instruction, but found the end " + "of the stream.")); } // A test case for an OpSwitch. @@ -379,7 +382,7 @@ INSTANTIATE_TEST_SUITE_P( "OpTypeQueue", "OpTypePipe ReadOnly", - // Skip OpTypeForwardPointer becasuse it doesn't even produce a result + // Skip OpTypeForwardPointer because it doesn't even produce a result // ID. // At least one thing that isn't a type at all diff --git a/test/text_to_binary.device_side_enqueue_test.cpp b/test/text_to_binary.device_side_enqueue_test.cpp index 03d7e741..2f4dd705 100644 --- a/test/text_to_binary.device_side_enqueue_test.cpp +++ b/test/text_to_binary.device_side_enqueue_test.cpp @@ -83,7 +83,8 @@ TEST_F(OpKernelEnqueueBad, MissingLastOperand) { CompileFailure( "%result = OpEnqueueKernel %type %queue %flags %NDRange %num_events" " %wait_events %ret_event %invoke %param %param_size"), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpEnqueueKernel instruction, but found the end " + "of the stream.")); } TEST_F(OpKernelEnqueueBad, InvalidLastOperand) { diff --git a/test/text_to_binary.extension_test.cpp b/test/text_to_binary.extension_test.cpp index 1324206c..3a592a09 100644 --- a/test/text_to_binary.extension_test.cpp +++ b/test/text_to_binary.extension_test.cpp @@ -197,7 +197,7 @@ INSTANTIATE_TEST_SUITE_P( SpvBuiltInSubgroupLtMask})}, }))); -// The old builtin names (with KHR suffix) still work in the assmebler, and +// The old builtin names (with KHR suffix) still work in the assembler, and // map to the enums without the KHR. INSTANTIATE_TEST_SUITE_P( SPV_KHR_shader_ballot_vulkan_1_1_alias_check, ExtensionAssemblyTest, @@ -957,61 +957,62 @@ INSTANTIATE_TEST_SUITE_P( INSTANTIATE_TEST_SUITE_P( SPV_KHR_integer_dot_product, ExtensionRoundTripTest, Combine( - Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_5, SPV_ENV_VULKAN_1_0, - SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_2), + Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_5, + SPV_ENV_UNIVERSAL_1_6, SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1, + SPV_ENV_VULKAN_1_2, SPV_ENV_VULKAN_1_3), ValuesIn(std::vector<AssemblyCase>{ {"OpExtension \"SPV_KHR_integer_dot_product\"\n", MakeInstruction(SpvOpExtension, MakeVector("SPV_KHR_integer_dot_product"))}, - {"OpCapability DotProductInputAllKHR\n", + {"OpCapability DotProductInputAll\n", MakeInstruction(SpvOpCapability, {SpvCapabilityDotProductInputAllKHR})}, - {"OpCapability DotProductInput4x8BitKHR\n", + {"OpCapability DotProductInput4x8Bit\n", MakeInstruction(SpvOpCapability, {SpvCapabilityDotProductInput4x8BitKHR})}, - {"OpCapability DotProductInput4x8BitPackedKHR\n", + {"OpCapability DotProductInput4x8BitPacked\n", MakeInstruction(SpvOpCapability, {SpvCapabilityDotProductInput4x8BitPackedKHR})}, - {"OpCapability DotProductKHR\n", + {"OpCapability DotProduct\n", MakeInstruction(SpvOpCapability, {SpvCapabilityDotProductKHR})}, - {"%2 = OpSDotKHR %1 %3 %4\n", + {"%2 = OpSDot %1 %3 %4\n", MakeInstruction(SpvOpSDotKHR, {1, 2, 3, 4})}, - {"%2 = OpSDotKHR %1 %3 %4 PackedVectorFormat4x8BitKHR\n", + {"%2 = OpSDot %1 %3 %4 PackedVectorFormat4x8Bit\n", MakeInstruction( SpvOpSDotKHR, {1, 2, 3, 4, SpvPackedVectorFormatPackedVectorFormat4x8BitKHR})}, - {"%2 = OpUDotKHR %1 %3 %4\n", + {"%2 = OpUDot %1 %3 %4\n", MakeInstruction(SpvOpUDotKHR, {1, 2, 3, 4})}, - {"%2 = OpUDotKHR %1 %3 %4 PackedVectorFormat4x8BitKHR\n", + {"%2 = OpUDot %1 %3 %4 PackedVectorFormat4x8Bit\n", MakeInstruction( SpvOpUDotKHR, {1, 2, 3, 4, SpvPackedVectorFormatPackedVectorFormat4x8BitKHR})}, - {"%2 = OpSUDotKHR %1 %3 %4\n", + {"%2 = OpSUDot %1 %3 %4\n", MakeInstruction(SpvOpSUDotKHR, {1, 2, 3, 4})}, - {"%2 = OpSUDotKHR %1 %3 %4 PackedVectorFormat4x8BitKHR\n", + {"%2 = OpSUDot %1 %3 %4 PackedVectorFormat4x8Bit\n", MakeInstruction( SpvOpSUDotKHR, {1, 2, 3, 4, SpvPackedVectorFormatPackedVectorFormat4x8BitKHR})}, - {"%2 = OpSDotAccSatKHR %1 %3 %4 %5\n", + {"%2 = OpSDotAccSat %1 %3 %4 %5\n", MakeInstruction(SpvOpSDotAccSatKHR, {1, 2, 3, 4, 5})}, - {"%2 = OpSDotAccSatKHR %1 %3 %4 %5 PackedVectorFormat4x8BitKHR\n", + {"%2 = OpSDotAccSat %1 %3 %4 %5 PackedVectorFormat4x8Bit\n", MakeInstruction( SpvOpSDotAccSatKHR, {1, 2, 3, 4, 5, SpvPackedVectorFormatPackedVectorFormat4x8BitKHR})}, - {"%2 = OpUDotAccSatKHR %1 %3 %4 %5\n", + {"%2 = OpUDotAccSat %1 %3 %4 %5\n", MakeInstruction(SpvOpUDotAccSatKHR, {1, 2, 3, 4, 5})}, - {"%2 = OpUDotAccSatKHR %1 %3 %4 %5 PackedVectorFormat4x8BitKHR\n", + {"%2 = OpUDotAccSat %1 %3 %4 %5 PackedVectorFormat4x8Bit\n", MakeInstruction( SpvOpUDotAccSatKHR, {1, 2, 3, 4, 5, SpvPackedVectorFormatPackedVectorFormat4x8BitKHR})}, - {"%2 = OpSUDotAccSatKHR %1 %3 %4 %5\n", + {"%2 = OpSUDotAccSat %1 %3 %4 %5\n", MakeInstruction(SpvOpSUDotAccSatKHR, {1, 2, 3, 4, 5})}, - {"%2 = OpSUDotAccSatKHR %1 %3 %4 %5 PackedVectorFormat4x8BitKHR\n", + {"%2 = OpSUDotAccSat %1 %3 %4 %5 PackedVectorFormat4x8Bit\n", MakeInstruction( SpvOpSUDotAccSatKHR, {1, 2, 3, 4, 5, @@ -1033,5 +1034,46 @@ INSTANTIATE_TEST_SUITE_P( {SpvCapabilityBitInstructions})}, }))); +// SPV_KHR_uniform_group_instructions + +INSTANTIATE_TEST_SUITE_P( + SPV_KHR_uniform_group_instructions, ExtensionRoundTripTest, + Combine( + Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_5, + SPV_ENV_UNIVERSAL_1_6, SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1, + SPV_ENV_VULKAN_1_2, SPV_ENV_VULKAN_1_3), + ValuesIn(std::vector<AssemblyCase>{ + {"OpExtension \"SPV_KHR_uniform_group_instructions\"\n", + MakeInstruction(SpvOpExtension, + MakeVector("SPV_KHR_uniform_group_instructions"))}, + {"OpCapability GroupUniformArithmeticKHR\n", + MakeInstruction(SpvOpCapability, + {SpvCapabilityGroupUniformArithmeticKHR})}, + {"%2 = OpGroupIMulKHR %1 %3 Reduce %4\n", + MakeInstruction(SpvOpGroupIMulKHR, + {1, 2, 3, SpvGroupOperationReduce, 4})}, + {"%2 = OpGroupFMulKHR %1 %3 Reduce %4\n", + MakeInstruction(SpvOpGroupFMulKHR, + {1, 2, 3, SpvGroupOperationReduce, 4})}, + {"%2 = OpGroupBitwiseAndKHR %1 %3 Reduce %4\n", + MakeInstruction(SpvOpGroupBitwiseAndKHR, + {1, 2, 3, SpvGroupOperationReduce, 4})}, + {"%2 = OpGroupBitwiseOrKHR %1 %3 Reduce %4\n", + MakeInstruction(SpvOpGroupBitwiseOrKHR, + {1, 2, 3, SpvGroupOperationReduce, 4})}, + {"%2 = OpGroupBitwiseXorKHR %1 %3 Reduce %4\n", + MakeInstruction(SpvOpGroupBitwiseXorKHR, + {1, 2, 3, SpvGroupOperationReduce, 4})}, + {"%2 = OpGroupLogicalAndKHR %1 %3 Reduce %4\n", + MakeInstruction(SpvOpGroupLogicalAndKHR, + {1, 2, 3, SpvGroupOperationReduce, 4})}, + {"%2 = OpGroupLogicalOrKHR %1 %3 Reduce %4\n", + MakeInstruction(SpvOpGroupLogicalOrKHR, + {1, 2, 3, SpvGroupOperationReduce, 4})}, + {"%2 = OpGroupLogicalXorKHR %1 %3 Reduce %4\n", + MakeInstruction(SpvOpGroupLogicalXorKHR, + {1, 2, 3, SpvGroupOperationReduce, 4})}, + }))); + } // namespace } // namespace spvtools diff --git a/test/text_to_binary.image_test.cpp b/test/text_to_binary.image_test.cpp index d445369c..8d8ff432 100644 --- a/test/text_to_binary.image_test.cpp +++ b/test/text_to_binary.image_test.cpp @@ -123,7 +123,8 @@ TEST_F(OpImageTest, InvalidTypeOperand) { TEST_F(OpImageTest, MissingSampledImageOperand) { EXPECT_THAT(CompileFailure("%2 = OpImage %1"), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpImage instruction, but found the end " + "of the stream.")); } TEST_F(OpImageTest, InvalidSampledImageOperand) { @@ -222,7 +223,8 @@ TEST_F(OpImageSparseReadTest, InvalidTypeOperand) { TEST_F(OpImageSparseReadTest, MissingImageOperand) { EXPECT_THAT(CompileFailure("%2 = OpImageSparseRead %1"), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpImageSparseRead instruction, but " + "found the end of the stream.")); } TEST_F(OpImageSparseReadTest, InvalidImageOperand) { @@ -232,7 +234,8 @@ TEST_F(OpImageSparseReadTest, InvalidImageOperand) { TEST_F(OpImageSparseReadTest, MissingCoordinateOperand) { EXPECT_THAT(CompileFailure("%2 = OpImageSparseRead %1 %2"), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpImageSparseRead instruction, but " + "found the end of the stream.")); } TEST_F(OpImageSparseReadTest, InvalidCoordinateOperand) { diff --git a/test/text_to_binary.memory_test.cpp b/test/text_to_binary.memory_test.cpp index 7b09ed58..f94c134a 100644 --- a/test/text_to_binary.memory_test.cpp +++ b/test/text_to_binary.memory_test.cpp @@ -166,7 +166,8 @@ TEST_F(MemoryRoundTripTest, OpCopyMemoryNoMemAccessGood) { TEST_F(MemoryRoundTripTest, OpCopyMemoryTooFewArgsBad) { std::string spirv = "OpCopyMemory %1\n"; std::string err = CompileFailure(spirv); - EXPECT_THAT(err, HasSubstr("Expected operand, found end of stream")); + EXPECT_THAT(err, HasSubstr("Expected operand for OpCopyMemory instruction, " + "but found the end of the stream.")); } TEST_F(MemoryRoundTripTest, OpCopyMemoryTooManyArgsBad) { @@ -295,7 +296,8 @@ TEST_F(MemoryRoundTripTest, OpCopyMemorySizedNoMemAccessGood) { TEST_F(MemoryRoundTripTest, OpCopyMemorySizedTooFewArgsBad) { std::string spirv = "OpCopyMemorySized %1 %2\n"; std::string err = CompileFailure(spirv); - EXPECT_THAT(err, HasSubstr("Expected operand, found end of stream")); + EXPECT_THAT(err, HasSubstr("Expected operand for OpCopyMemorySized " + "instruction, but found the end of the stream.")); } TEST_F(MemoryRoundTripTest, OpCopyMemorySizedTooManyArgsBad) { diff --git a/test/text_to_binary.mode_setting_test.cpp b/test/text_to_binary.mode_setting_test.cpp index 647bb3d9..7f15c8b4 100644 --- a/test/text_to_binary.mode_setting_test.cpp +++ b/test/text_to_binary.mode_setting_test.cpp @@ -290,7 +290,8 @@ using TextToBinaryCapability = spvtest::TextToBinaryTest; TEST_F(TextToBinaryCapability, BadMissingCapability) { EXPECT_THAT(CompileFailure("OpCapability"), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpCapability instruction, but found the " + "end of the stream.")); } TEST_F(TextToBinaryCapability, BadInvalidCapability) { diff --git a/test/text_to_binary.pipe_storage_test.cpp b/test/text_to_binary.pipe_storage_test.cpp index f74dbcfd..955f5ef8 100644 --- a/test/text_to_binary.pipe_storage_test.cpp +++ b/test/text_to_binary.pipe_storage_test.cpp @@ -59,10 +59,12 @@ TEST_F(OpConstantPipeStorageTest, ArgumentCount) { "'OpConstantPipeStorage'.")); EXPECT_THAT( CompileFailure("%1 = OpConstantPipeStorage", SPV_ENV_UNIVERSAL_1_1), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpConstantPipeStorage instruction, but found " + "the end of the stream.")); EXPECT_THAT(CompileFailure("%1 = OpConstantPipeStorage %2 3 4", SPV_ENV_UNIVERSAL_1_1), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpConstantPipeStorage instruction, but " + "found the end of the stream.")); EXPECT_THAT(CompiledInstructions("%1 = OpConstantPipeStorage %2 3 4 5", SPV_ENV_UNIVERSAL_1_1), Eq(MakeInstruction(SpvOpConstantPipeStorage, {1, 2, 3, 4, 5}))); @@ -101,10 +103,12 @@ TEST_F(OpCreatePipeFromPipeStorageTest, ArgumentCount) { "'OpCreatePipeFromPipeStorage'.")); EXPECT_THAT( CompileFailure("%1 = OpCreatePipeFromPipeStorage", SPV_ENV_UNIVERSAL_1_1), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpCreatePipeFromPipeStorage instruction, but " + "found the end of the stream.")); EXPECT_THAT(CompileFailure("%1 = OpCreatePipeFromPipeStorage %2 OpNop", SPV_ENV_UNIVERSAL_1_1), - Eq("Expected operand, found next instruction instead.")); + Eq("Expected operand for OpCreatePipeFromPipeStorage " + "instruction, but found the next instruction instead.")); EXPECT_THAT(CompiledInstructions("%1 = OpCreatePipeFromPipeStorage %2 %3", SPV_ENV_UNIVERSAL_1_1), Eq(MakeInstruction(SpvOpCreatePipeFromPipeStorage, {1, 2, 3}))); diff --git a/test/text_to_binary.subgroup_dispatch_test.cpp b/test/text_to_binary.subgroup_dispatch_test.cpp index 967e3c38..8c404457 100644 --- a/test/text_to_binary.subgroup_dispatch_test.cpp +++ b/test/text_to_binary.subgroup_dispatch_test.cpp @@ -46,11 +46,13 @@ TEST_F(OpGetKernelLocalSizeForSubgroupCountTest, ArgumentCount) { "found 'OpGetKernelLocalSizeForSubgroupCount'.")); EXPECT_THAT(CompileFailure("%res = OpGetKernelLocalSizeForSubgroupCount", SPV_ENV_UNIVERSAL_1_1), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpGetKernelLocalSizeForSubgroupCount " + "instruction, but found the end of the stream.")); EXPECT_THAT( CompileFailure("%1 = OpGetKernelLocalSizeForSubgroupCount %2 %3 %4 %5 %6", SPV_ENV_UNIVERSAL_1_1), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpGetKernelLocalSizeForSubgroupCount " + "instruction, but found the end of the stream.")); EXPECT_THAT( CompiledInstructions("%res = OpGetKernelLocalSizeForSubgroupCount %type " "%sgcount %invoke %param %param_size %param_align", @@ -93,10 +95,12 @@ TEST_F(OpGetKernelMaxNumSubgroupsTest, ArgumentCount) { "'OpGetKernelMaxNumSubgroups'.")); EXPECT_THAT(CompileFailure("%res = OpGetKernelMaxNumSubgroups", SPV_ENV_UNIVERSAL_1_1), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpGetKernelMaxNumSubgroups instruction, " + "but found the end of the stream.")); EXPECT_THAT(CompileFailure("%1 = OpGetKernelMaxNumSubgroups %2 %3 %4 %5", SPV_ENV_UNIVERSAL_1_1), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpGetKernelMaxNumSubgroups instruction, " + "but found the end of the stream.")); EXPECT_THAT( CompiledInstructions("%res = OpGetKernelMaxNumSubgroups %type " "%invoke %param %param_size %param_align", diff --git a/test/text_to_binary.type_declaration_test.cpp b/test/text_to_binary.type_declaration_test.cpp index 1589188f..65a23554 100644 --- a/test/text_to_binary.type_declaration_test.cpp +++ b/test/text_to_binary.type_declaration_test.cpp @@ -223,12 +223,14 @@ TEST_F(OpTypeForwardPointerTest, ValidStorageClass) { TEST_F(OpTypeForwardPointerTest, MissingType) { EXPECT_THAT(CompileFailure("OpTypeForwardPointer"), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpTypeForwardPointer instruction, but " + "found the end of the stream.")); } TEST_F(OpTypeForwardPointerTest, MissingClass) { EXPECT_THAT(CompileFailure("OpTypeForwardPointer %pt"), - Eq("Expected operand, found end of stream.")); + Eq("Expected operand for OpTypeForwardPointer instruction, but " + "found the end of the stream.")); } TEST_F(OpTypeForwardPointerTest, WrongClass) { @@ -252,7 +254,8 @@ TEST_F(OpSizeOfTest, ArgumentCount) { Eq("Expected <result-id> at the beginning of an instruction, found " "'OpSizeOf'.")); EXPECT_THAT(CompileFailure("%res = OpSizeOf OpNop", SPV_ENV_UNIVERSAL_1_1), - Eq("Expected operand, found next instruction instead.")); + Eq("Expected operand for OpSizeOf instruction, but found the " + "next instruction instead.")); EXPECT_THAT( CompiledInstructions("%1 = OpSizeOf %2 %3", SPV_ENV_UNIVERSAL_1_1), Eq(MakeInstruction(SpvOpSizeOf, {1, 2, 3}))); diff --git a/test/text_to_binary_test.cpp b/test/text_to_binary_test.cpp index 99d9ed68..0b348e87 100644 --- a/test/text_to_binary_test.cpp +++ b/test/text_to_binary_test.cpp @@ -65,7 +65,7 @@ INSTANTIATE_TEST_SUITE_P( {SPV_OPERAND_TYPE_FP_FAST_MATH_MODE, 1, "NotNaN"}, {SPV_OPERAND_TYPE_FP_FAST_MATH_MODE, 2, "NotInf"}, {SPV_OPERAND_TYPE_FP_FAST_MATH_MODE, 3, "NotNaN|NotInf"}, - // Mask experssions are symmetric. + // Mask expressions are symmetric. {SPV_OPERAND_TYPE_FP_FAST_MATH_MODE, 3, "NotInf|NotNaN"}, // Repeating a value has no effect. {SPV_OPERAND_TYPE_FP_FAST_MATH_MODE, 3, "NotInf|NotNaN|NotInf"}, diff --git a/test/tools/expect.py b/test/tools/expect.py index 0b51adc9..7351c029 100755 --- a/test/tools/expect.py +++ b/test/tools/expect.py @@ -285,6 +285,21 @@ class ValidObjectFile1_5(ReturnCodeIsZero, CorrectObjectFilePreamble): return True, '' +class ValidObjectFile1_6(ReturnCodeIsZero, CorrectObjectFilePreamble): + """Mixin class for checking that every input file generates a valid SPIR-V 1.6 + object file following the object file naming rule, and there is no output on + stdout/stderr.""" + + def check_object_file_preamble(self, status): + for input_filename in status.input_filenames: + object_filename = get_object_filename(input_filename) + success, message = self.verify_object_file_preamble( + os.path.join(status.directory, object_filename), 0x10600) + if not success: + return False, message + return True, '' + + class ValidObjectFileWithAssemblySubstr(SuccessfulReturn, CorrectObjectFilePreamble): """Mixin class for checking that every input file generates a valid object diff --git a/test/tools/opt/flags.py b/test/tools/opt/flags.py index c79f6807..52a43c51 100644 --- a/test/tools/opt/flags.py +++ b/test/tools/opt/flags.py @@ -34,7 +34,7 @@ def empty_main_assembly(): @inside_spirv_testsuite('SpirvOptBase') -class TestAssemblyFileAsOnlyParameter(expect.ValidObjectFile1_5): +class TestAssemblyFileAsOnlyParameter(expect.ValidObjectFile1_6): """Tests that spirv-opt accepts a SPIR-V object file.""" shader = placeholder.FileSPIRVShader(empty_main_assembly(), '.spvasm') @@ -52,7 +52,7 @@ class TestHelpFlag(expect.ReturnCodeIsZero, expect.StdoutMatch): @inside_spirv_testsuite('SpirvOptFlags') -class TestValidPassFlags(expect.ValidObjectFile1_5, +class TestValidPassFlags(expect.ValidObjectFile1_6, expect.ExecutedListOfPasses): """Tests that spirv-opt accepts all valid optimization flags.""" @@ -72,7 +72,7 @@ class TestValidPassFlags(expect.ValidObjectFile1_5, '--private-to-local', '--reduce-load-size', '--redundancy-elimination', '--remove-duplicates', '--replace-invalid-opcode', '--ssa-rewrite', '--scalar-replacement', '--scalar-replacement=42', '--strength-reduction', - '--strip-debug', '--strip-reflect', '--vector-dce', '--workaround-1209', + '--strip-debug', '--strip-nonsemantic', '--vector-dce', '--workaround-1209', '--unify-const', '--graphics-robust-access', '--wrap-opkill', '--amd-ext-to-khr' ] expected_passes = [ @@ -117,7 +117,7 @@ class TestValidPassFlags(expect.ValidObjectFile1_5, 'scalar-replacement=42', 'strength-reduction', 'strip-debug', - 'strip-reflect', + 'strip-nonsemantic', 'vector-dce', 'workaround-1209', 'unify-const', @@ -132,7 +132,7 @@ class TestValidPassFlags(expect.ValidObjectFile1_5, @inside_spirv_testsuite('SpirvOptFlags') -class TestPerformanceOptimizationPasses(expect.ValidObjectFile1_5, +class TestPerformanceOptimizationPasses(expect.ValidObjectFile1_6, expect.ExecutedListOfPasses): """Tests that spirv-opt schedules all the passes triggered by -O.""" @@ -190,7 +190,7 @@ class TestPerformanceOptimizationPasses(expect.ValidObjectFile1_5, @inside_spirv_testsuite('SpirvOptFlags') -class TestSizeOptimizationPasses(expect.ValidObjectFile1_5, +class TestSizeOptimizationPasses(expect.ValidObjectFile1_6, expect.ExecutedListOfPasses): """Tests that spirv-opt schedules all the passes triggered by -Os.""" @@ -237,7 +237,7 @@ class TestSizeOptimizationPasses(expect.ValidObjectFile1_5, @inside_spirv_testsuite('SpirvOptFlags') -class TestLegalizationPasses(expect.ValidObjectFile1_5, +class TestLegalizationPasses(expect.ValidObjectFile1_6, expect.ExecutedListOfPasses): """Tests that spirv-opt schedules all the passes triggered by --legalize-hlsl. """ diff --git a/test/util/CMakeLists.txt b/test/util/CMakeLists.txt index 6679dba7..68087836 100644 --- a/test/util/CMakeLists.txt +++ b/test/util/CMakeLists.txt @@ -16,6 +16,8 @@ add_spvtools_unittest(TARGET utils SRCS ilist_test.cpp bit_vector_test.cpp bitutils_test.cpp + hash_combine_test.cpp + pooled_linked_list_test.cpp small_vector_test.cpp LIBS SPIRV-Tools-opt ) diff --git a/test/util/hash_combine_test.cpp b/test/util/hash_combine_test.cpp new file mode 100644 index 00000000..9cd1d870 --- /dev/null +++ b/test/util/hash_combine_test.cpp @@ -0,0 +1,43 @@ +// Copyright (c) 2022 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <utility> +#include <vector> + +#include "gmock/gmock.h" +#include "source/util/hash_combine.h" + +namespace spvtools { +namespace utils { +namespace { + +using HashCombineTest = ::testing::Test; + +TEST(HashCombineTest, Identity) { EXPECT_EQ(hash_combine(0), 0); } + +TEST(HashCombineTest, Variadic) { + // Expect manual and variadic template versions be the same. + EXPECT_EQ(hash_combine(hash_combine(hash_combine(0, 1), 2), 3), + hash_combine(0, 1, 2, 3)); +} + +TEST(HashCombineTest, Vector) { + // Expect variadic and vector versions be the same. + EXPECT_EQ(hash_combine(0, std::vector<uint32_t>({1, 2, 3})), + hash_combine(0, 1, 2, 3)); +} + +} // namespace +} // namespace utils +} // namespace spvtools diff --git a/test/util/pooled_linked_list_test.cpp b/test/util/pooled_linked_list_test.cpp new file mode 100644 index 00000000..82fb4acb --- /dev/null +++ b/test/util/pooled_linked_list_test.cpp @@ -0,0 +1,185 @@ +// Copyright (c) 2021 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <algorithm> +#include <list> +#include <utility> +#include <vector> + +#include "gmock/gmock.h" +#include "source/util/pooled_linked_list.h" + +namespace spvtools { +namespace utils { +namespace { + +using PooledLinkedListTest = ::testing::Test; + +template <typename T> +static std::vector<T> ToVector(const PooledLinkedList<T>& list) { + std::vector<T> vec; + for (auto it = list.begin(); it != list.end(); ++it) { + vec.push_back(*it); + } + return vec; +} + +template <typename T> +static void AppendVector(PooledLinkedList<T>& list, const std::vector<T>& vec) { + for (const T& t : vec) { + list.push_back(t); + } +} + +TEST(PooledLinkedListTest, Empty) { + PooledLinkedListNodes<uint32_t> pool; + PooledLinkedList<uint32_t> ll(&pool); + EXPECT_TRUE(ll.empty()); + + ll.push_back(1u); + EXPECT_TRUE(!ll.empty()); +} + +TEST(PooledLinkedListTest, Iterator) { + PooledLinkedListNodes<uint32_t> pool; + PooledLinkedList<uint32_t> ll(&pool); + + EXPECT_EQ(ll.begin(), ll.end()); + + ll.push_back(1); + EXPECT_NE(ll.begin(), ll.end()); + + auto it = ll.begin(); + EXPECT_EQ(*it, 1); + ++it; + EXPECT_EQ(it, ll.end()); +} + +TEST(PooledLinkedListTest, Iterator_algorithms) { + PooledLinkedListNodes<uint32_t> pool; + PooledLinkedList<uint32_t> ll(&pool); + + AppendVector(ll, {3, 2, 0, 1}); + EXPECT_EQ(std::distance(ll.begin(), ll.end()), 4); + EXPECT_EQ(*std::min_element(ll.begin(), ll.end()), 0); + EXPECT_EQ(*std::max_element(ll.begin(), ll.end()), 3); +} + +TEST(PooledLinkedListTest, FrontBack) { + PooledLinkedListNodes<uint32_t> pool; + PooledLinkedList<uint32_t> ll(&pool); + + ll.push_back(1); + EXPECT_EQ(ll.front(), 1); + EXPECT_EQ(ll.back(), 1); + + ll.push_back(2); + EXPECT_EQ(ll.front(), 1); + EXPECT_EQ(ll.back(), 2); +} + +TEST(PooledLinkedListTest, PushBack) { + const std::vector<uint32_t> vec = {1, 2, 3, 4, 5, 6}; + + PooledLinkedListNodes<uint32_t> pool; + PooledLinkedList<uint32_t> ll(&pool); + + AppendVector(ll, vec); + EXPECT_EQ(vec, ToVector(ll)); +} + +TEST(PooledLinkedListTest, RemoveFirst) { + const std::vector<uint32_t> vec = {1, 2, 3, 4, 5, 6}; + + PooledLinkedListNodes<uint32_t> pool; + PooledLinkedList<uint32_t> ll(&pool); + + EXPECT_FALSE(ll.remove_first(0)); + AppendVector(ll, vec); + EXPECT_FALSE(ll.remove_first(0)); + + std::vector<uint32_t> tmp = vec; + while (!tmp.empty()) { + size_t mid = tmp.size() / 2; + uint32_t elt = tmp[mid]; + tmp.erase(tmp.begin() + mid); + + EXPECT_TRUE(ll.remove_first(elt)); + EXPECT_FALSE(ll.remove_first(elt)); + EXPECT_EQ(tmp, ToVector(ll)); + } + EXPECT_TRUE(ll.empty()); +} + +TEST(PooledLinkedListTest, RemoveFirst_Duplicates) { + const std::vector<uint32_t> vec = {3, 1, 2, 3, 3, 3, 3, 4, 3, 5, 3, 6, 3}; + + PooledLinkedListNodes<uint32_t> pool; + PooledLinkedList<uint32_t> ll(&pool); + AppendVector(ll, vec); + + std::vector<uint32_t> tmp = vec; + while (!tmp.empty()) { + size_t mid = tmp.size() / 2; + uint32_t elt = tmp[mid]; + tmp.erase(std::find(tmp.begin(), tmp.end(), elt)); + + EXPECT_TRUE(ll.remove_first(elt)); + EXPECT_EQ(tmp, ToVector(ll)); + } + EXPECT_TRUE(ll.empty()); +} + +TEST(PooledLinkedList, MoveTo) { + const std::vector<uint32_t> vec = {1, 2, 3, 4, 5, 6}; + + PooledLinkedListNodes<uint32_t> pool; + PooledLinkedList<uint32_t> ll1(&pool); + PooledLinkedList<uint32_t> ll2(&pool); + PooledLinkedList<uint32_t> ll3(&pool); + + AppendVector(ll1, vec); + AppendVector(ll2, vec); + AppendVector(ll3, vec); + EXPECT_EQ(pool.total_nodes(), vec.size() * 3); + EXPECT_EQ(pool.total_nodes(), vec.size() * 3); + EXPECT_EQ(pool.free_nodes(), 0); + + // Move two lists to the new pool + PooledLinkedListNodes<uint32_t> pool_new; + ll1.move_nodes(&pool_new); + ll2.move_nodes(&pool_new); + + // Moved nodes should belong to new pool + EXPECT_EQ(ll1.pool(), &pool_new); + EXPECT_EQ(ll2.pool(), &pool_new); + + // Old pool should be smaller & have free nodes. + EXPECT_EQ(pool.used_nodes(), vec.size()); + EXPECT_EQ(pool.free_nodes(), vec.size() * 2); + + // New pool should be sized exactly and no free nodes. + EXPECT_EQ(pool_new.total_nodes(), vec.size() * 2); + EXPECT_EQ(pool_new.used_nodes(), vec.size() * 2); + EXPECT_EQ(pool_new.free_nodes(), 0); + + // All lists should be preserved + EXPECT_EQ(ToVector(ll1), vec); + EXPECT_EQ(ToVector(ll2), vec); + EXPECT_EQ(ToVector(ll3), vec); +} + +} // namespace +} // namespace utils +} // namespace spvtools diff --git a/test/util/small_vector_test.cpp b/test/util/small_vector_test.cpp index 01d7df18..b8f068eb 100644 --- a/test/util/small_vector_test.cpp +++ b/test/util/small_vector_test.cpp @@ -56,6 +56,18 @@ TEST(SmallVectorTest, Initialize_list2) { } } +TEST(SmallVectorTest, Initialize_list3) { + std::vector<uint32_t> result = {0, 1, 2, 3}; + SmallVector<uint32_t, 6> vec(result.begin(), result.end()); + + EXPECT_FALSE(vec.empty()); + EXPECT_EQ(vec.size(), 4); + + for (uint32_t i = 0; i < vec.size(); ++i) { + EXPECT_EQ(vec[i], result[i]); + } +} + TEST(SmallVectorTest, Initialize_copy1) { SmallVector<uint32_t, 6> vec1 = {0, 1, 2, 3}; SmallVector<uint32_t, 6> vec2(vec1); @@ -593,6 +605,68 @@ TEST(SmallVectorTest, Resize6) { EXPECT_EQ(vec, result); } +TEST(SmallVectorTest, Pop_back) { + SmallVector<uint32_t, 8> vec = {0, 1, 2, 10, 10, 10}; + SmallVector<uint32_t, 8> result = {0, 1, 2}; + + EXPECT_EQ(vec.size(), 6); + vec.pop_back(); + vec.pop_back(); + vec.pop_back(); + EXPECT_EQ(vec.size(), 3); + EXPECT_EQ(vec, result); +} + +TEST(SmallVectorTest, Pop_back_TestDestructor) { + // Tracks number of constructions and destructions to ensure they are called. + struct TracksDtor { + TracksDtor& operator=(TracksDtor&&) = delete; + TracksDtor& operator=(const TracksDtor&) = delete; + + TracksDtor(int& num_ctors, int& num_dtors) + : num_ctors_(num_ctors), num_dtors_(num_dtors) { + num_ctors_++; + } + TracksDtor(const TracksDtor& that) + : TracksDtor(that.num_ctors_, that.num_dtors_) {} + TracksDtor(TracksDtor&& that) + : TracksDtor(that.num_ctors_, that.num_dtors_) {} + ~TracksDtor() { num_dtors_++; } + + int& num_ctors_; + int& num_dtors_; + }; + + constexpr int capacity = 4; + SmallVector<TracksDtor, capacity> vec; + + int num_ctors = 0; + int num_dtors = 0; + + // Make sure it works when staying within the smallvector capacity + for (int i = 0; i < capacity; ++i) { + vec.emplace_back(num_ctors, num_dtors); + } + + EXPECT_EQ(num_ctors, capacity); + while (!vec.empty()) { + vec.pop_back(); + } + + EXPECT_EQ(num_ctors, capacity); + EXPECT_EQ(num_dtors, num_ctors); + + // And when larger than builtin capacity + for (int i = 0; i < capacity * 2; ++i) { + vec.emplace_back(num_ctors, num_dtors); + } + + while (!vec.empty()) { + vec.pop_back(); + } + EXPECT_EQ(num_dtors, num_ctors); +} + } // namespace } // namespace utils } // namespace spvtools diff --git a/test/val/val_annotation_test.cpp b/test/val/val_annotation_test.cpp index 889c76ca..bb30de0a 100644 --- a/test/val/val_annotation_test.cpp +++ b/test/val/val_annotation_test.cpp @@ -754,6 +754,8 @@ OpFunctionEnd CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Location-06672")); EXPECT_THAT( getDiagnosticString(), HasSubstr("decoration must not be applied to this storage class")); @@ -794,6 +796,8 @@ OpFunctionEnd CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); EXPECT_THAT(getDiagnosticString(), + HasSubstr("VUID-StandaloneSpirv-DescriptorSet-06491")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("must be in the StorageBuffer, Uniform, or " "UniformConstant storage class")); } diff --git a/test/val/val_arithmetics_test.cpp b/test/val/val_arithmetics_test.cpp index 856ad02c..4c093e9f 100644 --- a/test/val/val_arithmetics_test.cpp +++ b/test/val/val_arithmetics_test.cpp @@ -656,7 +656,7 @@ TEST_F(ValidateArithmetics, DotDifferentVectorSize) { EXPECT_THAT( getDiagnosticString(), HasSubstr( - "Expected operands to have the same number of componenets: Dot")); + "Expected operands to have the same number of components: Dot")); } TEST_F(ValidateArithmetics, VectorTimesScalarSuccess) { diff --git a/test/val/val_atomics_test.cpp b/test/val/val_atomics_test.cpp index d1a030a8..a0308d59 100644 --- a/test/val/val_atomics_test.cpp +++ b/test/val/val_atomics_test.cpp @@ -280,7 +280,7 @@ TEST_F(ValidateAtomics, AtomicLoadVulkanWrongStorageClass) { AnyVUID("VUID-StandaloneSpirv-None-04645")); EXPECT_THAT( getDiagnosticString(), - HasSubstr("in Vulkan evironment, Workgroup Storage Class is limited to " + HasSubstr("in Vulkan environment, Workgroup Storage Class is limited to " "MeshNV, TaskNV, and GLCompute execution model")); } @@ -708,7 +708,7 @@ OpAtomicStore %f32_var %device %relaxed %f32_1 AnyVUID("VUID-StandaloneSpirv-None-04645")); EXPECT_THAT( getDiagnosticString(), - HasSubstr("in Vulkan evironment, Workgroup Storage Class is limited to " + HasSubstr("in Vulkan environment, Workgroup Storage Class is limited to " "MeshNV, TaskNV, and GLCompute execution model")); } diff --git a/test/val/val_bitwise_test.cpp b/test/val/val_bitwise_test.cpp index 1001def8..bebaa84f 100644 --- a/test/val/val_bitwise_test.cpp +++ b/test/val/val_bitwise_test.cpp @@ -340,6 +340,16 @@ TEST_F(ValidateBitwise, OpBitFieldInsertSuccess) { ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } +TEST_F(ValidateBitwise, OpBitFieldInsertVulkanSuccess) { + const std::string body = R"( +%val1 = OpBitFieldInsert %u32 %u32_1 %u32_2 %s32_1 %s32_2 +%val2 = OpBitFieldInsert %s32vec2 %s32vec2_12 %s32vec2_12 %s32_1 %u32_2 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + TEST_F(ValidateBitwise, OpBitFieldInsertWrongResultType) { const std::string body = R"( %val1 = OpBitFieldInsert %bool %u64_1 %u64_2 %s32_1 %s32_2 @@ -350,7 +360,7 @@ TEST_F(ValidateBitwise, OpBitFieldInsertWrongResultType) { EXPECT_THAT( getDiagnosticString(), HasSubstr( - "Expected int scalar or vector type as Result Type: BitFieldInsert")); + "Expected Base Type to be equal to Result Type: BitFieldInsert")); } TEST_F(ValidateBitwise, OpBitFieldInsertWrongBaseType) { @@ -403,6 +413,20 @@ TEST_F(ValidateBitwise, OpBitFieldInsertCountNotInt) { HasSubstr("Expected Count Type to be int scalar: BitFieldInsert")); } +TEST_F(ValidateBitwise, OpBitFieldInsertNot32Vulkan) { + const std::string body = R"( +%val1 = OpBitFieldInsert %u64 %u64_1 %u64_2 %s32_1 %s32_2 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Base-04781")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected 32-bit int type for Base operand: BitFieldInsert")); +} + TEST_F(ValidateBitwise, OpBitFieldSExtractSuccess) { const std::string body = R"( %val1 = OpBitFieldSExtract %u64 %u64_1 %s32_1 %s32_2 @@ -413,6 +437,16 @@ TEST_F(ValidateBitwise, OpBitFieldSExtractSuccess) { ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } +TEST_F(ValidateBitwise, OpBitFieldSExtractVulkanSuccess) { + const std::string body = R"( +%val1 = OpBitFieldSExtract %u32 %u32_1 %s32_1 %s32_2 +%val2 = OpBitFieldSExtract %s32vec2 %s32vec2_12 %s32_1 %u32_2 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + TEST_F(ValidateBitwise, OpBitFieldSExtractWrongResultType) { const std::string body = R"( %val1 = OpBitFieldSExtract %bool %u64_1 %s32_1 %s32_2 @@ -420,9 +454,10 @@ TEST_F(ValidateBitwise, OpBitFieldSExtractWrongResultType) { CompileSuccessfully(GenerateShaderCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Expected int scalar or vector type as Result Type: " - "BitFieldSExtract")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected Base Type to be equal to Result Type: BitFieldSExtract")); } TEST_F(ValidateBitwise, OpBitFieldSExtractWrongBaseType) { @@ -462,6 +497,20 @@ TEST_F(ValidateBitwise, OpBitFieldSExtractCountNotInt) { HasSubstr("Expected Count Type to be int scalar: BitFieldSExtract")); } +TEST_F(ValidateBitwise, OpBitFieldSExtractNot32Vulkan) { + const std::string body = R"( +%val1 = OpBitFieldSExtract %u64 %u64_1 %s32_1 %s32_2 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Base-04781")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected 32-bit int type for Base operand: BitFieldSExtract")); +} + TEST_F(ValidateBitwise, OpBitReverseSuccess) { const std::string body = R"( %val1 = OpBitReverse %u64 %u64_1 @@ -472,6 +521,16 @@ TEST_F(ValidateBitwise, OpBitReverseSuccess) { ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } +TEST_F(ValidateBitwise, OpBitReverseVulkanSuccess) { + const std::string body = R"( +%val1 = OpBitReverse %u32 %u32_1 +%val2 = OpBitReverse %s32vec2 %s32vec2_12 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + TEST_F(ValidateBitwise, OpBitReverseWrongResultType) { const std::string body = R"( %val1 = OpBitReverse %bool %u64_1 @@ -481,8 +540,7 @@ TEST_F(ValidateBitwise, OpBitReverseWrongResultType) { ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr( - "Expected int scalar or vector type as Result Type: BitReverse")); + HasSubstr("Expected Base Type to be equal to Result Type: BitReverse")); } TEST_F(ValidateBitwise, OpBitReverseWrongBaseType) { @@ -497,16 +555,41 @@ TEST_F(ValidateBitwise, OpBitReverseWrongBaseType) { HasSubstr("Expected Base Type to be equal to Result Type: BitReverse")); } +TEST_F(ValidateBitwise, OpBitReverseNot32Vulkan) { + const std::string body = R"( +%val1 = OpBitReverse %u64 %u64_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Base-04781")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected 32-bit int type for Base operand: BitReverse")); +} + TEST_F(ValidateBitwise, OpBitCountSuccess) { const std::string body = R"( %val1 = OpBitCount %s32 %u64_1 %val2 = OpBitCount %u32vec2 %s32vec2_12 +%val3 = OpBitCount %s64 %s64_1 )"; CompileSuccessfully(GenerateShaderCode(body).c_str()); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } +TEST_F(ValidateBitwise, OpBitCountVulkanSuccess) { + const std::string body = R"( +%val1 = OpBitCount %s32 %u32_1 +%val2 = OpBitCount %u32vec2 %s32vec2_12 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + TEST_F(ValidateBitwise, OpBitCountWrongResultType) { const std::string body = R"( %val1 = OpBitCount %bool %u64_1 @@ -524,11 +607,14 @@ TEST_F(ValidateBitwise, OpBitCountBaseNotInt) { %val1 = OpBitCount %u32 %f64_1 )"; - CompileSuccessfully(GenerateShaderCode(body).c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Base-04781")); EXPECT_THAT( getDiagnosticString(), - HasSubstr("Expected Base Type to be int scalar or vector: BitCount")); + HasSubstr( + "Expected int scalar or vector type for Base operand: BitCount")); } TEST_F(ValidateBitwise, OpBitCountBaseWrongDimension) { @@ -544,6 +630,19 @@ TEST_F(ValidateBitwise, OpBitCountBaseWrongDimension) { "BitCount")); } +TEST_F(ValidateBitwise, OpBitCountNot32Vulkan) { + const std::string body = R"( +%val1 = OpBitCount %s64 %s64_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Base-04781")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected 32-bit int type for Base operand: BitCount")); +} + } // namespace } // namespace val } // namespace spvtools diff --git a/test/val/val_builtins_test.cpp b/test/val/val_builtins_test.cpp index dff9adfe..d749c5a8 100644 --- a/test/val/val_builtins_test.cpp +++ b/test/val/val_builtins_test.cpp @@ -93,7 +93,8 @@ CodeGenerator GetInMainCodeGenerator(const char* const built_in, generator.extensions_ += extensions; } - generator.before_types_ = "OpMemberDecorate %built_in_type 0 BuiltIn "; + generator.before_types_ = R"(OpDecorate %built_in_type Block + OpMemberDecorate %built_in_type 0 BuiltIn )"; generator.before_types_ += built_in; generator.before_types_ += "\n"; @@ -251,7 +252,8 @@ CodeGenerator GetInFunctionCodeGenerator(const char* const built_in, generator.extensions_ += extensions; } - generator.before_types_ = "OpMemberDecorate %built_in_type 0 BuiltIn "; + generator.before_types_ = R"(OpDecorate %built_in_type Block + OpMemberDecorate %built_in_type 0 BuiltIn )"; generator.before_types_ += built_in; generator.before_types_ += "\n"; @@ -3098,6 +3100,8 @@ TEST_F(ValidateBuiltIns, TwoBuiltInsFirstFails) { CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); generator.before_types_ = R"( +OpDecorate %input_type Block +OpDecorate %output_type Block OpMemberDecorate %input_type 0 BuiltIn FragCoord OpMemberDecorate %output_type 0 BuiltIn Position )"; @@ -3138,6 +3142,8 @@ TEST_F(ValidateBuiltIns, TwoBuiltInsSecondFails) { CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); generator.before_types_ = R"( +OpDecorate %input_type Block +OpDecorate %output_type Block OpMemberDecorate %input_type 0 BuiltIn Position OpMemberDecorate %output_type 0 BuiltIn FragCoord )"; @@ -3201,6 +3207,7 @@ OpStore %position %f32vec4_0123 TEST_F(ValidateBuiltIns, FragmentPositionTwoEntryPoints) { CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); generator.before_types_ = R"( +OpDecorate %output_type Block OpMemberDecorate %output_type 0 BuiltIn Position )"; @@ -3252,6 +3259,7 @@ CodeGenerator GetNoDepthReplacingGenerator() { CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); generator.before_types_ = R"( +OpDecorate %output_type Block OpMemberDecorate %output_type 0 BuiltIn FragDepth )"; @@ -3303,6 +3311,7 @@ CodeGenerator GetOneMainHasDepthReplacingOtherHasntGenerator() { CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); generator.before_types_ = R"( +OpDecorate %output_type Block OpMemberDecorate %output_type 0 BuiltIn FragDepth )"; @@ -3374,6 +3383,7 @@ OpExtension "SPV_NV_ray_tracing" )"; generator.before_types_ = R"( +OpDecorate %input_type Block OpMemberDecorate %input_type 0 BuiltIn InstanceId )"; @@ -3609,6 +3619,7 @@ OpCapability GroupNonUniformBallot OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %foo "foo" OpExecutionMode %foo LocalSize 1 1 1 +OpDecorate %struct Block OpMemberDecorate %struct 0 BuiltIn SubgroupEqMask %void = OpTypeVoid %int = OpTypeInt 32 0 @@ -3663,6 +3674,7 @@ OpCapability GroupNonUniform OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %foo "foo" OpExecutionMode %foo LocalSize 1 1 1 +OpDecorate %struct Block OpMemberDecorate %struct 0 BuiltIn SubgroupSize %void = OpTypeVoid %int = OpTypeInt 32 0 @@ -3723,6 +3735,7 @@ OpCapability GroupNonUniform OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %foo "foo" OpExecutionMode %foo LocalSize 1 1 1 +OpDecorate %struct Block OpMemberDecorate %struct 0 BuiltIn SubgroupId %void = OpTypeVoid %int = OpTypeInt 32 0 diff --git a/test/val/val_capability_test.cpp b/test/val/val_capability_test.cpp index c432c3cf..0d84caa1 100644 --- a/test/val/val_capability_test.cpp +++ b/test/val/val_capability_test.cpp @@ -2918,8 +2918,8 @@ std::vector<CapabilityExtensionVersionCase> CapVersionCases1_5() { "StorageTexelBufferArrayNonUniformIndexing", "SPV_EXT_descriptor_indexing"), // SPV_EXT_physical_storage_buffer - IN15("PhysicalStorageBufferAddressesEXT", - "PhysicalStorageBufferAddresses", "SPV_EXT_physical_storage_buffer"), + IN15("PhysicalStorageBufferAddresses", "PhysicalStorageBufferAddresses", + "SPV_EXT_physical_storage_buffer"), // SPV_KHR_vulkan_memory_model IN15("VulkanMemoryModelKHR", "VulkanMemoryModel", "SPV_KHR_vulkan_memory_model"), diff --git a/test/val/val_cfg_test.cpp b/test/val/val_cfg_test.cpp index 311cfa77..76477468 100644 --- a/test/val/val_cfg_test.cpp +++ b/test/val/val_cfg_test.cpp @@ -65,7 +65,7 @@ class Block { /// Creates a Block with a given label /// /// @param[in]: label the label id of the block - /// @param[in]: type the branch instruciton that ends the block + /// @param[in]: type the branch instruction that ends the block explicit Block(std::string label, SpvOp type = SpvOpBranch) : label_(label), body_(), type_(type), successors_() {} @@ -3523,7 +3523,7 @@ OpFunctionEnd EXPECT_THAT( getDiagnosticString(), HasSubstr( - "OpSwitch must be preceeded by an OpSelectionMerge instruction")); + "OpSwitch must be preceded by an OpSelectionMerge instruction")); } TEST_F(ValidateCFG, MissingMergeSwitchBad2) { @@ -3550,7 +3550,7 @@ OpFunctionEnd EXPECT_THAT( getDiagnosticString(), HasSubstr( - "OpSwitch must be preceeded by an OpSelectionMerge instruction")); + "OpSwitch must be preceded by an OpSelectionMerge instruction")); } TEST_F(ValidateCFG, MissingMergeOneBranchToMergeGood) { @@ -3622,7 +3622,7 @@ OpFunctionEnd EXPECT_THAT( getDiagnosticString(), HasSubstr( - "OpSwitch must be preceeded by an OpSelectionMerge instruction")); + "OpSwitch must be preceded by an OpSelectionMerge instruction")); } TEST_F(ValidateCFG, MissingMergeOneUnseenTargetSwitchBad) { @@ -3654,7 +3654,7 @@ OpFunctionEnd EXPECT_THAT( getDiagnosticString(), HasSubstr( - "OpSwitch must be preceeded by an OpSelectionMerge instruction")); + "OpSwitch must be preceded by an OpSelectionMerge instruction")); } TEST_F(ValidateCFG, MissingMergeLoopBreakGood) { @@ -4579,6 +4579,51 @@ OpFunctionEnd EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); } +TEST_F(ValidateCFG, BranchConditionalDifferentTargetsPre1p6) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%bool = OpTypeBool +%undef = OpUndef %bool +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpBranchConditional %undef %target %target +%target = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); +} + +TEST_F(ValidateCFG, BranchConditionalDifferentTargetsPost1p6) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%bool = OpTypeBool +%undef = OpUndef %bool +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpBranchConditional %undef %target %target +%target = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_6); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_6)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("In SPIR-V 1.6 or later, True Label and False Label " + "must be different labels")); +} + } // namespace } // namespace val } // namespace spvtools diff --git a/test/val/val_conversion_test.cpp b/test/val/val_conversion_test.cpp index b9802ece..94bd27db 100644 --- a/test/val/val_conversion_test.cpp +++ b/test/val/val_conversion_test.cpp @@ -1557,7 +1557,7 @@ OpExtension "SPV_EXT_physical_storage_buffer" OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft -OpDecorate %val1 RestrictPointerEXT +OpDecorate %val1 RestrictPointer %uint64 = OpTypeInt 64 0 %u64_1 = OpConstant %uint64 1 %ptr = OpTypePointer PhysicalStorageBuffer %uint64 @@ -1615,7 +1615,7 @@ OpExtension "SPV_EXT_physical_storage_buffer" OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft -OpDecorate %val1 RestrictPointerEXT +OpDecorate %val1 RestrictPointer %uint32 = OpTypeInt 32 0 %uint64 = OpTypeInt 64 0 %ptr = OpTypePointer PhysicalStorageBuffer %uint64 diff --git a/test/val/val_data_test.cpp b/test/val/val_data_test.cpp index 1d4c0e04..eef08a1e 100644 --- a/test/val/val_data_test.cpp +++ b/test/val/val_data_test.cpp @@ -767,6 +767,8 @@ TEST_F(ValidateData, vulkan_RTA_not_at_end_of_struct) { CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_1); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("In Vulkan, OpTypeRuntimeArray must only be used for " "the last member of an OpTypeStruct\n %_struct_3 = " "OpTypeStruct %_runtimearr_uint %uint\n")); diff --git a/test/val/val_decoration_test.cpp b/test/val/val_decoration_test.cpp index f2953edc..2db44a4a 100644 --- a/test/val/val_decoration_test.cpp +++ b/test/val/val_decoration_test.cpp @@ -226,6 +226,7 @@ TEST_F(ValidateDecorations, StructAllMembersHaveBuiltInDecorationsGood) { OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 + OpDecorate %_struct_1 Block OpMemberDecorate %_struct_1 0 BuiltIn Position OpMemberDecorate %_struct_1 1 BuiltIn Position OpMemberDecorate %_struct_1 2 BuiltIn Position @@ -243,6 +244,7 @@ TEST_F(ValidateDecorations, MixedBuiltInDecorationsBad) { OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 + OpDecorate %_struct_1 Block OpMemberDecorate %_struct_1 0 BuiltIn Position OpMemberDecorate %_struct_1 1 BuiltIn Position %float = OpTypeFloat 32 @@ -265,6 +267,7 @@ TEST_F(ValidateDecorations, StructContainsBuiltInStructBad) { OpCapability Shader OpCapability Linkage OpMemoryModel Logical GLSL450 + OpDecorate %_struct_1 Block OpMemberDecorate %_struct_1 0 BuiltIn Position OpMemberDecorate %_struct_1 1 BuiltIn Position OpMemberDecorate %_struct_1 2 BuiltIn Position @@ -305,6 +308,8 @@ TEST_F(ValidateDecorations, MultipleBuiltInObjectsConsumedByOpEntryPointBad) { OpEntryPoint Geometry %main "main" %in_1 %in_2 OpExecutionMode %main InputPoints OpExecutionMode %main OutputPoints + OpDecorate %struct_1 Block + OpDecorate %struct_2 Block OpMemberDecorate %struct_1 0 BuiltIn InvocationId OpMemberDecorate %struct_2 0 BuiltIn Position %int = OpTypeInt 32 1 @@ -339,6 +344,8 @@ TEST_F(ValidateDecorations, OpEntryPoint Geometry %main "main" %in_1 %out_1 OpExecutionMode %main InputPoints OpExecutionMode %main OutputPoints + OpDecorate %struct_1 Block + OpDecorate %struct_2 Block OpMemberDecorate %struct_1 0 BuiltIn InvocationId OpMemberDecorate %struct_2 0 BuiltIn Position %int = OpTypeInt 32 1 @@ -560,6 +567,8 @@ OpFunctionEnd CompileSuccessfully(spirv, env); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Location-04915")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("A BuiltIn variable (id 2) cannot have any Location or " "Component decorations")); } @@ -594,10 +603,354 @@ OpFunctionEnd CompileSuccessfully(spirv, env); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Location-04915")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("A BuiltIn variable (id 2) cannot have any Location or " "Component decorations")); } +TEST_F(ValidateDecorations, LocationDecorationOnNumericTypeBad) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %fragCoord + OpExecutionMode %main OriginUpperLeft + OpDecorate %fragCoord Location 0 + OpDecorate %v4float Location 1 + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%ptr_v4float = OpTypePointer Output %v4float + %fragCoord = OpVariable %ptr_v4float Output +%non_interface = OpVariable %ptr_v4float Output + %main = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Location decoration on target <id> '3[%v4float]' must " + "be a variable")); +} + +TEST_F(ValidateDecorations, LocationDecorationOnStructBad) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %fragCoord + OpExecutionMode %main OriginUpperLeft + OpDecorate %fragCoord Location 0 + OpDecorate %struct Location 1 + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %struct = OpTypeStruct %float + %v4float = OpTypeVector %float 4 +%ptr_v4float = OpTypePointer Output %v4float + %fragCoord = OpVariable %ptr_v4float Output +%non_interface = OpVariable %ptr_v4float Output + %main = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Location decoration on target <id> '3[%_struct_3]' " + "must be a variable")); +} + +TEST_F(ValidateDecorations, + LocationDecorationUnusedNonInterfaceVariableVulkan_Ignored) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %fragCoord + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpDecorate %fragCoord Location 0 + OpDecorate %non_interface Location 1 + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%ptr_v4float = OpTypePointer Output %v4float + %fragCoord = OpVariable %ptr_v4float Output +%non_interface = OpVariable %ptr_v4float Output + %main = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); + EXPECT_EQ(getDiagnosticString(), ""); +} + +TEST_F(ValidateDecorations, + LocationDecorationNonInterfaceStructVulkan_Ignored) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %fragCoord + OpExecutionMode %main OriginUpperLeft + OpDecorate %fragCoord Location 0 + OpMemberDecorate %block 0 Location 2 + OpMemberDecorate %block 0 Component 1 + OpDecorate %block Block + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %vec3 = OpTypeVector %float 3 +%outvar_ptr = OpTypePointer Output %vec3 + %fragCoord = OpVariable %outvar_ptr Output + %block = OpTypeStruct %vec3 + %invar_ptr = OpTypePointer Input %block +%non_interface = OpVariable %invar_ptr Input + %main = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); + EXPECT_EQ(getDiagnosticString(), ""); +} + +TEST_F(ValidateDecorations, LocationDecorationNonInterfaceStructVulkanGood) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %fragCoord %interface + OpExecutionMode %main OriginUpperLeft + OpDecorate %fragCoord Location 0 + OpMemberDecorate %block 0 Location 2 + OpMemberDecorate %block 0 Component 1 + OpDecorate %block Block + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %vec3 = OpTypeVector %float 3 +%outvar_ptr = OpTypePointer Output %vec3 + %fragCoord = OpVariable %outvar_ptr Output + %block = OpTypeStruct %vec3 + %invar_ptr = OpTypePointer Input %block + %interface = OpVariable %invar_ptr Input ;; this variable is unused. Ignore it + %main = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); +} + +TEST_F(ValidateDecorations, LocationDecorationVariableNonStructVulkanBad) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %fragCoord %nonblock_var + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpDecorate %fragCoord Location 0 + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%ptr_v4float = OpTypePointer Output %v4float + %fragCoord = OpVariable %ptr_v4float Output +%nonblock_var = OpVariable %ptr_v4float Output + %main = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Location-04916")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable must be decorated with a location")); +} + +TEST_F(ValidateDecorations, LocationDecorationVariableStructNoBlockVulkanBad) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %fragCoord %block_var + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpDecorate %fragCoord Location 0 + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%ptr_v4float = OpTypePointer Output %v4float + %fragCoord = OpVariable %ptr_v4float Output + %block = OpTypeStruct %v4float + %block_ptr = OpTypePointer Output %block + %block_var = OpVariable %block_ptr Output + %main = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Location-04917")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable must be decorated with a location")); +} + +TEST_F(ValidateDecorations, LocationDecorationVariableNoBlockVulkanGood) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %fragCoord %block_var + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpDecorate %fragCoord Location 0 + OpDecorate %block_var Location 1 + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%ptr_v4float = OpTypePointer Output %v4float + %fragCoord = OpVariable %ptr_v4float Output + %block = OpTypeStruct %v4float + %block_ptr = OpTypePointer Output %block + %block_var = OpVariable %block_ptr Output + %main = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); +} + +TEST_F(ValidateDecorations, LocationDecorationVariableExtraMemeberVulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %fragCoord %block_var + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpDecorate %fragCoord Location 0 + OpDecorate %block Block + OpDecorate %block_var Location 1 + OpMemberDecorate %block 0 Location 1 + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%ptr_v4float = OpTypePointer Output %v4float + %fragCoord = OpVariable %ptr_v4float Output + %block = OpTypeStruct %v4float + %block_ptr = OpTypePointer Output %block + %block_var = OpVariable %block_ptr Output + %main = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd + +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Location-04918")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Members cannot be assigned a location")); +} + +TEST_F(ValidateDecorations, LocationDecorationVariableMissingMemeberVulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %fragCoord %block_var + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpDecorate %fragCoord Location 0 + OpDecorate %block Block + OpMemberDecorate %block 0 Location 1 + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%ptr_v4float = OpTypePointer Output %v4float + %fragCoord = OpVariable %ptr_v4float Output + %block = OpTypeStruct %v4float %v4float + %block_ptr = OpTypePointer Output %block + %block_var = OpVariable %block_ptr Output + %main = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Location-04919")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Member index 1 is missing a location assignment")); +} + +TEST_F(ValidateDecorations, LocationDecorationVariableOnlyMemeberVulkanGood) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %fragCoord %block_var + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpDecorate %fragCoord Location 0 + OpDecorate %block Block + OpMemberDecorate %block 0 Location 1 + OpMemberDecorate %block 1 Location 4 + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%ptr_v4float = OpTypePointer Output %v4float + %fragCoord = OpVariable %ptr_v4float Output + %block = OpTypeStruct %v4float %v4float + %block_ptr = OpTypePointer Output %block + %block_var = OpVariable %block_ptr Output + %main = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); +} + // #version 440 // #extension GL_EXT_nonuniform_qualifier : enable // layout(binding = 1) uniform sampler2D s2d[]; @@ -2334,6 +2687,8 @@ TEST_F(ValidateDecorations, VulkanBufferBlockOnStorageBufferBad) { EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-PushConstant-06675")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("In Vulkan, BufferBlock is disallowed on variables in " "the StorageBuffer storage class")); } @@ -2528,8 +2883,10 @@ TEST_F(ValidateDecorations, VulkanPushConstantMissingBlockBad) { EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-PushConstant-06675")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("PushConstant id '2' is missing Block decoration.\n" - "From Vulkan spec, section 14.5.1:\n" + "From Vulkan spec:\n" "Such variables must be identified with a Block " "decoration")); } @@ -2680,11 +3037,13 @@ TEST_F(ValidateDecorations, VulkanMultiplePushConstantsSingleEntryPointBad) { CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpEntryPoint-06674")); EXPECT_THAT( getDiagnosticString(), HasSubstr( "Entry point id '1' uses more than one PushConstant interface.\n" - "From Vulkan spec, section 14.5.1:\n" + "From Vulkan spec:\n" "There must be no more than one push constant block " "statically used per shader entry point.")); } @@ -2791,11 +3150,13 @@ TEST_F(ValidateDecorations, CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpEntryPoint-06674")); EXPECT_THAT( getDiagnosticString(), HasSubstr( "Entry point id '1' uses more than one PushConstant interface.\n" - "From Vulkan spec, section 14.5.1:\n" + "From Vulkan spec:\n" "There must be no more than one push constant block " "statically used per shader entry point.")); } @@ -2833,8 +3194,10 @@ TEST_F(ValidateDecorations, VulkanUniformMissingDescriptorSetBad) { EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-UniformConstant-06677")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("Uniform id '3' is missing DescriptorSet decoration.\n" - "From Vulkan spec, section 14.5.2:\n" + "From Vulkan spec:\n" "These variables must have DescriptorSet and Binding " "decorations specified")); } @@ -2872,8 +3235,10 @@ TEST_F(ValidateDecorations, VulkanUniformMissingBindingBad) { EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-UniformConstant-06677")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("Uniform id '3' is missing Binding decoration.\n" - "From Vulkan spec, section 14.5.2:\n" + "From Vulkan spec:\n" "These variables must have DescriptorSet and Binding " "decorations specified")); } @@ -2903,10 +3268,12 @@ TEST_F(ValidateDecorations, VulkanUniformConstantMissingDescriptorSetBad) { CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-UniformConstant-06677")); EXPECT_THAT( getDiagnosticString(), HasSubstr("UniformConstant id '2' is missing DescriptorSet decoration.\n" - "From Vulkan spec, section 14.5.2:\n" + "From Vulkan spec:\n" "These variables must have DescriptorSet and Binding " "decorations specified")); } @@ -2936,10 +3303,12 @@ TEST_F(ValidateDecorations, VulkanUniformConstantMissingBindingBad) { CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-UniformConstant-06677")); EXPECT_THAT( getDiagnosticString(), HasSubstr("UniformConstant id '2' is missing Binding decoration.\n" - "From Vulkan spec, section 14.5.2:\n" + "From Vulkan spec:\n" "These variables must have DescriptorSet and Binding " "decorations specified")); } @@ -2976,10 +3345,12 @@ TEST_F(ValidateDecorations, VulkanStorageBufferMissingDescriptorSetBad) { CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-UniformConstant-06677")); EXPECT_THAT( getDiagnosticString(), HasSubstr("StorageBuffer id '3' is missing DescriptorSet decoration.\n" - "From Vulkan spec, section 14.5.2:\n" + "From Vulkan spec:\n" "These variables must have DescriptorSet and Binding " "decorations specified")); } @@ -3017,8 +3388,10 @@ TEST_F(ValidateDecorations, VulkanStorageBufferMissingBindingBad) { EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-UniformConstant-06677")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("StorageBuffer id '3' is missing Binding decoration.\n" - "From Vulkan spec, section 14.5.2:\n" + "From Vulkan spec:\n" "These variables must have DescriptorSet and Binding " "decorations specified")); } @@ -3061,10 +3434,12 @@ TEST_F(ValidateDecorations, CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-UniformConstant-06677")); EXPECT_THAT( getDiagnosticString(), HasSubstr("StorageBuffer id '3' is missing DescriptorSet decoration.\n" - "From Vulkan spec, section 14.5.2:\n" + "From Vulkan spec:\n" "These variables must have DescriptorSet and Binding " "decorations specified")); } @@ -5590,16 +5965,16 @@ OpFunctionEnd TEST_F(ValidateDecorations, PSBAliasedRestrictPointerSuccess) { const std::string body = R"( -OpCapability PhysicalStorageBufferAddressesEXT +OpCapability PhysicalStorageBufferAddresses OpCapability Int64 OpCapability Shader OpExtension "SPV_EXT_physical_storage_buffer" -OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft -OpDecorate %val1 RestrictPointerEXT +OpDecorate %val1 RestrictPointer %uint64 = OpTypeInt 64 0 -%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64 +%ptr = OpTypePointer PhysicalStorageBuffer %uint64 %pptr_f = OpTypePointer Function %ptr %void = OpTypeVoid %voidfn = OpTypeFunction %void @@ -5616,15 +5991,15 @@ OpFunctionEnd TEST_F(ValidateDecorations, PSBAliasedRestrictPointerMissing) { const std::string body = R"( -OpCapability PhysicalStorageBufferAddressesEXT +OpCapability PhysicalStorageBufferAddresses OpCapability Int64 OpCapability Shader OpExtension "SPV_EXT_physical_storage_buffer" -OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft %uint64 = OpTypeInt 64 0 -%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64 +%ptr = OpTypePointer PhysicalStorageBuffer %uint64 %pptr_f = OpTypePointer Function %ptr %void = OpTypeVoid %voidfn = OpTypeFunction %void @@ -5638,23 +6013,23 @@ OpFunctionEnd CompileSuccessfully(body.c_str()); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("expected AliasedPointerEXT or RestrictPointerEXT for " - "PhysicalStorageBufferEXT pointer")); + HasSubstr("expected AliasedPointer or RestrictPointer for " + "PhysicalStorageBuffer pointer")); } TEST_F(ValidateDecorations, PSBAliasedRestrictPointerBoth) { const std::string body = R"( -OpCapability PhysicalStorageBufferAddressesEXT +OpCapability PhysicalStorageBufferAddresses OpCapability Int64 OpCapability Shader OpExtension "SPV_EXT_physical_storage_buffer" -OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft -OpDecorate %val1 RestrictPointerEXT -OpDecorate %val1 AliasedPointerEXT +OpDecorate %val1 RestrictPointer +OpDecorate %val1 AliasedPointer %uint64 = OpTypeInt 64 0 -%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64 +%ptr = OpTypePointer PhysicalStorageBuffer %uint64 %pptr_f = OpTypePointer Function %ptr %void = OpTypeVoid %voidfn = OpTypeFunction %void @@ -5667,24 +6042,23 @@ OpFunctionEnd CompileSuccessfully(body.c_str()); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("can't specify both AliasedPointerEXT and RestrictPointerEXT " - "for PhysicalStorageBufferEXT pointer")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("can't specify both AliasedPointer and RestrictPointer " + "for PhysicalStorageBuffer pointer")); } TEST_F(ValidateDecorations, PSBAliasedRestrictFunctionParamSuccess) { const std::string body = R"( -OpCapability PhysicalStorageBufferAddressesEXT +OpCapability PhysicalStorageBufferAddresses OpCapability Int64 OpCapability Shader OpExtension "SPV_EXT_physical_storage_buffer" -OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft OpDecorate %fparam Restrict %uint64 = OpTypeInt 64 0 -%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64 +%ptr = OpTypePointer PhysicalStorageBuffer %uint64 %void = OpTypeVoid %voidfn = OpTypeFunction %void %fnptr = OpTypeFunction %void %ptr @@ -5705,15 +6079,15 @@ OpFunctionEnd TEST_F(ValidateDecorations, PSBAliasedRestrictFunctionParamMissing) { const std::string body = R"( -OpCapability PhysicalStorageBufferAddressesEXT +OpCapability PhysicalStorageBufferAddresses OpCapability Int64 OpCapability Shader OpExtension "SPV_EXT_physical_storage_buffer" -OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft %uint64 = OpTypeInt 64 0 -%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64 +%ptr = OpTypePointer PhysicalStorageBuffer %uint64 %void = OpTypeVoid %voidfn = OpTypeFunction %void %fnptr = OpTypeFunction %void %ptr @@ -5732,22 +6106,22 @@ OpFunctionEnd ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("expected Aliased or Restrict for " - "PhysicalStorageBufferEXT pointer")); + "PhysicalStorageBuffer pointer")); } TEST_F(ValidateDecorations, PSBAliasedRestrictFunctionParamBoth) { const std::string body = R"( -OpCapability PhysicalStorageBufferAddressesEXT +OpCapability PhysicalStorageBufferAddresses OpCapability Int64 OpCapability Shader OpExtension "SPV_EXT_physical_storage_buffer" -OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft OpDecorate %fparam Restrict OpDecorate %fparam Aliased %uint64 = OpTypeInt 64 0 -%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64 +%ptr = OpTypePointer PhysicalStorageBuffer %uint64 %void = OpTypeVoid %voidfn = OpTypeFunction %void %fnptr = OpTypeFunction %void %ptr @@ -5766,12 +6140,12 @@ OpFunctionEnd ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("can't specify both Aliased and Restrict for " - "PhysicalStorageBufferEXT pointer")); + "PhysicalStorageBuffer pointer")); } TEST_F(ValidateDecorations, PSBFPRoundingModeSuccess) { std::string spirv = R"( -OpCapability PhysicalStorageBufferAddressesEXT +OpCapability PhysicalStorageBufferAddresses OpCapability Shader OpCapability Linkage OpCapability StorageBuffer16BitAccess @@ -5779,14 +6153,14 @@ OpExtension "SPV_EXT_physical_storage_buffer" OpExtension "SPV_KHR_storage_buffer_storage_class" OpExtension "SPV_KHR_variable_pointers" OpExtension "SPV_KHR_16bit_storage" -OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint GLCompute %main "main" OpDecorate %_ FPRoundingMode RTE -OpDecorate %half_ptr_var AliasedPointerEXT +OpDecorate %half_ptr_var AliasedPointer %half = OpTypeFloat 16 %float = OpTypeFloat 32 %float_1_25 = OpConstant %float 1.25 -%half_ptr = OpTypePointer PhysicalStorageBufferEXT %half +%half_ptr = OpTypePointer PhysicalStorageBuffer %half %half_pptr_f = OpTypePointer Function %half_ptr %void = OpTypeVoid %func = OpTypeFunction %void @@ -6806,7 +7180,9 @@ OpFunctionEnd CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); EXPECT_THAT(getDiagnosticString(), - HasSubstr("From Vulkan spec, section 14.5.2:\nSuch variables " + AnyVUID("VUID-StandaloneSpirv-PushConstant-06675")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("From Vulkan spec:\nSuch variables " "must be identified with a Block decoration")); } @@ -6834,7 +7210,9 @@ OpFunctionEnd CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); EXPECT_THAT(getDiagnosticString(), - HasSubstr("From Vulkan spec, section 14.5.2:\nSuch variables " + AnyVUID("VUID-StandaloneSpirv-PushConstant-06675")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("From Vulkan spec:\nSuch variables " "must be identified with a Block decoration")); } @@ -6863,7 +7241,9 @@ OpFunctionEnd CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); EXPECT_THAT(getDiagnosticString(), - HasSubstr("From Vulkan spec, section 14.5.2:\nSuch variables " + AnyVUID("VUID-StandaloneSpirv-PushConstant-06675")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("From Vulkan spec:\nSuch variables " "must be identified with a Block decoration")); } @@ -6935,10 +7315,11 @@ OpFunctionEnd CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("From Vulkan spec, section 14.5.2:\nSuch variables must be " - "identified with a Block or BufferBlock decoration")); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Uniform-06676")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("From Vulkan spec:\nSuch variables must be " + "identified with a Block or BufferBlock decoration")); } TEST_F(ValidateDecorations, VulkanUniformArrayMissingBlock) { @@ -6963,10 +7344,11 @@ OpFunctionEnd CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("From Vulkan spec, section 14.5.2:\nSuch variables must be " - "identified with a Block or BufferBlock decoration")); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Uniform-06676")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("From Vulkan spec:\nSuch variables must be " + "identified with a Block or BufferBlock decoration")); } TEST_F(ValidateDecorations, VulkanUniformRuntimeArrayMissingBlock) { @@ -6992,10 +7374,11 @@ OpFunctionEnd CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); - EXPECT_THAT( - getDiagnosticString(), - HasSubstr("From Vulkan spec, section 14.5.2:\nSuch variables must be " - "identified with a Block or BufferBlock decoration")); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Uniform-06676")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("From Vulkan spec:\nSuch variables must be " + "identified with a Block or BufferBlock decoration")); } TEST_F(ValidateDecorations, VulkanArrayStrideZero) { diff --git a/test/val/val_ext_inst_debug_test.cpp b/test/val/val_ext_inst_debug_test.cpp index 307a8009..554e78b0 100644 --- a/test/val/val_ext_inst_debug_test.cpp +++ b/test/val/val_ext_inst_debug_test.cpp @@ -1547,7 +1547,7 @@ OpExtension "SPV_KHR_non_semantic_info" "integer scalar type")); } -TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayFailComponentCountZero) { +TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayComponentCountZero) { const std::string src = R"( %src = OpString "simple.hlsl" %code = OpString "main() {}" @@ -1574,12 +1574,7 @@ OpExtension "SPV_KHR_non_semantic_info" CompileSuccessfully(GenerateShaderCodeForDebugInfo( src, constants, dbg_inst_header, "", extension, "Vertex")); - ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("Component Count must be OpConstant with a 32- or " - "64-bits integer scalar type or DebugGlobalVariable or " - "DebugLocalVariable with a 32- or 64-bits unsigned " - "integer scalar type")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } TEST_F(ValidateVulkan100DebugInfo, DebugTypeArrayFailVariableSizeTypeFloat) { @@ -1869,6 +1864,178 @@ OpExtension "SPV_KHR_non_semantic_info" "integer less than or equal to 4")); } +TEST_F(ValidateVulkan100DebugInfo, DebugTypeMatrix) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +%true = OpConstantTrue %bool +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4 +%mfloat_info = OpExtInst %void %DbgExt DebugTypeMatrix %vfloat_info %u32_4 %true +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeMatrixFailVectorTypeType) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +%true = OpConstantTrue %bool +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4 +%mfloat_info = OpExtInst %void %DbgExt DebugTypeMatrix %dbg_src %u32_4 %true +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Vector Type must be a result id of " + "DebugTypeVector")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeMatrixFailVectorCountType) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +%true = OpConstantTrue %bool +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4 +%mfloat_info = OpExtInst %void %DbgExt DebugTypeMatrix %vfloat_info %dbg_src %true +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Vector Count must be a result id of " + "32-bit unsigned OpConstant")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeMatrixFailVectorCountZero) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +%true = OpConstantTrue %bool +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4 +%mfloat_info = OpExtInst %void %DbgExt DebugTypeMatrix %vfloat_info %u32_0 %true +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Vector Count must be positive " + "integer less than or equal to 4")); +} + +TEST_F(ValidateVulkan100DebugInfo, DebugTypeMatrixFailVectorCountFive) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string constants = R"( +%u32_4 = OpConstant %u32 4 +%u32_5 = OpConstant %u32 5 +%u32_32 = OpConstant %u32 32 +%true = OpConstantTrue %bool +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit %u32_2 %u32_4 %dbg_src %u32_5 +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %u32_32 %u32_3 %u32_0 +%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %float_info %u32_4 +%mfloat_info = OpExtInst %void %DbgExt DebugTypeMatrix %vfloat_info %u32_5 %true +)"; + + const std::string extension = R"( +OpExtension "SPV_KHR_non_semantic_info" +%DbgExt = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, constants, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Vector Count must be positive " + "integer less than or equal to 4")); +} + TEST_F(ValidateOpenCL100DebugInfo, DebugTypedef) { const std::string src = R"( %src = OpString "simple.hlsl" diff --git a/test/val/val_ext_inst_test.cpp b/test/val/val_ext_inst_test.cpp index 2b6df04d..a2109db0 100644 --- a/test/val/val_ext_inst_test.cpp +++ b/test/val/val_ext_inst_test.cpp @@ -6045,6 +6045,18 @@ OpMemoryModel Logical GLSL450 "declared without SPV_KHR_non_semantic_info")); } +TEST_F(ValidateClspvReflection, DoesNotRequiresNonSemanticExtensionPost1p6) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +%1 = OpExtInstImport "NonSemantic.ClspvReflection.1" +OpMemoryModel Logical GLSL450 +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_6); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_6)); +} + TEST_F(ValidateClspvReflection, MissingVersion) { const std::string text = R"( OpCapability Shader diff --git a/test/val/val_extension_spv_khr_terminate_invocation.cpp b/test/val/val_extension_spv_khr_terminate_invocation.cpp index 4cabf9e2..8d924149 100644 --- a/test/val/val_extension_spv_khr_terminate_invocation.cpp +++ b/test/val/val_extension_spv_khr_terminate_invocation.cpp @@ -55,7 +55,7 @@ TEST_F(ValidateSpvKHRTerminateInvocation, Valid) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateSpvKHRTerminateInvocation, RequiresExtension) { +TEST_F(ValidateSpvKHRTerminateInvocation, RequiresExtensionPre1p6) { const std::string str = R"( OpCapability Shader OpMemoryModel Logical Simple @@ -72,9 +72,30 @@ TEST_F(ValidateSpvKHRTerminateInvocation, RequiresExtension) { )"; CompileSuccessfully(str.c_str()); EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("TerminateInvocation requires one of the following " - "extensions: SPV_KHR_terminate_invocation")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "TerminateInvocation requires SPIR-V version 1.6 at minimum or one " + "of the following extensions: SPV_KHR_terminate_invocation")); +} + +TEST_F(ValidateSpvKHRTerminateInvocation, RequiresNoExtensionPost1p6) { + const std::string str = R"( + OpCapability Shader + OpMemoryModel Logical Simple + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + + %void = OpTypeVoid + %void_fn = OpTypeFunction %void + + %main = OpFunction %void None %void_fn + %entry = OpLabel + OpTerminateInvocation + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str(), SPV_ENV_UNIVERSAL_1_6); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_6)); } TEST_F(ValidateSpvKHRTerminateInvocation, RequiresShaderCapability) { diff --git a/test/val/val_fixtures.h b/test/val/val_fixtures.h index acbe0e57..98d8d32a 100644 --- a/test/val/val_fixtures.h +++ b/test/val/val_fixtures.h @@ -40,8 +40,10 @@ class ValidateBase : public ::testing::Test, // Assembles the given SPIR-V text, checks that it fails to assemble, // and returns resulting diagnostic. No internal state is updated. + // Setting the desired_result to SPV_SUCCESS is used to allow all results std::string CompileFailure(std::string code, - spv_target_env env = SPV_ENV_UNIVERSAL_1_0); + spv_target_env env = SPV_ENV_UNIVERSAL_1_0, + spv_result_t desired_result = SPV_SUCCESS); // Checks that 'code' is valid SPIR-V text representation and stores the // binary version for further method calls. @@ -108,11 +110,17 @@ void ValidateBase<T>::TearDown() { template <typename T> std::string ValidateBase<T>::CompileFailure(std::string code, - spv_target_env env) { + spv_target_env env, + spv_result_t desired_result) { spv_diagnostic diagnostic = nullptr; - EXPECT_NE(SPV_SUCCESS, - spvTextToBinary(ScopedContext(env).context, code.c_str(), - code.size(), &binary_, &diagnostic)); + spv_result_t actual_result = + spvTextToBinary(ScopedContext(env).context, code.c_str(), code.size(), + &binary_, &diagnostic); + EXPECT_NE(SPV_SUCCESS, actual_result); + // optional check for exact result + if (desired_result != SPV_SUCCESS) { + EXPECT_EQ(actual_result, desired_result); + } std::string result(diagnostic->error); spvDiagnosticDestroy(diagnostic); return result; diff --git a/test/val/val_id_test.cpp b/test/val/val_id_test.cpp index ac057493..69257a58 100644 --- a/test/val/val_id_test.cpp +++ b/test/val/val_id_test.cpp @@ -1015,7 +1015,7 @@ TEST_F(ValidateIdWithMessage, OpTypeRuntimeArrayBad) { "type.")); } // TODO: Object of this type can only be created with OpVariable using the -// Unifrom Storage Class +// Uniform Storage Class TEST_F(ValidateIdWithMessage, OpTypeStructGood) { std::string spirv = kGLSL450MemoryModel + R"( @@ -2859,7 +2859,7 @@ TEST_F(ValidateIdWithMessage, OpStoreTypeRelaxedStruct) { EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -// Same code as the last test excect for an extra decoration on one of the +// Same code as the last test except for an extra decoration on one of the // members. With the relaxed rules, the code is still valid. TEST_F(ValidateIdWithMessage, OpStoreTypeRelaxedStructWithExtraDecoration) { std::string spirv = kGLSL450MemoryModel + R"( diff --git a/test/val/val_image_test.cpp b/test/val/val_image_test.cpp index 11b14fb0..76af29cc 100644 --- a/test/val/val_image_test.cpp +++ b/test/val/val_image_test.cpp @@ -61,8 +61,11 @@ OpCapability ImageBuffer // In 1.4, the entry point must list all module-scope variables used. Just // list all of them. - std::string interface_vars = (env != SPV_ENV_UNIVERSAL_1_4) ? "" : - R"( + // + // For Vulkan, anything Location decoration needs to be an interface variable + std::string interface_vars = + (env != SPV_ENV_UNIVERSAL_1_4) ? "%input_flat_u32" : + R"( %uniform_image_f32_1d_0001 %uniform_image_f32_1d_0002_rgba32f %uniform_image_f32_2d_0001 @@ -243,6 +246,11 @@ OpDecorate %input_flat_u32 Location 0 %uniform_image_u32_2d_0001 = OpVariable %ptr_image_u32_2d_0001 UniformConstant %type_sampled_image_u32_2d_0001 = OpTypeSampledImage %type_image_u32_2d_0001 +%type_image_u32_3d_0001 = OpTypeImage %u32 3D 0 0 0 1 Unknown +%ptr_image_u32_3d_0001 = OpTypePointer UniformConstant %type_image_u32_3d_0001 +%uniform_image_u32_3d_0001 = OpVariable %ptr_image_u32_3d_0001 UniformConstant +%type_sampled_image_u32_3d_0001 = OpTypeSampledImage %type_image_u32_3d_0001 + %type_image_u32_2d_0002 = OpTypeImage %u32 2D 0 0 0 2 Unknown %ptr_image_u32_2d_0002 = OpTypePointer UniformConstant %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 = OpVariable %ptr_image_u32_2d_0002 UniformConstant @@ -269,6 +277,11 @@ OpDecorate %input_flat_u32 Location 0 %uniform_image_f32_3d_0111 = OpVariable %ptr_image_f32_3d_0111 UniformConstant %type_sampled_image_f32_3d_0111 = OpTypeSampledImage %type_image_f32_3d_0111 +%type_image_f32_3d_0001 = OpTypeImage %f32 3D 0 0 0 1 Unknown +%ptr_image_f32_3d_0001 = OpTypePointer UniformConstant %type_image_f32_3d_0001 +%uniform_image_f32_3d_0001 = OpVariable %ptr_image_f32_3d_0001 UniformConstant +%type_sampled_image_f32_3d_0001 = OpTypeSampledImage %type_image_f32_3d_0001 + %type_image_f32_cube_0101 = OpTypeImage %f32 Cube 0 1 0 1 Unknown %ptr_image_f32_cube_0101 = OpTypePointer UniformConstant %type_image_f32_cube_0101 %uniform_image_f32_cube_0101 = OpVariable %ptr_image_f32_cube_0101 UniformConstant @@ -735,6 +748,34 @@ TEST_F(ValidateImage, TypeImageWrongSampledForSubpassData) { HasSubstr("Dim SubpassData requires Sampled to be 2")); } +TEST_F(ValidateImage, TypeImageWrongSampledForSubpassDataVulkan) { + const std::string code = GetShaderHeader("OpCapability InputAttachment\n") + + R"( +%img_type = OpTypeImage %f32 SubpassData 0 0 0 1 Unknown +)" + TrivialMain(); + + CompileSuccessfully(code.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpTypeImage-06214")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Dim SubpassData requires Sampled to be 2")); +} + +TEST_F(ValidateImage, TypeImageWrongArrayForSubpassDataVulkan) { + const std::string code = GetShaderHeader("OpCapability InputAttachment\n") + + R"( +%img_type = OpTypeImage %f32 SubpassData 0 1 0 2 Unknown +)" + TrivialMain(); + + CompileSuccessfully(code.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpTypeImage-06214")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Dim SubpassData requires Arrayed to be 0")); +} + TEST_F(ValidateImage, TypeImage_OpenCL_Sampled0_OK) { const std::string code = GetKernelHeader() + R"( %img_type = OpTypeImage %void 2D 0 0 0 0 Unknown ReadOnly @@ -1002,6 +1043,26 @@ TEST_F(ValidateImage, SampledImageNotSampler) { HasSubstr("Expected Sampler to be of type OpTypeSampler")); } +TEST_F(ValidateImage, SampledImageIsStorage) { + const std::string declarations = R"( +%type_sampled_image_f32_2d_0002 = OpTypeSampledImage %type_image_f32_2d_0002 +)"; + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0002 %uniform_image_f32_2d_0002 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0002 %img %sampler +)"; + + CompileSuccessfully(GenerateShaderCode(body, "", "Fragment", "", + SPV_ENV_UNIVERSAL_1_0, "GLSL450", + declarations) + .c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Sampled image type requires an image type with " + "\"Sampled\" operand set to 0 or 1")); +} + TEST_F(ValidateImage, ImageTexelPointerSuccess) { const std::string body = R"( %texel_ptr = OpImageTexelPointer %ptr_Image_u32 %private_image_u32_buffer_0002_r32ui %u32_0 %u32_0 @@ -1059,7 +1120,7 @@ TEST_F(ValidateImage, ImageTexelPointerImageNotResultTypePointer) { CompileSuccessfully(GenerateShaderCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 136[%136] cannot be a " + EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 145[%145] cannot be a " "type")); } @@ -2290,6 +2351,24 @@ TEST_F(ValidateImage, SampleDrefImplicitLodWrongDrefType) { HasSubstr("Expected Dref to be of 32-bit float type")); } +TEST_F(ValidateImage, SampleDrefImplicitLodWrongDimVulkan) { + const std::string body = R"( +%img = OpLoad %type_image_u32_3d_0001 %uniform_image_u32_3d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_u32_3d_0001 %img %sampler +%res1 = OpImageSampleDrefImplicitLod %u32 %simg %f32vec3_hhh %f32_1 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "", "Fragment", "", SPV_ENV_VULKAN_1_0).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpImage-04777")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("In Vulkan, OpImage*Dref* instructions must not use " + "images with a 3D Dim")); +} + TEST_F(ValidateImage, SampleDrefExplicitLodSuccess) { const std::string body = R"( %img = OpLoad %type_image_s32_3d_0001 %uniform_image_s32_3d_0001 @@ -3131,7 +3210,7 @@ TEST_F(ValidateImage, GatherConstOffsetsArrayNotVector) { CompileSuccessfully(GenerateShaderCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Expected Image Operand ConstOffsets array componenets " + HasSubstr("Expected Image Operand ConstOffsets array components " "to be int vectors of size 2")); } @@ -3146,7 +3225,7 @@ TEST_F(ValidateImage, GatherConstOffsetsArrayVectorWrongSize) { CompileSuccessfully(GenerateShaderCode(body).c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Expected Image Operand ConstOffsets array componenets " + HasSubstr("Expected Image Operand ConstOffsets array components " "to be int vectors of size 2")); } @@ -3246,6 +3325,23 @@ TEST_F(ValidateImage, DrefGatherWrongDrefType) { HasSubstr("Expected Dref to be of 32-bit float type")); } +TEST_F(ValidateImage, DrefGatherWrongDimVulkan) { + const std::string body = R"( +%img = OpLoad %type_image_f32_3d_0001 %uniform_image_f32_3d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_3d_0001 %img %sampler +%res1 = OpImageDrefGather %f32vec4 %simg %f32vec4_0000 %f32_0_5 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "", "Fragment", "", SPV_ENV_VULKAN_1_0).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpImage-04777")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Image 'Dim' to be 2D, Cube, or Rect")); +} + TEST_F(ValidateImage, ReadSuccess1) { const std::string body = R"( %img = OpLoad %type_image_u32_2d_0002 %uniform_image_u32_2d_0002 @@ -5464,7 +5560,8 @@ TEST_F(ValidateImage, SignExtendV13Bad) { )"; EXPECT_THAT(CompileFailure(GenerateShaderCode(body, "", "Fragment", "", - SPV_ENV_UNIVERSAL_1_3)), + SPV_ENV_UNIVERSAL_1_3), + SPV_ENV_UNIVERSAL_1_3, SPV_ERROR_WRONG_VERSION), HasSubstr("Invalid image operand 'SignExtend'")); } @@ -5475,7 +5572,8 @@ TEST_F(ValidateImage, ZeroExtendV13Bad) { )"; EXPECT_THAT(CompileFailure(GenerateShaderCode(body, "", "Fragment", "", - SPV_ENV_UNIVERSAL_1_3)), + SPV_ENV_UNIVERSAL_1_3), + SPV_ENV_UNIVERSAL_1_3, SPV_ERROR_WRONG_VERSION), HasSubstr("Invalid image operand 'ZeroExtend'")); } @@ -6099,6 +6197,57 @@ OpFunctionEnd "execution mode for GLCompute execution model")); } +TEST_F(ValidateImage, TypeSampledImageNotBufferPost1p6) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpCapability SampledBuffer +OpMemoryModel Logical GLSL450 +%float = OpTypeFloat 32 +%image = OpTypeImage %float Buffer 0 0 0 1 Unknown +%sampled = OpTypeSampledImage %image +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_6); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_6)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("In SPIR-V 1.6 or later, sampled image dimension must " + "not be Buffer")); +} + +TEST_F(ValidateImage, NonTemporalImage) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 " " %4 %5 +OpExecutionMode %2 OriginUpperLeft +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%12 = OpTypeImage %float 2D 0 0 0 1 Rgba8ui +%13 = OpTypeSampledImage %12 +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%5 = OpVariable %_ptr_UniformConstant_13 UniformConstant +%_ptr_Input_v4float = OpTypePointer Input %v4float +%4 = OpVariable %_ptr_Input_v4float Input +%v2float = OpTypeVector %float 2 +%float_1_35631564en19 = OpConstant %float 1.35631564e-19 +%2 = OpFunction %void None %8 +%8224 = OpLabel +%6 = OpLoad %13 %5 +%19 = OpLoad %v4float %4 +%20 = OpVectorShuffle %v2float %19 %19 0 1 +%21 = OpVectorTimesScalar %v2float %20 %float_1_35631564en19 +%65312 = OpImageSampleImplicitLod %v4float %6 %21 Nontemporal +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_6); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_6)); +} + } // namespace } // namespace val } // namespace spvtools diff --git a/test/val/val_interfaces_test.cpp b/test/val/val_interfaces_test.cpp index a01fc19b..bec8d026 100644 --- a/test/val/val_interfaces_test.cpp +++ b/test/val/val_interfaces_test.cpp @@ -448,6 +448,8 @@ OpFunctionEnd CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Location-04918")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("Members cannot be assigned a location")); } @@ -476,6 +478,8 @@ OpFunctionEnd CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Location-04918")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("Members cannot be assigned a location")); } @@ -1496,6 +1500,42 @@ OpFunctionEnd EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); } +TEST_F(ValidateInterfacesTest, StructWithBuiltinsMissingBlock_Bad) { + // See https://github.com/KhronosGroup/SPIRV-Registry/issues/134 + // + // When a shader input or output is a struct that does not have Block, + // then it must have a Location. + // But BuiltIns must not have locations. + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in +OpExecutionMode %main OriginUpperLeft +; %struct needs a Block decoration +OpMemberDecorate %struct 0 BuiltIn Position +%void = OpTypeVoid +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%struct = OpTypeStruct %v4float +%in_ptr = OpTypePointer Input %struct +%in = OpVariable %in_ptr Input +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Location-04919")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Interface struct has no Block decoration but has BuiltIn members.")); +} + } // namespace } // namespace val } // namespace spvtools diff --git a/test/val/val_limits_test.cpp b/test/val/val_limits_test.cpp index 8fb80a46..364d514e 100644 --- a/test/val/val_limits_test.cpp +++ b/test/val/val_limits_test.cpp @@ -750,7 +750,7 @@ TEST_F(ValidateLimits, CustomizedControlFlowDepthBad) { } // Valid. The purpose here is to test the CFG depth calculation code when a loop -// continue target is the loop iteself. It also exercises the case where a loop +// continue target is the loop itself. It also exercises the case where a loop // is unreachable. TEST_F(ValidateLimits, ControlFlowNoEntryToLoopGood) { std::string str = header + R"( diff --git a/test/val/val_literals_test.cpp b/test/val/val_literals_test.cpp index 6eadf321..7c9aad67 100644 --- a/test/val/val_literals_test.cpp +++ b/test/val/val_literals_test.cpp @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Validation tests for ilegal literals +// Validation tests for illegal literals #include <string> #include <utility> diff --git a/test/val/val_logicals_test.cpp b/test/val/val_logicals_test.cpp index b57c7433..1b76c853 100644 --- a/test/val/val_logicals_test.cpp +++ b/test/val/val_logicals_test.cpp @@ -1053,18 +1053,18 @@ TEST_F(ValidateLogicals, OpSGreaterThanDifferentBitWidth) { TEST_F(ValidateLogicals, PSBSelectSuccess) { const std::string body = R"( -OpCapability PhysicalStorageBufferAddressesEXT +OpCapability PhysicalStorageBufferAddresses OpCapability Int64 OpCapability Shader OpExtension "SPV_EXT_physical_storage_buffer" -OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft -OpDecorate %val1 AliasedPointerEXT +OpDecorate %val1 AliasedPointer %uint64 = OpTypeInt 64 0 %bool = OpTypeBool %true = OpConstantTrue %bool -%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64 +%ptr = OpTypePointer PhysicalStorageBuffer %uint64 %pptr_f = OpTypePointer Function %ptr %void = OpTypeVoid %voidfn = OpTypeFunction %void diff --git a/test/val/val_memory_test.cpp b/test/val/val_memory_test.cpp index 2a884c4d..5fb43f7b 100644 --- a/test/val/val_memory_test.cpp +++ b/test/val/val_memory_test.cpp @@ -833,11 +833,45 @@ TEST_F(ValidateMemory, VulkanPushConstantNotStructBad) { )"; CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("PushConstant OpVariable <id> '6[%6]' has illegal " - "type.\nFrom Vulkan spec, section 14.5.1:\n" - "Such variables must be typed as OpTypeStruct, " - "or an array of this type")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("PushConstant OpVariable <id> '6[%6]' has illegal " + "type.\nFrom Vulkan spec, Push Constant Interface section:\n" + "Such variables must be typed as OpTypeStruct")); +} + +TEST_F(ValidateMemory, VulkanPushConstantArrayOfStructBad) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + + OpDecorate %struct Block + OpMemberDecorate %struct 0 Offset 0 + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %int = OpTypeInt 32 0 + %int_1 = OpConstant %int 1 + %struct = OpTypeStruct %float + %array = OpTypeArray %struct %int_1 + %ptr = OpTypePointer PushConstant %array + %pc = OpVariable %ptr PushConstant + + %1 = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("PushConstant OpVariable <id> '10[%10]' has illegal " + "type.\nFrom Vulkan spec, Push Constant Interface section:\n" + "Such variables must be typed as OpTypeStruct")); } TEST_F(ValidateMemory, VulkanPushConstant) { @@ -1563,16 +1597,16 @@ OpFunctionEnd TEST_F(ValidateMemory, PSBLoadAlignedSuccess) { const std::string body = R"( -OpCapability PhysicalStorageBufferAddressesEXT +OpCapability PhysicalStorageBufferAddresses OpCapability Int64 OpCapability Shader OpExtension "SPV_EXT_physical_storage_buffer" -OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft -OpDecorate %val1 AliasedPointerEXT +OpDecorate %val1 AliasedPointer %uint64 = OpTypeInt 64 0 -%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64 +%ptr = OpTypePointer PhysicalStorageBuffer %uint64 %pptr_f = OpTypePointer Function %ptr %void = OpTypeVoid %voidfn = OpTypeFunction %void @@ -1585,22 +1619,22 @@ OpReturn OpFunctionEnd )"; - CompileSuccessfully(body.c_str()); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); } TEST_F(ValidateMemory, PSBLoadAlignedMissing) { const std::string body = R"( -OpCapability PhysicalStorageBufferAddressesEXT +OpCapability PhysicalStorageBufferAddresses OpCapability Int64 OpCapability Shader OpExtension "SPV_EXT_physical_storage_buffer" -OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft -OpDecorate %val1 AliasedPointerEXT +OpDecorate %val1 AliasedPointer %uint64 = OpTypeInt 64 0 -%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64 +%ptr = OpTypePointer PhysicalStorageBuffer %uint64 %pptr_f = OpTypePointer Function %ptr %void = OpTypeVoid %voidfn = OpTypeFunction %void @@ -1613,27 +1647,61 @@ OpReturn OpFunctionEnd )"; - CompileSuccessfully(body.c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-PhysicalStorageBuffer64-04708")); EXPECT_THAT( getDiagnosticString(), - HasSubstr( - "Memory accesses with PhysicalStorageBufferEXT must use Aligned")); + HasSubstr("Memory accesses with PhysicalStorageBuffer must use Aligned")); +} + +TEST_F(ValidateMemory, PSBLoadAlignedMissingWithOtherOperand) { + const std::string body = R"( +OpCapability PhysicalStorageBufferAddresses +OpCapability Int64 +OpCapability Shader +OpExtension "SPV_EXT_physical_storage_buffer" +OpMemoryModel PhysicalStorageBuffer64 GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %val1 AliasedPointer +%uint64 = OpTypeInt 64 0 +%ptr = OpTypePointer PhysicalStorageBuffer %uint64 +%pptr_f = OpTypePointer Function %ptr +%void = OpTypeVoid +%voidfn = OpTypeFunction %void +%main = OpFunction %void None %voidfn +%entry = OpLabel +%val1 = OpVariable %pptr_f Function +%val2 = OpLoad %ptr %val1 +%val3 = OpLoad %uint64 %val2 Volatile +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-PhysicalStorageBuffer64-04708")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Memory accesses with PhysicalStorageBuffer must use Aligned")); } TEST_F(ValidateMemory, PSBStoreAlignedSuccess) { const std::string body = R"( -OpCapability PhysicalStorageBufferAddressesEXT +OpCapability PhysicalStorageBufferAddresses OpCapability Int64 OpCapability Shader OpExtension "SPV_EXT_physical_storage_buffer" -OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft -OpDecorate %val1 AliasedPointerEXT +OpDecorate %val1 AliasedPointer %uint64 = OpTypeInt 64 0 %u64_1 = OpConstant %uint64 1 -%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64 +%ptr = OpTypePointer PhysicalStorageBuffer %uint64 %pptr_f = OpTypePointer Function %ptr %void = OpTypeVoid %voidfn = OpTypeFunction %void @@ -1646,23 +1714,23 @@ OpReturn OpFunctionEnd )"; - CompileSuccessfully(body.c_str()); - ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); } TEST_F(ValidateMemory, PSBStoreAlignedMissing) { const std::string body = R"( -OpCapability PhysicalStorageBufferAddressesEXT +OpCapability PhysicalStorageBufferAddresses OpCapability Int64 OpCapability Shader OpExtension "SPV_EXT_physical_storage_buffer" -OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft -OpDecorate %val1 AliasedPointerEXT +OpDecorate %val1 AliasedPointer %uint64 = OpTypeInt 64 0 %u64_1 = OpConstant %uint64 1 -%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64 +%ptr = OpTypePointer PhysicalStorageBuffer %uint64 %pptr_f = OpTypePointer Function %ptr %void = OpTypeVoid %voidfn = OpTypeFunction %void @@ -1675,27 +1743,168 @@ OpReturn OpFunctionEnd )"; - CompileSuccessfully(body.c_str()); - ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-PhysicalStorageBuffer64-04708")); EXPECT_THAT( getDiagnosticString(), - HasSubstr( - "Memory accesses with PhysicalStorageBufferEXT must use Aligned")); + HasSubstr("Memory accesses with PhysicalStorageBuffer must use Aligned")); +} + +TEST_F(ValidateMemory, PSBCopyMemoryAlignedSuccess) { + const std::string body = R"( +OpCapability PhysicalStorageBufferAddresses +OpCapability Int64 +OpCapability Shader +OpExtension "SPV_EXT_physical_storage_buffer" +OpMemoryModel PhysicalStorageBuffer64 GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %val1 AliasedPointer +%int = OpTypeInt 32 0 +%uint64 = OpTypeInt 64 0 +%u64_1 = OpConstant %uint64 1 +%ptr = OpTypePointer PhysicalStorageBuffer %uint64 +%pptr_f = OpTypePointer Function %ptr +%void = OpTypeVoid +%voidfn = OpTypeFunction %void +%main = OpFunction %void None %voidfn +%entry = OpLabel +%val1 = OpVariable %pptr_f Function +%val2 = OpLoad %ptr %val1 +%val3 = OpLoad %ptr %val1 +OpCopyMemory %val2 %val3 Aligned 4 +OpCopyMemory %val3 %val2 Aligned 4 Aligned 4 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +TEST_F(ValidateMemory, PSBCopyMemoryAlignedMissingTarget) { + const std::string body = R"( +OpCapability PhysicalStorageBufferAddresses +OpCapability Int64 +OpCapability Shader +OpExtension "SPV_EXT_physical_storage_buffer" +OpMemoryModel PhysicalStorageBuffer64 GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %val1 AliasedPointer +%int = OpTypeInt 32 0 +%uint64 = OpTypeInt 64 0 +%u64_1 = OpConstant %uint64 1 +%ptr = OpTypePointer PhysicalStorageBuffer %uint64 +%pptr_f = OpTypePointer Function %ptr +%void = OpTypeVoid +%voidfn = OpTypeFunction %void +%main = OpFunction %void None %voidfn +%entry = OpLabel +%val1 = OpVariable %pptr_f Function +%val2 = OpLoad %ptr %val1 +%val3 = OpLoad %ptr %val1 +OpCopyMemory %val2 %val3 Volatile Aligned 4 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-PhysicalStorageBuffer64-04708")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Memory accesses with PhysicalStorageBuffer must use Aligned")); +} + +TEST_F(ValidateMemory, PSBCopyMemoryAlignedMissingSource) { + const std::string body = R"( +OpCapability PhysicalStorageBufferAddresses +OpCapability Int64 +OpCapability Shader +OpExtension "SPV_EXT_physical_storage_buffer" +OpMemoryModel PhysicalStorageBuffer64 GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %val1 AliasedPointer +%int = OpTypeInt 32 0 +%uint64 = OpTypeInt 64 0 +%u64_1 = OpConstant %uint64 1 +%ptr = OpTypePointer PhysicalStorageBuffer %uint64 +%pptr_f = OpTypePointer Function %ptr +%void = OpTypeVoid +%voidfn = OpTypeFunction %void +%main = OpFunction %void None %voidfn +%entry = OpLabel +%val1 = OpVariable %pptr_f Function +%val2 = OpLoad %ptr %val1 +%val3 = OpLoad %ptr %val1 +OpCopyMemory %val2 %val3 Aligned 4 Volatile +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-PhysicalStorageBuffer64-04708")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Memory accesses with PhysicalStorageBuffer must use Aligned")); +} + +TEST_F(ValidateMemory, PSBCopyMemoryAlignedMissingBoth) { + const std::string body = R"( +OpCapability PhysicalStorageBufferAddresses +OpCapability Int64 +OpCapability Shader +OpExtension "SPV_EXT_physical_storage_buffer" +OpMemoryModel PhysicalStorageBuffer64 GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %val1 AliasedPointer +%int = OpTypeInt 32 0 +%uint64 = OpTypeInt 64 0 +%u64_1 = OpConstant %uint64 1 +%ptr = OpTypePointer PhysicalStorageBuffer %uint64 +%pptr_f = OpTypePointer Function %ptr +%void = OpTypeVoid +%voidfn = OpTypeFunction %void +%main = OpFunction %void None %voidfn +%entry = OpLabel +%val1 = OpVariable %pptr_f Function +%val2 = OpLoad %ptr %val1 +%val3 = OpLoad %ptr %val1 +OpCopyMemory %val2 %val3 Volatile +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str(), SPV_ENV_VULKAN_1_2); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-PhysicalStorageBuffer64-04708")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Memory accesses with PhysicalStorageBuffer must use Aligned")); } TEST_F(ValidateMemory, PSBVariable) { const std::string body = R"( -OpCapability PhysicalStorageBufferAddressesEXT +OpCapability PhysicalStorageBufferAddresses OpCapability Int64 OpCapability Shader OpExtension "SPV_EXT_physical_storage_buffer" -OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpMemoryModel PhysicalStorageBuffer64 GLSL450 OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft -OpDecorate %val1 AliasedPointerEXT +OpDecorate %val1 AliasedPointer %uint64 = OpTypeInt 64 0 -%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64 -%val1 = OpVariable %ptr PhysicalStorageBufferEXT +%ptr = OpTypePointer PhysicalStorageBuffer %uint64 +%val1 = OpVariable %ptr PhysicalStorageBuffer %void = OpTypeVoid %voidfn = OpTypeFunction %void %main = OpFunction %void None %voidfn @@ -1708,7 +1917,7 @@ OpFunctionEnd EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr("PhysicalStorageBufferEXT must not be used with OpVariable")); + HasSubstr("PhysicalStorageBuffer must not be used with OpVariable")); } std::string GenCoopMatLoadStoreShader(const std::string& storeMemoryAccess, @@ -2097,6 +2306,8 @@ OpFunctionEnd CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680")); EXPECT_THAT( getDiagnosticString(), HasSubstr( @@ -2162,6 +2373,8 @@ OpFunctionEnd CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680")); EXPECT_THAT( getDiagnosticString(), HasSubstr("For Vulkan with RuntimeDescriptorArrayEXT, a variable " @@ -2217,11 +2430,14 @@ OpFunctionEnd CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680")); EXPECT_THAT( getDiagnosticString(), HasSubstr( "For Vulkan, OpTypeStruct variables containing OpTypeRuntimeArray " - "must have storage class of StorageBuffer or Uniform.\n %6 = " + "must have storage class of StorageBuffer, PhysicalStorageBuffer, or " + "Uniform.\n %6 = " "OpVariable %_ptr_Workgroup__struct_4 Workgroup\n")); } @@ -2247,9 +2463,12 @@ OpFunctionEnd CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("For Vulkan, an OpTypeStruct variable containing an " "OpTypeRuntimeArray must be decorated with Block if it " - "has storage class StorageBuffer.\n %6 = OpVariable " + "has storage class StorageBuffer or " + "PhysicalStorageBuffer.\n %6 = OpVariable " "%_ptr_StorageBuffer__struct_4 StorageBuffer\n")); } @@ -2301,6 +2520,8 @@ OpFunctionEnd CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("For Vulkan, an OpTypeStruct variable containing an " "OpTypeRuntimeArray must be decorated with BufferBlock " "if it has storage class Uniform.\n %6 = OpVariable " @@ -2328,6 +2549,8 @@ OpFunctionEnd CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680")); EXPECT_THAT( getDiagnosticString(), HasSubstr( @@ -2361,6 +2584,8 @@ OpFunctionEnd CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680")); EXPECT_THAT( getDiagnosticString(), HasSubstr( @@ -2423,6 +2648,8 @@ OpFunctionEnd CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680")); EXPECT_THAT( getDiagnosticString(), HasSubstr( @@ -2459,6 +2686,8 @@ OpFunctionEnd CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680")); EXPECT_THAT( getDiagnosticString(), HasSubstr( @@ -2490,6 +2719,8 @@ OpFunctionEnd CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680")); EXPECT_THAT( getDiagnosticString(), HasSubstr("OpTypeArray Element Type <id> '5[%_runtimearr_4]' is not " @@ -2524,6 +2755,8 @@ OpFunctionEnd CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680")); EXPECT_THAT( getDiagnosticString(), HasSubstr( @@ -2558,6 +2791,8 @@ OpFunctionEnd CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680")); EXPECT_THAT( getDiagnosticString(), HasSubstr( @@ -2595,6 +2830,8 @@ OpFunctionEnd CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680")); EXPECT_THAT( getDiagnosticString(), HasSubstr( @@ -3015,8 +3252,8 @@ OpFunctionEnd EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Instruction cannot be used without a variable " - "pointers capability")); + HasSubstr("Instruction cannot for logical addressing model be " + "used without a variable pointers capability")); } } @@ -4048,7 +4285,7 @@ TEST_F(ValidateMemory, VulkanInvariantOutputSuccess) { const std::string spirv = R"( OpCapability Shader OpMemoryModel Logical GLSL450 -OpEntryPoint Vertex %main "main" +OpEntryPoint Vertex %main "main" %var OpDecorate %var Location 0 OpDecorate %var Invariant %void = OpTypeVoid @@ -4070,7 +4307,7 @@ TEST_F(ValidateMemory, VulkanInvariantInputStructSuccess) { const std::string spirv = R"( OpCapability Shader OpMemoryModel Logical GLSL450 -OpEntryPoint Fragment %main "main" +OpEntryPoint Fragment %main "main" %var OpExecutionMode %main OriginUpperLeft OpDecorate %var Location 0 OpMemberDecorate %struct 1 Invariant @@ -4259,8 +4496,10 @@ OpFunctionEnd CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_0); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); EXPECT_THAT(getDiagnosticString(), - HasSubstr("Variable initializers in Workgroup storage class are " - "limited to OpConstantNull")); + AnyVUID(" VUID-StandaloneSpirv-OpVariable-04734")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpVariable, <id> '5[%5]', initializers are limited to " + "OpConstantNull in Workgroup storage class")); } TEST_F(ValidateMemory, VulkanInitializerWithWorkgroupStorageClassGood) { diff --git a/test/val/val_modes_test.cpp b/test/val/val_modes_test.cpp index 02a61327..a37989b4 100644 --- a/test/val/val_modes_test.cpp +++ b/test/val/val_modes_test.cpp @@ -1178,6 +1178,26 @@ OpFunctionEnd HasSubstr("Expected bool scalar type as Result Type")); } +TEST_F(ValidateMode, LocalSizeIdVulkan1p3DoesNotRequireOption) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionModeId %main LocalSizeId %int_1 %int_1 %int_1 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_1 = OpConstant %int 1 +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); +} + } // namespace } // namespace val } // namespace spvtools diff --git a/test/val/val_non_semantic_test.cpp b/test/val/val_non_semantic_test.cpp index b80bb1ac..210cd1ac 100644 --- a/test/val/val_non_semantic_test.cpp +++ b/test/val/val_non_semantic_test.cpp @@ -105,7 +105,7 @@ INSTANTIATE_TEST_SUITE_P(OnlyOpExtension, ValidateNonSemanticGenerated, Values(""), Values(TestResult()))); INSTANTIATE_TEST_SUITE_P( - MissingOpExtension, ValidateNonSemanticGenerated, + MissingOpExtensionPre1p6, ValidateNonSemanticGenerated, Combine(Values(false), Values(true), Values(""), Values(""), Values(TestResult( SPV_ERROR_INVALID_DATA, @@ -190,6 +190,26 @@ OpEntryPoint Vertex %main "main" HasSubstr("ID 2[%2] has not been defined")); } +TEST_F(ValidateNonSemanticString, MissingOpExtensionPost1p6) { + const std::string spirv = R"( +OpCapability Shader +%extinst = OpExtInstImport "NonSemantic.Testing.Set" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%void = OpTypeVoid +%test = OpExtInst %void %extinst 3 +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_6); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_6)); +} + } // namespace } // namespace val } // namespace spvtools diff --git a/test/val/val_storage_test.cpp b/test/val/val_storage_test.cpp index 35f6a8d5..ae4047b9 100644 --- a/test/val/val_storage_test.cpp +++ b/test/val/val_storage_test.cpp @@ -280,7 +280,7 @@ TEST_P(ValidateStorageExecutionModel, VulkanOutsideStoreFailure) { AnyVUID("VUID-StandaloneSpirv-None-04644")); EXPECT_THAT( getDiagnosticString(), - HasSubstr("in Vulkan evironment, Output Storage Class must not be used " + HasSubstr("in Vulkan environment, Output Storage Class must not be used " "in GLCompute, RayGenerationKHR, IntersectionKHR, AnyHitKHR, " "ClosestHitKHR, MissKHR, or CallableKHR execution models")); } diff --git a/test/val/val_version_test.cpp b/test/val/val_version_test.cpp index 98565ddb..6b7c4fe8 100644 --- a/test/val/val_version_test.cpp +++ b/test/val/val_version_test.cpp @@ -74,6 +74,12 @@ std::string version(spv_target_env env) { case SPV_ENV_UNIVERSAL_1_4: case SPV_ENV_VULKAN_1_1_SPIRV_1_4: return "1.4"; + case SPV_ENV_UNIVERSAL_1_5: + case SPV_ENV_VULKAN_1_2: + return "1.5"; + case SPV_ENV_UNIVERSAL_1_6: + case SPV_ENV_VULKAN_1_3: + return "1.6"; default: return "0"; } @@ -103,8 +109,14 @@ INSTANTIATE_TEST_SUITE_P(Universal, ValidateVersion, std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_VULKAN_1_0, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_VULKAN_1_1, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_VULKAN_1_1_SPIRV_1_4,vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_VULKAN_1_2, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_VULKAN_1_3, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_OPENGL_4_0, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_OPENGL_4_1, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_OPENGL_4_2, vulkan_spirv, true), @@ -115,8 +127,14 @@ INSTANTIATE_TEST_SUITE_P(Universal, ValidateVersion, std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_VULKAN_1_0, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_VULKAN_1_1, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_VULKAN_1_2, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_VULKAN_1_3, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_OPENGL_4_0, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), @@ -127,8 +145,14 @@ INSTANTIATE_TEST_SUITE_P(Universal, ValidateVersion, std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_VULKAN_1_0, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_VULKAN_1_1, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_VULKAN_1_2, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_VULKAN_1_3, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_OPENGL_4_0, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), @@ -139,13 +163,73 @@ INSTANTIATE_TEST_SUITE_P(Universal, ValidateVersion, std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_0, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_1, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_2, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_3, vulkan_spirv, true), std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_0, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_3, vulkan_spirv, false), - std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_5, vulkan_spirv, false) + std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_5, vulkan_spirv, false), + + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_VULKAN_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_VULKAN_1_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_VULKAN_1_2, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_VULKAN_1_3, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_OPENGL_4_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_OPENGL_4_3, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_4, SPV_ENV_OPENGL_4_5, vulkan_spirv, false), + + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_VULKAN_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_VULKAN_1_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_VULKAN_1_2, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_VULKAN_1_3, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_OPENGL_4_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_OPENGL_4_3, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_5, SPV_ENV_OPENGL_4_5, vulkan_spirv, false), + + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_VULKAN_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_VULKAN_1_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_VULKAN_1_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_VULKAN_1_3, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_OPENGL_4_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_OPENGL_4_3, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_6, SPV_ENV_OPENGL_4_5, vulkan_spirv, false) ) ); @@ -156,27 +240,91 @@ INSTANTIATE_TEST_SUITE_P(Vulkan, ValidateVersion, std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_0, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_2, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_3, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_OPENGL_4_0, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_OPENGL_4_1, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_OPENGL_4_2, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_OPENGL_4_3, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_OPENGL_4_5, vulkan_spirv, true), - std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, false), std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false), std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, false), std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_0, vulkan_spirv, false), std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_1, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_2, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_3, vulkan_spirv, true), std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_OPENGL_4_0, vulkan_spirv, false), std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_OPENGL_4_3, vulkan_spirv, false), std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_OPENGL_4_5, vulkan_spirv, false), - std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true) + + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_VULKAN_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_VULKAN_1_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_VULKAN_1_2, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_VULKAN_1_3, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_OPENGL_4_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_OPENGL_4_3, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_OPENGL_4_5, vulkan_spirv, false), + + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_VULKAN_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_VULKAN_1_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_VULKAN_1_2, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_VULKAN_1_3, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_OPENGL_4_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_OPENGL_4_3, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_2, SPV_ENV_OPENGL_4_5, vulkan_spirv, false), + + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_UNIVERSAL_1_4, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_UNIVERSAL_1_5, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_UNIVERSAL_1_6, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_VULKAN_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_VULKAN_1_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_VULKAN_1_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_VULKAN_1_3, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_OPENGL_4_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_OPENGL_4_3, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_3, SPV_ENV_OPENGL_4_5, vulkan_spirv, false) ) ); @@ -187,6 +335,9 @@ INSTANTIATE_TEST_SUITE_P(OpenCL, ValidateVersion, std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_UNIVERSAL_1_1, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_UNIVERSAL_1_2, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_UNIVERSAL_1_3, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_UNIVERSAL_1_4, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_UNIVERSAL_1_5, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_UNIVERSAL_1_6, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_OPENCL_2_0, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_OPENCL_2_1, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_OPENCL_2_2, opencl_spirv, true), @@ -199,6 +350,9 @@ INSTANTIATE_TEST_SUITE_P(OpenCL, ValidateVersion, std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_UNIVERSAL_1_1, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_UNIVERSAL_1_2, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_UNIVERSAL_1_3, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_UNIVERSAL_1_4, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_UNIVERSAL_1_5, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_UNIVERSAL_1_6, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_OPENCL_2_0, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_OPENCL_2_1, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_OPENCL_2_2, opencl_spirv, true), @@ -211,6 +365,9 @@ INSTANTIATE_TEST_SUITE_P(OpenCL, ValidateVersion, std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_UNIVERSAL_1_1, opencl_spirv, false), std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_UNIVERSAL_1_2, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_UNIVERSAL_1_3, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_UNIVERSAL_1_4, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_UNIVERSAL_1_5, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_UNIVERSAL_1_6, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_OPENCL_2_0, opencl_spirv, false), std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_OPENCL_2_1, opencl_spirv, false), std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_OPENCL_2_2, opencl_spirv, true), @@ -223,6 +380,9 @@ INSTANTIATE_TEST_SUITE_P(OpenCL, ValidateVersion, std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_UNIVERSAL_1_1, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_UNIVERSAL_1_2, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_UNIVERSAL_1_3, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_UNIVERSAL_1_4, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_UNIVERSAL_1_5, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_UNIVERSAL_1_6, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_OPENCL_2_0, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_OPENCL_2_1, opencl_spirv, true), std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_OPENCL_2_2, opencl_spirv, true), diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 6039089a..86d0bc46 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -41,10 +41,11 @@ endfunction() if (NOT ${SPIRV_SKIP_EXECUTABLES}) add_spvtools_tool(TARGET spirv-as SRCS as/as.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) + add_spvtools_tool(TARGET spirv-diff SRCS diff/diff.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-diff SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY}) add_spvtools_tool(TARGET spirv-dis SRCS dis/dis.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) add_spvtools_tool(TARGET spirv-val SRCS val/val.cpp util/cli_consumer.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) add_spvtools_tool(TARGET spirv-opt SRCS opt/opt.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY}) - if (NOT DEFINED IOS_PLATFORM) # iOS does not allow std::system calls which spirv-reduce requires + if(NOT (${CMAKE_SYSTEM_NAME} STREQUAL "iOS")) # iOS does not allow std::system calls which spirv-reduce requires add_spvtools_tool(TARGET spirv-reduce SRCS reduce/reduce.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-reduce ${SPIRV_TOOLS_FULL_VISIBILITY}) endif() add_spvtools_tool(TARGET spirv-link SRCS link/linker.cpp LIBS SPIRV-Tools-link ${SPIRV_TOOLS_FULL_VISIBILITY}) @@ -58,7 +59,7 @@ if (NOT ${SPIRV_SKIP_EXECUTABLES}) ${SPIRV_HEADER_INCLUDE_DIR}) set(SPIRV_INSTALL_TARGETS spirv-as spirv-dis spirv-val spirv-opt spirv-cfg spirv-link spirv-lint) - if(NOT DEFINED IOS_PLATFORM) + if(NOT (${CMAKE_SYSTEM_NAME} STREQUAL "iOS")) set(SPIRV_INSTALL_TARGETS ${SPIRV_INSTALL_TARGETS} spirv-reduce) endif() @@ -68,9 +69,6 @@ if (NOT ${SPIRV_SKIP_EXECUTABLES}) endif(SPIRV_BUILD_FUZZER) if(ENABLE_SPIRV_TOOLS_INSTALL) - install(TARGETS ${SPIRV_INSTALL_TARGETS} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + install(TARGETS ${SPIRV_INSTALL_TARGETS} DESTINATION ${CMAKE_INSTALL_BINDIR}) endif(ENABLE_SPIRV_TOOLS_INSTALL) endif() diff --git a/tools/as/as.cpp b/tools/as/as.cpp index c8a44456..506b0585 100644 --- a/tools/as/as.cpp +++ b/tools/as/as.cpp @@ -48,7 +48,7 @@ Options: argv0, argv0, target_env_list.c_str()); } -static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5; +static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6; int main(int argc, char** argv) { const char* inFile = nullptr; diff --git a/tools/cfg/bin_to_dot.cpp b/tools/cfg/bin_to_dot.cpp index 2561eea4..72e7693a 100644 --- a/tools/cfg/bin_to_dot.cpp +++ b/tools/cfg/bin_to_dot.cpp @@ -57,13 +57,13 @@ class DotConverter { // Ends processing for the current block, emitting its dot code. void FlushBlock(const std::vector<uint32_t>& successors); - // The ID of the current functio, or 0 if outside of a function. + // The ID of the current function, or 0 if outside of a function. uint32_t current_function_id_ = 0; // The ID of the current basic block, or 0 if outside of a block. uint32_t current_block_id_ = 0; - // Have we completed processing for the entry block to this fuction? + // Have we completed processing for the entry block to this function? bool seen_function_entry_block_ = false; // The Id of the merge block for this block if it exists, or 0 otherwise. diff --git a/tools/cfg/bin_to_dot.h b/tools/cfg/bin_to_dot.h index 4de2e07f..a61c9759 100644 --- a/tools/cfg/bin_to_dot.h +++ b/tools/cfg/bin_to_dot.h @@ -20,7 +20,7 @@ #include "spirv-tools/libspirv.h" // Dumps the control flow graph for the given module to the output stream. -// Returns SPV_SUCCESS on succes. +// Returns SPV_SUCCESS on success. spv_result_t BinaryToDot(const spv_const_context context, const uint32_t* words, size_t num_words, std::iostream* out, spv_diagnostic* diagnostic); diff --git a/tools/cfg/cfg.cpp b/tools/cfg/cfg.cpp index 6a5faa18..5380c21e 100644 --- a/tools/cfg/cfg.cpp +++ b/tools/cfg/cfg.cpp @@ -44,7 +44,7 @@ Options: argv0, argv0); } -static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5; +static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6; int main(int argc, char** argv) { const char* inFile = nullptr; diff --git a/tools/diff/diff.cpp b/tools/diff/diff.cpp new file mode 100644 index 00000000..d3cad04b --- /dev/null +++ b/tools/diff/diff.cpp @@ -0,0 +1,201 @@ +// Copyright (c) 2022 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) +#include <unistd.h> +#endif + +#include "source/diff/diff.h" + +#include "source/opt/build_module.h" +#include "source/opt/ir_context.h" +#include "spirv-tools/libspirv.hpp" +#include "tools/io.h" +#include "tools/util/cli_consumer.h" + +static void print_usage(char* argv0) { + printf(R"(%s - Compare two SPIR-V files + +Usage: %s <src_filename> <dst_filename> + +The SPIR-V binary is read from <src_filename> and <dst_filename>. If either +file ends in .spvasm, the SPIR-V is read as text and disassembled. + +The contents of the SPIR-V modules are analyzed and a diff is produced showing a +logical transformation from src to dst, in src's id-space. + + -h, --help Print this help. + --version Display diff version information. + + --color Force color output. The default when printing to a terminal. + Overrides a previous --no-color option. + --no-color Don't print in color. Overrides a previous --color option. + The default when output goes to something other than a + terminal (e.g. a pipe, or a shell redirection). + + --no-indent Don't indent instructions. + + --no-header Don't output the header as leading comments. + + --with-id-map Also output the mapping between src and dst outputs. + + --ignore-set-binding + Don't use set/binding decorations for variable matching. + --ignore-location + Don't use location decorations for variable matching. +)", + argv0, argv0); +} + +static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6; + +static bool is_assembly(const char* path) { + const char* suffix = strrchr(path, '.'); + if (suffix == nullptr) { + return false; + } + + return strcmp(suffix, ".spvasm") == 0; +} + +static std::unique_ptr<spvtools::opt::IRContext> load_module(const char* path) { + if (is_assembly(path)) { + std::vector<char> contents; + if (!ReadTextFile<char>(path, &contents)) return {}; + + return spvtools::BuildModule( + kDefaultEnvironment, spvtools::utils::CLIMessageConsumer, + std::string(contents.begin(), contents.end()), + spvtools::SpirvTools::kDefaultAssembleOption | + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + } + + std::vector<uint32_t> contents; + if (!ReadBinaryFile<uint32_t>(path, &contents)) return {}; + + return spvtools::BuildModule(kDefaultEnvironment, + spvtools::utils::CLIMessageConsumer, + contents.data(), contents.size()); +} + +int main(int argc, char** argv) { + const char* src_file = nullptr; + const char* dst_file = nullptr; + bool color_is_possible = +#if SPIRV_COLOR_TERMINAL + true; +#else + false; +#endif + bool force_color = false; + bool force_no_color = false; + bool allow_indent = true; + bool no_header = false; + bool dump_id_map = false; + bool ignore_set_binding = false; + bool ignore_location = false; + + for (int argi = 1; argi < argc; ++argi) { + if ('-' == argv[argi][0]) { + switch (argv[argi][1]) { + case 'h': + print_usage(argv[0]); + return 0; + case '-': { + // Long options + if (strcmp(argv[argi], "--no-color") == 0) { + force_no_color = true; + force_color = false; + } else if (strcmp(argv[argi], "--color") == 0) { + force_no_color = false; + force_color = true; + } else if (strcmp(argv[argi], "--no-indent") == 0) { + allow_indent = false; + } else if (strcmp(argv[argi], "--no-header") == 0) { + no_header = true; + } else if (strcmp(argv[argi], "--with-id-map") == 0) { + dump_id_map = true; + } else if (strcmp(argv[argi], "--ignore-set-binding") == 0) { + ignore_set_binding = true; + } else if (strcmp(argv[argi], "--ignore-location") == 0) { + ignore_location = true; + } else if (strcmp(argv[argi], "--help") == 0) { + print_usage(argv[0]); + return 0; + } else if (strcmp(argv[argi], "--version") == 0) { + printf("%s\n", spvSoftwareVersionDetailsString()); + printf("Target: %s\n", + spvTargetEnvDescription(kDefaultEnvironment)); + return 0; + } else { + print_usage(argv[0]); + return 1; + } + } break; + default: + print_usage(argv[0]); + return 1; + } + } else { + if (src_file == nullptr) { + src_file = argv[argi]; + } else if (dst_file == nullptr) { + dst_file = argv[argi]; + } else { + fprintf(stderr, "error: More than two input files specified\n"); + return 1; + } + } + } + + if (src_file == nullptr || dst_file == nullptr) { + print_usage(argv[0]); + return 1; + } + + spvtools::diff::Options options; + + if (allow_indent) options.indent = true; + if (no_header) options.no_header = true; + if (dump_id_map) options.dump_id_map = true; + if (ignore_set_binding) options.ignore_set_binding = true; + if (ignore_location) options.ignore_location = true; + + if (color_is_possible && !force_no_color) { + bool output_is_tty = true; +#if defined(_POSIX_VERSION) + output_is_tty = isatty(fileno(stdout)); +#endif + if (output_is_tty || force_color) { + options.color_output = true; + } + } + + std::unique_ptr<spvtools::opt::IRContext> src = load_module(src_file); + std::unique_ptr<spvtools::opt::IRContext> dst = load_module(dst_file); + + if (!src) { + fprintf(stderr, "error: Loading src file\n"); + } + if (!dst) { + fprintf(stderr, "error: Loading dst file\n"); + } + if (!src || !dst) { + return 1; + } + + spvtools::diff::Diff(src.get(), dst.get(), std::cout, options); + + return 0; +} diff --git a/tools/fuzz/fuzz.cpp b/tools/fuzz/fuzz.cpp index 306f9255..ca6633a6 100644 --- a/tools/fuzz/fuzz.cpp +++ b/tools/fuzz/fuzz.cpp @@ -673,19 +673,6 @@ void DumpTransformationsBinary( transformations_file.close(); } -// The Chromium project applies the following patch to the protobuf library: -// -// source.chromium.org/chromium/chromium/src/+/main:third_party/protobuf/patches/0003-remove-static-initializers.patch -// -// This affects how Status objects must be constructed. This method provides a -// convenient way to get the OK status that works both with and without the -// patch. With the patch OK is a StatusPod, from which a Status can be -// constructed. Without the patch, OK is already a Status, and we harmlessly -// copy-construct the result from it. -google::protobuf::util::Status GetProtobufOkStatus() { - return google::protobuf::util::Status(google::protobuf::util::Status::OK); -} - // Dumps |transformations| to file |filename| in JSON format. Useful for // interactive debugging. void DumpTransformationsJson( @@ -696,7 +683,7 @@ void DumpTransformationsJson( json_options.add_whitespace = true; auto json_generation_status = google::protobuf::util::MessageToJsonString( transformations, &json_string, json_options); - if (json_generation_status == GetProtobufOkStatus()) { + if (json_generation_status.ok()) { std::ofstream transformations_json_file(filename); transformations_json_file << json_string; transformations_json_file.close(); @@ -747,8 +734,9 @@ int main(int argc, const char** argv) { std::string facts_json_string((std::istreambuf_iterator<char>(facts_input)), std::istreambuf_iterator<char>()); facts_input.close(); - if (GetProtobufOkStatus() != google::protobuf::util::JsonStringToMessage( - facts_json_string, &initial_facts)) { + if (!google::protobuf::util::JsonStringToMessage(facts_json_string, + &initial_facts) + .ok()) { spvtools::Error(FuzzDiagnostic, nullptr, {}, "Error reading facts data"); return 1; } @@ -828,7 +816,7 @@ int main(int argc, const char** argv) { json_options.add_whitespace = true; auto json_generation_status = google::protobuf::util::MessageToJsonString( transformations_applied, &json_string, json_options); - if (json_generation_status != GetProtobufOkStatus()) { + if (!json_generation_status.ok()) { spvtools::Error(FuzzDiagnostic, nullptr, {}, "Error writing out transformations in JSON format"); return 1; diff --git a/tools/link/linker.cpp b/tools/link/linker.cpp index 359e8030..bdddeb89 100644 --- a/tools/link/linker.cpp +++ b/tools/link/linker.cpp @@ -25,7 +25,7 @@ namespace { -const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5; +const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6; void print_usage(const char* program) { std::string target_env_list = spvTargetEnvList(16, 80); @@ -49,15 +49,18 @@ Options (in lexicographical order): --create-library Link the binaries into a library, keeping all exported symbols. -h, --help - Print this help. + Print this help. --target-env <env> - Set the target environment. Without this flag the target - environment defaults to spv1.5. <env> must be one of - {%s} + Set the environment used for interpreting the inputs. Without + this option the environment defaults to spv1.6. <env> must be + one of {%s}. + NOTE: The SPIR-V version used by the linked binary module + depends only on the version of the inputs, and is not affected + by this option. --verify-ids Verify that IDs in the resulting modules are truly unique. --version - Display linker version information + Display linker version information. )", program, program, target_env_list.c_str()); } @@ -160,10 +163,11 @@ int main(int argc, char** argv) { std::vector<uint32_t> linkingResult; spv_result_t status = Link(context, contents, &linkingResult, options); + if (status != SPV_SUCCESS && status != SPV_WARNING) return 1; if (!WriteFile<uint32_t>(outFile, "wb", linkingResult.data(), linkingResult.size())) return 1; - return status == SPV_SUCCESS ? 0 : 1; + return 0; } diff --git a/tools/lint/lint.cpp b/tools/lint/lint.cpp index 5c2a82ac..d37df830 100644 --- a/tools/lint/lint.cpp +++ b/tools/lint/lint.cpp @@ -19,7 +19,7 @@ #include "tools/io.h" #include "tools/util/cli_consumer.h" -const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5; +const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6; namespace { // Status and actions to perform after parsing command-line arguments. diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp index 04f81b8c..0129478b 100644 --- a/tools/opt/opt.cpp +++ b/tools/opt/opt.cpp @@ -44,7 +44,7 @@ struct OptStatus { // initialization and setup. Note that |source| and |position| are irrelevant // here because we are still not processing a SPIR-V input file. void opt_diagnostic(spv_message_level_t level, const char* /*source*/, - const spv_position_t& /*positon*/, const char* message) { + const spv_position_t& /*position*/, const char* message) { if (level == SPV_MSG_ERROR) { fprintf(stderr, "error: "); } @@ -59,7 +59,7 @@ std::string GetListOfPassesAsString(const spvtools::Optimizer& optimizer) { return ss.str(); } -const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5; +const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6; std::string GetLegalizationPasses() { spvtools::Optimizer optimizer(kDefaultEnvironment); @@ -168,6 +168,16 @@ Options (in lexicographical order):)", with a switch that has a case for every possible value of the index.)"); printf(R"( + --spread-volatile-semantics + Spread Volatile semantics to variables with SMIDNV, WarpIDNV, + SubgroupSize, SubgroupLocalInvocationId, SubgroupEqMask, + SubgroupGeMask, SubgroupGtMask, SubgroupLeMask, or SubgroupLtMask + BuiltIn decorations or OpLoad for them when the shader model is + ray generation, closest hit, miss, intersection, or callable. + For the SPIR-V version is 1.6 or above, it also spreads Volatile + semantics to a variable with HelperInvocation BuiltIn decoration + in the fragement shader.)"); + printf(R"( --descriptor-scalar-replacement Replaces every array variable |desc| that has a DescriptorSet and Binding decorations with a new variable for each element of @@ -198,6 +208,10 @@ Options (in lexicographical order):)", unused stores to vector components, that are not removed by aggressive dead code elimination.)"); printf(R"( + --eliminate-dead-input-components + Deletes unused components from input variables. Currently + deletes trailing unused elements from input arrays.)"); + printf(R"( --eliminate-dead-variables Deletes module scope variables that are not referenced.)"); printf(R"( @@ -479,10 +493,13 @@ Options (in lexicographical order):)", --strip-debug Remove all debug instructions.)"); printf(R"( + --strip-nonsemantic + Remove all reflection and nonsemantic information.)"); + printf(R"( --strip-reflect - Remove all reflection information. For now, this covers - reflection information defined by SPV_GOOGLE_hlsl_functionality1 - and SPV_KHR_non_semantic_info)"); + DEPRECATED. Remove all reflection information. For now, this + covers reflection information defined by + SPV_GOOGLE_hlsl_functionality1 and SPV_KHR_non_semantic_info)"); printf(R"( --target-env=<env> Set the target environment. Without this flag the target diff --git a/tools/reduce/reduce.cpp b/tools/reduce/reduce.cpp index 4447b356..37600543 100644 --- a/tools/reduce/reduce.cpp +++ b/tools/reduce/reduce.cpp @@ -262,7 +262,7 @@ void DumpShader(spvtools::opt::IRContext* context, const char* filename) { DumpShader(binary, filename); } -const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5; +const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_6; int main(int argc, const char** argv) { std::string in_binary_file; diff --git a/tools/sva/README.md b/tools/sva/README.md index d80b4d2c..cd3d13c9 100644 --- a/tools/sva/README.md +++ b/tools/sva/README.md @@ -1,6 +1,6 @@ # SVA -SPIR-V Assember for WebGPU. The SPIR-V Assembler is a JavaScript library to +SPIR-V Assembler for WebGPU. The SPIR-V Assembler is a JavaScript library to convert SPIR-V assembly (as produced by spirv-dis in SPIR-V Tools) into a SPIR-V binary. The assembler assumes it is generating WebGPU SPIR-V and thus has the following limitations. diff --git a/tools/sva/yarn.lock b/tools/sva/yarn.lock index afa11f6c..e7b735e0 100644 --- a/tools/sva/yarn.lock +++ b/tools/sva/yarn.lock @@ -1260,9 +1260,9 @@ path-to-regexp@2.2.1: integrity sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ== pathval@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.0.tgz#b942e6d4bde653005ef6b71361def8727d0645e0" - integrity sha1-uULm1L3mUwBe9rcTYd74cn0GReA= + version "1.1.1" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" + integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== prelude-ls@~1.1.2: version "1.1.2" diff --git a/tools/val/val.cpp b/tools/val/val.cpp index 55321dab..880ce46b 100644 --- a/tools/val/val.cpp +++ b/tools/val/val.cpp @@ -77,7 +77,7 @@ Options: int main(int argc, char** argv) { const char* inFile = nullptr; - spv_target_env target_env = SPV_ENV_UNIVERSAL_1_5; + spv_target_env target_env = SPV_ENV_UNIVERSAL_1_6; spvtools::ValidatorOptions options; bool continue_processing = true; int return_code = 0; @@ -111,17 +111,20 @@ int main(int argc, char** argv) { printf("%s\n", spvSoftwareVersionDetailsString()); printf( "Targets:\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n " - "%s\n", + "%s\n %s\n %s\n %s\n", spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_0), spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_1), spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_2), spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_3), spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_4), spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_5), + spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_6), spvTargetEnvDescription(SPV_ENV_OPENCL_2_2), spvTargetEnvDescription(SPV_ENV_VULKAN_1_0), spvTargetEnvDescription(SPV_ENV_VULKAN_1_1), - spvTargetEnvDescription(SPV_ENV_VULKAN_1_1_SPIRV_1_4)); + spvTargetEnvDescription(SPV_ENV_VULKAN_1_1_SPIRV_1_4), + spvTargetEnvDescription(SPV_ENV_VULKAN_1_2), + spvTargetEnvDescription(SPV_ENV_VULKAN_1_3)); continue_processing = false; return_code = 0; } else if (0 == strcmp(cur_arg, "--help") || 0 == strcmp(cur_arg, "-h")) { diff --git a/utils/check_copyright.py b/utils/check_copyright.py index 49892ee6..aa647af5 100755 --- a/utils/check_copyright.py +++ b/utils/check_copyright.py @@ -14,6 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. """Checks for copyright notices in all the files that need them under the + current directory. Optionally insert them. When inserting, replaces an MIT or Khronos free use license with Apache 2. """ @@ -41,11 +42,28 @@ AUTHORS = ['The Khronos Group Inc.', 'Mostafa Ashraf', 'Shiyu Liu', 'ZHOU He'] -CURRENT_YEAR='2021' - -YEARS = '(2014-2016|2015-2016|2015-2020|2016|2016-2017|2017|2017-2019|2018|2019|2020|2021)' -COPYRIGHT_RE = re.compile( - 'Copyright \(c\) {} ({})'.format(YEARS, '|'.join(AUTHORS))) +CURRENT_YEAR = 2022 + +FIRST_YEAR = 2014 +FINAL_YEAR = CURRENT_YEAR + 5 +# A regular expression to match the valid years in the copyright information. +YEAR_REGEX = '(' + '|'.join( + str(year) for year in range(FIRST_YEAR, FINAL_YEAR + 1)) + ')' + +# A regular expression to make a range of years in the form <year1>-<year2>. +YEAR_RANGE_REGEX = '(' +for year1 in range(FIRST_YEAR, FINAL_YEAR + 1): + for year2 in range(year1 + 1, FINAL_YEAR + 1): + YEAR_RANGE_REGEX += str(year1) + '-' + str(year2) + '|' +YEAR_RANGE_REGEX = YEAR_RANGE_REGEX[:-1] + ')' + +# In the copyright info, the year can be a single year or a range. This is a +# regex to make sure it matches one of them. +YEAR_OR_RANGE_REGEX = '(' + YEAR_REGEX + '|' + YEAR_RANGE_REGEX + ')' + +# The final regular expression to match a valid copyright line. +COPYRIGHT_RE = re.compile('Copyright \(c\) {} ({})'.format( + YEAR_OR_RANGE_REGEX, '|'.join(AUTHORS))) MIT_BEGIN_RE = re.compile('Permission is hereby granted, ' 'free of charge, to any person obtaining a') diff --git a/utils/generate_grammar_tables.py b/utils/generate_grammar_tables.py index 9ccf410b..74aa2829 100755 --- a/utils/generate_grammar_tables.py +++ b/utils/generate_grammar_tables.py @@ -23,7 +23,7 @@ import re PYGEN_VARIABLE_PREFIX = 'pygen_variable' # Extensions to recognize, but which don't necessarily come from the SPIR-V -# core or KHR grammar files. Get this list from the SPIR-V registery web page. +# core or KHR grammar files. Get this list from the SPIR-V registry web page. # NOTE: Only put things on this list if it is not in those grammar files. EXTENSIONS_FROM_SPIRV_REGISTRY_AND_NOT_FROM_GRAMMARS = """ SPV_AMD_gcn_shader diff --git a/utils/git-sync-deps b/utils/git-sync-deps index eecfbe93..7a7e606f 100755 --- a/utils/git-sync-deps +++ b/utils/git-sync-deps @@ -168,7 +168,7 @@ def git_checkout_to_directory(git, repo, checkoutable, directory, verbose): with open(os.devnull, 'w') as devnull: # If this fails, we will fetch before trying again. Don't spam user - # with error infomation. + # with error information. if 0 == subprocess.call([git, 'checkout', '--quiet', checkoutable], cwd=directory, stderr=devnull): # if this succeeds, skip slow `git fetch`. diff --git a/utils/roll_deps.sh b/utils/roll_deps.sh index f61f2a31..cef8b526 100755 --- a/utils/roll_deps.sh +++ b/utils/roll_deps.sh @@ -23,9 +23,9 @@ set -eo pipefail effcee_dir="external/effcee/" effcee_trunk="origin/main" googletest_dir="external/googletest/" -googletest_trunk="origin/master" +googletest_trunk="origin/main" re2_dir="external/re2/" -re2_trunk="origin/master" +re2_trunk="origin/main" spirv_headers_dir="external/spirv-headers/" spirv_headers_trunk="origin/master" @@ -41,6 +41,7 @@ fi old_head=$(git rev-parse HEAD) +set +e roll-dep --ignore-dirty-tree --roll-to="${effcee_trunk}" "${effcee_dir}" roll-dep --ignore-dirty-tree --roll-to="${googletest_trunk}" "${googletest_dir}" roll-dep --ignore-dirty-tree --roll-to="${re2_trunk}" "${re2_dir}" diff --git a/utils/vscode/src/lsp/jsonrpc2/jsonrpc2.go b/utils/vscode/src/lsp/jsonrpc2/jsonrpc2.go index b8436d27..44dd2205 100644 --- a/utils/vscode/src/lsp/jsonrpc2/jsonrpc2.go +++ b/utils/vscode/src/lsp/jsonrpc2/jsonrpc2.go @@ -48,7 +48,7 @@ const ( requestDone ) -// Request is sent to a server to represent a Call or Notify operaton. +// Request is sent to a server to represent a Call or Notify operation. type Request struct { conn *Conn cancel context.CancelFunc diff --git a/utils/vscode/src/lsp/jsonrpc2/wire.go b/utils/vscode/src/lsp/jsonrpc2/wire.go index 3e31c340..fed9a25b 100644 --- a/utils/vscode/src/lsp/jsonrpc2/wire.go +++ b/utils/vscode/src/lsp/jsonrpc2/wire.go @@ -44,7 +44,7 @@ const ( CodeServerOverloaded = -32000 ) -// WireRequest is sent to a server to represent a Call or Notify operaton. +// WireRequest is sent to a server to represent a Call or Notify operation. type WireRequest struct { // VersionTag is always encoded as the string "2.0" VersionTag VersionTag `json:"jsonrpc"` diff --git a/utils/vscode/src/lsp/protocol/tsprotocol.go b/utils/vscode/src/lsp/protocol/tsprotocol.go index 50543fc8..e0a35946 100644 --- a/utils/vscode/src/lsp/protocol/tsprotocol.go +++ b/utils/vscode/src/lsp/protocol/tsprotocol.go @@ -143,7 +143,7 @@ type WorkspaceFoldersServerCapabilities struct { * change notifications. * * If a strings is provided the string is treated as a ID - * under which the notification is registed on the client + * under which the notification is registered on the client * side. The ID can be used to unregister for these events * using the `client/unregisterCapability` request. */ @@ -162,7 +162,7 @@ type WorkspaceFolder struct { /*Name defined: * The name of the workspace folder. Used to refer to this - * workspace folder in thge user interface. + * workspace folder in the user interface. */ Name string `json:"name"` } @@ -1129,7 +1129,7 @@ type ServerCapabilities struct { * change notifications. * * If a strings is provided the string is treated as a ID - * under which the notification is registed on the client + * under which the notification is registered on the client * side. The ID can be used to unregister for these events * using the `client/unregisterCapability` request. */ @@ -1803,7 +1803,7 @@ type CompletionOptions struct { /*AllCommitCharacters defined: * The list of all possible characters that commit a completion. This field can be used - * if clients don't support individual commmit characters per completion item. See + * if clients don't support individual commit characters per completion item. See * `ClientCapabilities.textDocument.completion.completionItem.commitCharactersSupport` * * @since 3.2.0 @@ -2844,7 +2844,7 @@ type LocationLink struct { /*TargetSelectionRange defined: * The range that should be selected and revealed when this link is being followed, e.g the name of a function. - * Must be contained by the the `targetRange`. See also `DocumentSymbol#range` + * Must be contained by the `targetRange`. See also `DocumentSymbol#range` */ TargetSelectionRange Range `json:"targetSelectionRange"` } @@ -2881,7 +2881,7 @@ type Color struct { type ColorInformation struct { /*Range defined: - * The range in the document where this color appers. + * The range in the document where this color appears. */ Range Range `json:"range"` @@ -3627,14 +3627,14 @@ type DocumentSymbol struct { /*Range defined: * The range enclosing this symbol not including leading/trailing whitespace but everything else - * like comments. This information is typically used to determine if the the clients cursor is + * like comments. This information is typically used to determine if the clients cursor is * inside the symbol to reveal in the symbol in the UI. */ Range Range `json:"range"` /*SelectionRange defined: * The range that should be selected and revealed when this symbol is being picked, e.g the name of a function. - * Must be contained by the the `range`. + * Must be contained by the `range`. */ SelectionRange Range `json:"selectionRange"` @@ -3652,7 +3652,7 @@ type CodeActionContext struct { /*Diagnostics defined: * An array of diagnostics known on the client side overlapping the range provided to the - * `textDocument/codeAction` request. They are provied so that the server knows which + * `textDocument/codeAction` request. They are provided so that the server knows which * errors are currently presented to the user for the given range. There is no guarantee * that these accurately reflect the error state of the resource. The primary parameter * to compute code actions is the provided range. @@ -4081,13 +4081,13 @@ const ( /*TextOnlyTransactional defined: * If the workspace edit contains only textual file changes they are executed transactional. * If resource changes (create, rename or delete file) are part of the change the failure - * handling startegy is abort. + * handling strategy is abort. */ TextOnlyTransactional FailureHandlingKind = "textOnlyTransactional" /*Undo defined: * The client tries to undo the operations already executed. But there is no - * guaruntee that this is succeeding. + * guarantee that this is succeeding. */ Undo FailureHandlingKind = "undo" diff --git a/utils/vscode/src/parser/parser.go b/utils/vscode/src/parser/parser.go index cc6f3332..4c0fa8f7 100644 --- a/utils/vscode/src/parser/parser.go +++ b/utils/vscode/src/parser/parser.go @@ -798,7 +798,7 @@ type Identifier struct { References []*Token // all the places the identifier was referenced } -// Severity is an enumerator of diagnositc seeverities +// Severity is an enumerator of diagnostic severities type Severity int // Severity levels |