diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2021-10-06 12:32:26 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2021-10-06 12:32:26 +0000 |
commit | 0ffea25b869dd017a5601fe648b4ac490111e135 (patch) | |
tree | 5bfa88b833e4004e8e1efe297744560296aa1d7a | |
parent | 1a5ab3a47b99064714cc6b999c71ff9825bc3e45 (diff) | |
parent | 5c9f70f14d6560a031241be903772bb9b1b8199f (diff) | |
download | SPIRV-Tools-android12-mainline-resolv-release.tar.gz |
Snap for 7798373 from 5c9f70f14d6560a031241be903772bb9b1b8199f to mainline-resolv-releaseandroid-mainline-12.0.0_r94android-mainline-12.0.0_r80android-mainline-12.0.0_r65android-mainline-12.0.0_r52android-mainline-12.0.0_r35android-mainline-12.0.0_r124android-mainline-12.0.0_r108android12-mainline-resolv-release
Change-Id: Ibb3489c85a1de54f758649dadd836bec2a3fb960
518 files changed, 12524 insertions, 3365 deletions
@@ -139,5 +139,6 @@ cc_library { cppflags: [ "-Wno-implicit-fallthrough", "-Wno-sign-conversion", + "-Wno-switch", ], } @@ -19,6 +19,7 @@ if (build_with_chromium) { } spirv_headers = spirv_tools_spirv_headers_dir +spirv_is_winuwp = is_win && target_os == "winuwp" template("spvtools_core_tables") { assert(defined(invoker.version), "Need version in $target_name generation.") @@ -32,13 +33,14 @@ template("spvtools_core_tables") { "${spirv_headers}/include/spirv/$version/spirv.core.grammar.json" core_insts_file = "${target_gen_dir}/core.insts-$version.inc" operand_kinds_file = "${target_gen_dir}/operand.kinds-$version.inc" - debuginfo_insts_file = "${spirv_headers}/include/spirv/unified1/extinst.debuginfo.grammar.json" + debuginfo_insts_file = + "${spirv_headers}/include/spirv/unified1/extinst.debuginfo.grammar.json" cldebuginfo100_insts_file = "${spirv_headers}/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json" sources = [ + cldebuginfo100_insts_file, core_json_file, debuginfo_insts_file, - cldebuginfo100_insts_file, ] outputs = [ core_insts_file, @@ -69,7 +71,8 @@ template("spvtools_core_enums") { core_json_file = "${spirv_headers}/include/spirv/$version/spirv.core.grammar.json" - debuginfo_insts_file = "${spirv_headers}/include/spirv/unified1/extinst.debuginfo.grammar.json" + debuginfo_insts_file = + "${spirv_headers}/include/spirv/unified1/extinst.debuginfo.grammar.json" cldebuginfo100_insts_file = "${spirv_headers}/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json" extension_enum_file = "${target_gen_dir}/extension_enum.inc" @@ -110,7 +113,8 @@ template("spvtools_glsl_tables") { core_json_file = "${spirv_headers}/include/spirv/$version/spirv.core.grammar.json" glsl_json_file = "${spirv_headers}/include/spirv/${version}/extinst.glsl.std.450.grammar.json" - debuginfo_insts_file = "${spirv_headers}/include/spirv/unified1/extinst.debuginfo.grammar.json" + debuginfo_insts_file = + "${spirv_headers}/include/spirv/unified1/extinst.debuginfo.grammar.json" cldebuginfo100_insts_file = "${spirv_headers}/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json" glsl_insts_file = "${target_gen_dir}/glsl.std.450.insts.inc" @@ -133,9 +137,7 @@ template("spvtools_glsl_tables") { debuginfo_insts_file, cldebuginfo100_insts_file, ] - outputs = [ - glsl_insts_file, - ] + outputs = [ glsl_insts_file ] } } @@ -150,7 +152,8 @@ template("spvtools_opencl_tables") { core_json_file = "${spirv_headers}/include/spirv/$version/spirv.core.grammar.json" opencl_json_file = "${spirv_headers}/include/spirv/${version}/extinst.opencl.std.100.grammar.json" - debuginfo_insts_file = "${spirv_headers}/include/spirv/unified1/extinst.debuginfo.grammar.json" + debuginfo_insts_file = + "${spirv_headers}/include/spirv/unified1/extinst.debuginfo.grammar.json" cldebuginfo100_insts_file = "${spirv_headers}/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json" opencl_insts_file = "${target_gen_dir}/opencl.std.insts.inc" @@ -173,9 +176,7 @@ template("spvtools_opencl_tables") { debuginfo_insts_file, cldebuginfo100_insts_file, ] - outputs = [ - opencl_insts_file, - ] + outputs = [ opencl_insts_file ] } } @@ -194,12 +195,8 @@ template("spvtools_language_header") { "--extinst-output-path", rebase_path(extinst_output_path, root_build_dir), ] - inputs = [ - invoker.grammar_file, - ] - outputs = [ - "${extinst_output_path}", - ] + inputs = [ invoker.grammar_file ] + outputs = [ "${extinst_output_path}" ] } } @@ -210,7 +207,8 @@ template("spvtools_vendor_table") { script = "utils/generate_grammar_tables.py" name = invoker.name - extinst_vendor_grammar = "${spirv_headers}/include/spirv/unified1/extinst.${name}.grammar.json" + extinst_vendor_grammar = + "${spirv_headers}/include/spirv/unified1/extinst.${name}.grammar.json" extinst_file = "${target_gen_dir}/${name}.insts.inc" args = [ @@ -219,14 +217,10 @@ template("spvtools_vendor_table") { "--vendor-insts-output", rebase_path(extinst_file, root_build_dir), "--vendor-operand-kind-prefix", - invoker.operand_kind_prefix - ] - inputs = [ - extinst_vendor_grammar, - ] - outputs = [ - extinst_file, + invoker.operand_kind_prefix, ] + inputs = [ extinst_vendor_grammar ] + outputs = [ extinst_file ] } } @@ -237,12 +231,8 @@ action("spvtools_generators_inc") { xml_file = "${spirv_headers}/include/spirv/spir-v.xml" inc_file = "${target_gen_dir}/generators.inc" - sources = [ - xml_file, - ] - outputs = [ - inc_file, - ] + sources = [ xml_file ] + outputs = [ inc_file ] args = [ "--xml", rebase_path(xml_file, root_build_dir), @@ -257,9 +247,7 @@ action("spvtools_build_version") { src_dir = "." inc_file = "${target_gen_dir}/build-version.inc" - outputs = [ - inc_file, - ] + outputs = [ inc_file ] args = [ rebase_path(src_dir, root_build_dir), rebase_path(inc_file, root_build_dir), @@ -280,7 +268,8 @@ spvtools_opencl_tables("opencl1-0") { } spvtools_language_header("debuginfo") { name = "DebugInfo" - grammar_file = "${spirv_headers}/include/spirv/unified1/extinst.debuginfo.grammar.json" + grammar_file = + "${spirv_headers}/include/spirv/unified1/extinst.debuginfo.grammar.json" } spvtools_language_header("cldebuginfo100") { name = "OpenCLDebugInfo100" @@ -288,13 +277,34 @@ spvtools_language_header("cldebuginfo100") { } spvtools_vendor_tables = [ - ["spv-amd-shader-explicit-vertex-parameter", "...nil..."], - ["spv-amd-shader-trinary-minmax", "...nil..."], - ["spv-amd-gcn-shader", "...nil..."], - ["spv-amd-shader-ballot", "...nil..."], - ["debuginfo", "...nil..."], - ["opencl.debuginfo.100", "CLDEBUG100_"], - ["nonsemantic.clspvreflection", "...nil..."], + [ + "spv-amd-shader-explicit-vertex-parameter", + "...nil...", + ], + [ + "spv-amd-shader-trinary-minmax", + "...nil...", + ], + [ + "spv-amd-gcn-shader", + "...nil...", + ], + [ + "spv-amd-shader-ballot", + "...nil...", + ], + [ + "debuginfo", + "...nil...", + ], + [ + "opencl.debuginfo.100", + "CLDEBUG100_", + ], + [ + "nonsemantic.clspvreflection", + "...nil...", + ], ] foreach(table_def, spvtools_vendor_tables) { @@ -317,11 +327,15 @@ config("spvtools_internal_config") { configs = [ ":spvtools_public_config" ] + cflags = [] if (is_clang) { - cflags = [ + cflags += [ "-Wno-implicit-fallthrough", "-Wno-newline-eof", ] + } else if (!is_win) { + # Work around a false-positive on a Skia GCC 10 builder. + cflags += [ "-Wno-format-truncation" ] } } @@ -342,8 +356,8 @@ static_library("spvtools") { ":spvtools_core_tables_unified1", ":spvtools_generators_inc", ":spvtools_glsl_tables_glsl1-0", - ":spvtools_language_header_debuginfo", ":spvtools_language_header_cldebuginfo100", + ":spvtools_language_header_debuginfo", ":spvtools_opencl_tables_opencl1-0", ] foreach(table_def, spvtools_vendor_tables) { @@ -486,9 +500,7 @@ static_library("spvtools_val") { ":spvtools_language_header_cldebuginfo100", ":spvtools_language_header_debuginfo", ] - public_deps = [ - ":spvtools_headers", - ] + public_deps = [ ":spvtools_headers" ] if (build_with_chromium) { configs -= [ "//build/config/compiler:chromium_code" ] @@ -539,10 +551,10 @@ static_library("spvtools_opt") { "source/opt/dead_insert_elim_pass.h", "source/opt/dead_variable_elimination.cpp", "source/opt/dead_variable_elimination.h", - "source/opt/decoration_manager.cpp", - "source/opt/decoration_manager.h", "source/opt/debug_info_manager.cpp", "source/opt/debug_info_manager.h", + "source/opt/decoration_manager.cpp", + "source/opt/decoration_manager.h", "source/opt/def_use_manager.cpp", "source/opt/def_use_manager.h", "source/opt/desc_sroa.cpp", @@ -598,6 +610,8 @@ static_library("spvtools_opt") { "source/opt/instruction_list.h", "source/opt/instrument_pass.cpp", "source/opt/instrument_pass.h", + "source/opt/interp_fixup_pass.cpp", + "source/opt/interp_fixup_pass.h", "source/opt/ir_builder.h", "source/opt/ir_context.cpp", "source/opt/ir_context.h", @@ -709,9 +723,7 @@ static_library("spvtools_opt") { ":spvtools_language_header_debuginfo", ":spvtools_vendor_tables_spv-amd-shader-ballot", ] - public_deps = [ - ":spvtools_headers", - ] + public_deps = [ ":spvtools_headers" ] if (build_with_chromium) { configs -= [ "//build/config/compiler:chromium_code" ] @@ -721,17 +733,13 @@ static_library("spvtools_opt") { } static_library("spvtools_link") { - sources = [ - "source/link/linker.cpp", - ] + sources = [ "source/link/linker.cpp" ] deps = [ ":spvtools", ":spvtools_opt", ":spvtools_val", ] - public_deps = [ - ":spvtools_headers", - ] + public_deps = [ ":spvtools_headers" ] if (build_with_chromium) { configs -= [ "//build/config/compiler:chromium_code" ] configs += [ "//build/config/compiler:no_chromium_code" ] @@ -804,9 +812,7 @@ static_library("spvtools_reduce") { ":spvtools", ":spvtools_opt", ] - public_deps = [ - ":spvtools_headers", - ] + public_deps = [ ":spvtools_headers" ] if (build_with_chromium) { configs -= [ "//build/config/compiler:chromium_code" ] configs += [ "//build/config/compiler:no_chromium_code" ] @@ -841,10 +847,10 @@ if (build_with_chromium) { "test/comment_test.cpp", "test/enum_set_test.cpp", "test/enum_string_mapping_test.cpp", + "test/ext_inst.cldebug100_test.cpp", "test/ext_inst.debuginfo_test.cpp", "test/ext_inst.glsl_test.cpp", "test/ext_inst.opencl_test.cpp", - "test/ext_inst.cldebug100_test.cpp", "test/fix_word_test.cpp", "test/generator_magic_number_test.cpp", "test/hex_float_test.cpp", @@ -891,8 +897,8 @@ if (build_with_chromium) { deps = [ ":spvtools", - ":spvtools_language_header_debuginfo", ":spvtools_language_header_cldebuginfo100", + ":spvtools_language_header_debuginfo", ":spvtools_val", "//testing/gmock", "//testing/gtest", @@ -912,9 +918,7 @@ if (build_with_chromium) { if (spirv_tools_standalone) { group("fuzzers") { testonly = true - deps = [ - "test/fuzzers", - ] + deps = [ "test/fuzzers" ] } } @@ -923,16 +927,12 @@ source_set("spvtools_util_cli_consumer") { "tools/util/cli_consumer.cpp", "tools/util/cli_consumer.h", ] - deps = [ - ":spvtools_headers", - ] + deps = [ ":spvtools_headers" ] configs += [ ":spvtools_internal_config" ] } source_set("spvtools_software_version") { - sources = [ - "source/software_version.cpp", - ] + sources = [ "source/software_version.cpp" ] deps = [ ":spvtools_build_version", ":spvtools_headers", @@ -941,9 +941,7 @@ source_set("spvtools_software_version") { } executable("spirv-as") { - sources = [ - "tools/as/as.cpp", - ] + sources = [ "tools/as/as.cpp" ] deps = [ ":spvtools", ":spvtools_software_version", @@ -952,9 +950,7 @@ executable("spirv-as") { } executable("spirv-dis") { - sources = [ - "tools/dis/dis.cpp", - ] + sources = [ "tools/dis/dis.cpp" ] deps = [ ":spvtools", ":spvtools_software_version", @@ -963,9 +959,7 @@ executable("spirv-dis") { } executable("spirv-val") { - sources = [ - "tools/val/val.cpp", - ] + sources = [ "tools/val/val.cpp" ] deps = [ ":spvtools", ":spvtools_software_version", @@ -989,9 +983,7 @@ executable("spirv-cfg") { } executable("spirv-opt") { - sources = [ - "tools/opt/opt.cpp", - ] + sources = [ "tools/opt/opt.cpp" ] deps = [ ":spvtools", ":spvtools_opt", @@ -1003,9 +995,7 @@ executable("spirv-opt") { } executable("spirv-link") { - sources = [ - "tools/link/linker.cpp", - ] + sources = [ "tools/link/linker.cpp" ] deps = [ ":spvtools", ":spvtools_link", @@ -1016,12 +1006,10 @@ executable("spirv-link") { configs += [ ":spvtools_internal_config" ] } -if (!is_ios) { - # iOS does not allow std::system calls which spirv-reduce requires +if (!is_ios && !spirv_is_winuwp) { + # iOS and UWP do not allow std::system calls which spirv-reduce requires executable("spirv-reduce") { - sources = [ - "tools/reduce/reduce.cpp", - ] + sources = [ "tools/reduce/reduce.cpp" ] deps = [ ":spvtools", ":spvtools_opt", @@ -1043,7 +1031,7 @@ group("all_spirv_tools") { ":spirv-opt", ":spirv-val", ] - if (!is_ios) { + if (!is_ios && !spirv_is_winuwp) { deps += [ ":spirv-reduce" ] } } @@ -1,7 +1,92 @@ Revision history for SPIRV-Tools -v2020.7-dev 2020-12-07 - - Start v2020.7-dev +v2021.3-dev 2021-06-22 + - Start v2021.3-dev + +v2021.2 2021-06-18 + - General + - Support SPV_KHR_subgroup_uniform_control_flow (#4318) + - Support Intel extensions for fixed point and hls-float (#4321) + - Fix crash when optimizing shaders with DebugPrintf (#4280) + + - Validator + - Support Vulkan Storage Class for Execution Model (#4212) + + - Optimizer + - Handle SPV_KHR_vulkan_memory_model in dead-code elimination (#4320) + - Support folding OpBitcast with numeric constants (#4247) + + - Fuzz + - Add tests for MaybeGet* functions in fuzzerutil (#4284) + - Fix OutlineFunction in presence of unreachable blocks (#4308) + - Fix def-use update in PermutePhiOperands (#4309) + - Swap positions of two functions in a module (#4236) + +v2021.1 2021-04-19 + - General + - Support SPV_KHR_linkonce_odr, SPV_KHR_expect_assume (#4161) + - Fixes for the vscode language server extension (#4150) + - Validator + - Add validation for SPV_EXT_shader_atomic_float_min_max (#4105) + - Add Vulkan Execution Scope checks (#4183) + - Vulkan 64-bit OpAtomicStore check (#4163) + - Optimizer + - Add interpolate legalization pass (#4220) + - Fuzz + - Various performance optimizations + - Do not add too many dead blocks (#4217) + - Add WGSL compatibility flag to context (#4193) + - Add persistent state to the fuzzer (#4137) + +v2020.7 2021-02-16 + - General + - Support pending Intel extensions (#4116) + - Remove WebGPU support (#4108) + - Validator + - Vulkan image gather constant component (#4133) + - Add Vulkan PSB64 convert VUID (#4122) + - Validate SPV_KHR_workgroup_memory_explicit_layout (#4128) + - Validate VK_KHR_zero_initialize_workgroup_memory (#4124) + - Add Vulkan image gather offset VUID (#4118) + - Label Vulkan atomic semantics VUIDs (#4120) + - Label VUID 04662 (#4123) + - Label VUID 04683 (#4121) + - Add Vulkan EXT builtins (#4115) + - Validate Sampled=1 for Vulkan ImageQuerySizeLod, ImageQueryLevels, ImageQueryLod (#4103) + - Add Vulkan Memory Scope VUs (#4106) + - Add Vulkan Addressing Model check (#4107) + - Vulkan atomic storage class (#4079) + - Label standalone Vulkan VUID (#4091) + - Add Vulkan decroation VUID (#4090) + - Add Vulkan FP Mode VUID (#4088) + - Fix Vulkan image sampled check (#4085) + - Add Vulkan ForwardPointer VUID (#4089) + - Add Vulkan ImageTexelPointer format check (#4087) + - Add Vulkan Group Operation VUID (#4086) + - Add first StandAlone VUID 04633 (#4077) + - Add Subgroup VUIDs (#4074) + - validate return type of OpImageRead (#4072) + - tighter validation of multisampled images (#4059) + - validate OpTypeImage Sampled values for environemnts (#4064) + - validate StorageImageMultisampled capability (#4062) + - Add last TessLevelOuter and TessLevelInner VUID (#4055) + - Add last ClipDistance and CullDistance VUID (#4054) + - Add last ViewportIndex and Layer VUID (#4053) + - Add last Position VUID (#4052) + - Allow forward pointer to be used in types generally (#4044) + - Optimizer + - Mark module as modified if convert-to-half removes decorations (#4127) + - Fix binding number calculation in desc sroa (#4095) + - Run DCE when SPV_KHR_shader_clock is used (#4049) + - Debug Info + - Set correct scope and line info for DebugValue (#4125) + - Avoid integrity check failures caused by propagating line instructions (#4096) + - Linker + - Linker usability improvements (#4084) + - Instrumentation + - Generate differentiated error codes for buffer oob checking (#4097) + - Fuzz + - Fix OpPhi handling in DuplicateRegionWithSelection (#4065) v2020.6 2020-12-07 - General @@ -4,9 +4,9 @@ vars = { 'github': 'https://github.com', 'effcee_revision': '2ec8f8738118cc483b67c04a759fee53496c5659', - 'googletest_revision': '3af06fe1664d30f98de1e78c53a7087e842a2547', - 're2_revision': 'ca11026a032ce2a3de4b3c389ee53d2bdc8794d6', - 'spirv_headers_revision': 'faa570afbc91ac73d594d787486bcf8f2df1ace0', + 'googletest_revision': 'b7d472f1225c5a64943821d8483fecb469d3f382', + 're2_revision': 'f8e389f3acdc2517562924239e2a188037393683', + 'spirv_headers_revision': 'f95c3b3761ee1b1903f54ae69b526ed6f0edc3b9', } deps = { diff --git a/docs/spirv-fuzz.md b/docs/spirv-fuzz.md index e5439e3c..5716b35e 100644 --- a/docs/spirv-fuzz.md +++ b/docs/spirv-fuzz.md @@ -53,7 +53,7 @@ See `transformation_set_selection_control.h` for an example. The `IsApplicable` method should have a comment in the header file describing the conditions for applicability in simple terms. These conditions should be implemented in the body of this method in the `.cpp` file. -In the case of `TransformationSetSelectionControl`, `IsApplicable` involves checking that `block_id` is indeed the id of a block that has an `OpSelectoinMerge` instruction, and that `selection_control` is a valid selection mask. +In the case of `TransformationSetSelectionControl`, `IsApplicable` involves checking that `block_id` is indeed the id of a block that has an `OpSelectionMerge` instruction, and that `selection_control` is a valid selection mask. The `Apply` method should have a comment in the header file summarising the result of applying the transformation. It should be implemented in the `.cpp` file, and you should assume that `IsApplicable` holds whenever `Apply` is invoked. diff --git a/include/spirv-tools/instrument.hpp b/include/spirv-tools/instrument.hpp index 2b47a564..a19491fd 100644 --- a/include/spirv-tools/instrument.hpp +++ b/include/spirv-tools/instrument.hpp @@ -170,11 +170,15 @@ static const int kInstMaxOutCnt = kInstStageOutCnt + 4; static const int kInstErrorBindlessBounds = 0; static const int kInstErrorBindlessUninit = 1; static const int kInstErrorBuffAddrUnallocRef = 2; -static const int kInstErrorBindlessBuffOOB = 3; +// Deleted: static const int kInstErrorBindlessBuffOOB = 3; +// This comment will will remain for 2 releases to allow +// for the transition of all builds. Buffer OOB is +// generating the following four differentiated codes instead: static const int kInstErrorBuffOOBUniform = 4; static const int kInstErrorBuffOOBStorage = 5; static const int kInstErrorBuffOOBUniformTexel = 6; static const int kInstErrorBuffOOBStorageTexel = 7; +static const int kInstErrorMax = kInstErrorBuffOOBStorageTexel; // Direct Input Buffer Offsets // diff --git a/include/spirv-tools/libspirv.h b/include/spirv-tools/libspirv.h index 658c4dd5..039dab18 100644 --- a/include/spirv-tools/libspirv.h +++ b/include/spirv-tools/libspirv.h @@ -113,6 +113,9 @@ typedef enum spv_endianness_t { // Sometimes we also need to be able to express the fact that an operand // is a member of an optional tuple of values. In that case the first member // would be optional, and the subsequent members would be required. +// +// NOTE: Although we don't promise binary compatibility, as a courtesy, please +// add new enum values at the end. typedef enum spv_operand_type_t { // A sentinel value. SPV_OPERAND_TYPE_NONE = 0, @@ -167,12 +170,8 @@ typedef enum spv_operand_type_t { SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS, // SPIR-V Sec 3.29 SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO, // SPIR-V Sec 3.30 SPV_OPERAND_TYPE_CAPABILITY, // SPIR-V Sec 3.31 - SPV_OPERAND_TYPE_RAY_FLAGS, // SPIR-V Sec 3.RF - SPV_OPERAND_TYPE_RAY_QUERY_INTERSECTION, // SPIR-V Sec 3.RQIntersection - SPV_OPERAND_TYPE_RAY_QUERY_COMMITTED_INTERSECTION_TYPE, // SPIR-V Sec - // 3.RQCommitted - SPV_OPERAND_TYPE_RAY_QUERY_CANDIDATE_INTERSECTION_TYPE, // SPIR-V Sec - // 3.RQCandidate + + // NOTE: New concrete enum values should be added at the end. // Set 5: Operands that are a single word bitmask. // Sometimes a set bit indicates the instruction requires still more operands. @@ -184,7 +183,10 @@ typedef enum spv_operand_type_t { SPV_OPERAND_TYPE_MEMORY_ACCESS, // SPIR-V Sec 3.26 SPV_OPERAND_TYPE_FRAGMENT_SHADING_RATE, // SPIR-V Sec 3.FSR -// The remaining operand types are only used internally by the assembler. +// NOTE: New concrete enum values should be added at the end. + +// The "optional" and "variable" operand types are only used internally by +// the assembler and the binary parser. // There are two categories: // Optional : expands to 0 or 1 operand, like ? in regular expressions. // Variable : expands to 0, 1 or many operands or pairs of operands. @@ -264,6 +266,24 @@ typedef enum spv_operand_type_t { // https://github.com/intel/llvm/blob/39fa9b0cbfbae88327118990a05c5b387b56d2ef/sycl/doc/extensions/SPIRV/SPV_INTEL_float_controls2.asciidoc SPV_OPERAND_TYPE_FPDENORM_MODE, // Sec 3.17 FP Denorm Mode SPV_OPERAND_TYPE_FPOPERATION_MODE, // Sec 3.18 FP Operation Mode + // A value enum from https://github.com/KhronosGroup/SPIRV-Headers/pull/177 + SPV_OPERAND_TYPE_QUANTIZATION_MODES, + // A value enum from https://github.com/KhronosGroup/SPIRV-Headers/pull/177 + SPV_OPERAND_TYPE_OVERFLOW_MODES, + + // Concrete operand types for the provisional Vulkan ray tracing feature. + SPV_OPERAND_TYPE_RAY_FLAGS, // SPIR-V Sec 3.RF + SPV_OPERAND_TYPE_RAY_QUERY_INTERSECTION, // SPIR-V Sec 3.RQIntersection + SPV_OPERAND_TYPE_RAY_QUERY_COMMITTED_INTERSECTION_TYPE, // SPIR-V Sec + // 3.RQCommitted + SPV_OPERAND_TYPE_RAY_QUERY_CANDIDATE_INTERSECTION_TYPE, // SPIR-V Sec + // 3.RQCandidate + + // Concrete operand types for integer dot product. + // Packed vector format + SPV_OPERAND_TYPE_PACKED_VECTOR_FORMAT, // SPIR-V Sec 3.x + // An optional packed vector format + SPV_OPERAND_TYPE_OPTIONAL_PACKED_VECTOR_FORMAT, // This is a sentinel value, and does not represent an operand type. // It should come last. @@ -588,6 +608,8 @@ SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetRelaxLogicalPointer( // 3) Pointers that are actaul 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 +// for a first argument. SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetBeforeHlslLegalization( spv_validator_options options, bool val); @@ -671,7 +693,7 @@ SPIRV_TOOLS_EXPORT void spvOptimizerOptionsSetPreserveSpecConstants( // Creates a reducer options object with default options. Returns a valid // options object. The object remains valid until it is passed into // |spvReducerOptionsDestroy|. -SPIRV_TOOLS_EXPORT spv_reducer_options spvReducerOptionsCreate(); +SPIRV_TOOLS_EXPORT spv_reducer_options spvReducerOptionsCreate(void); // Destroys the given reducer options object. SPIRV_TOOLS_EXPORT void spvReducerOptionsDestroy(spv_reducer_options options); @@ -698,7 +720,7 @@ SPIRV_TOOLS_EXPORT void spvReducerOptionsSetTargetFunction( // Creates a fuzzer options object with default options. Returns a valid // options object. The object remains valid until it is passed into // |spvFuzzerOptionsDestroy|. -SPIRV_TOOLS_EXPORT spv_fuzzer_options spvFuzzerOptionsCreate(); +SPIRV_TOOLS_EXPORT spv_fuzzer_options spvFuzzerOptionsCreate(void); // Destroys the given fuzzer options object. SPIRV_TOOLS_EXPORT void spvFuzzerOptionsDestroy(spv_fuzzer_options options); diff --git a/include/spirv-tools/libspirv.hpp b/include/spirv-tools/libspirv.hpp index e7e7fc7a..0c31a182 100644 --- a/include/spirv-tools/libspirv.hpp +++ b/include/spirv-tools/libspirv.hpp @@ -136,6 +136,8 @@ class ValidatorOptions { // 3) Pointers that are actaul 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 + // for a first argument. void SetBeforeHlslLegalization(bool val) { spvValidatorOptionsSetBeforeHlslLegalization(options_, val); } diff --git a/include/spirv-tools/optimizer.hpp b/include/spirv-tools/optimizer.hpp index 1683d077..e8b5b698 100644 --- a/include/spirv-tools/optimizer.hpp +++ b/include/spirv-tools/optimizer.hpp @@ -838,6 +838,15 @@ Optimizer::PassToken CreateWrapOpKillPass(); // capabilities. Optimizer::PassToken CreateAmdExtToKhrPass(); +// Replaces the internal version of GLSLstd450 InterpolateAt* extended +// instructions with the externally valid version. The internal version allows +// an OpLoad of the interpolant for the first argument. This pass removes the +// OpLoad and replaces it with its pointer. glslang and possibly other +// frontends will create the internal version for HLSL. This pass will be part +// of HLSL legalization and should be called after interpolants have been +// propagated into their final positions. +Optimizer::PassToken CreateInterpolateFixupPass(); + } // namespace spvtools #endif // INCLUDE_SPIRV_TOOLS_OPTIMIZER_HPP_ diff --git a/kokoro/check-format/build.sh b/kokoro/check-format/build.sh index 0c4b8d1c..8a5df9a8 100644 --- a/kokoro/check-format/build.sh +++ b/kokoro/check-format/build.sh @@ -35,7 +35,8 @@ git clone https://github.com/google/googletest external/googletest cd external && cd googletest && git reset --hard 1fb1bb23bb8418dc73a5a9a82bbed31dc610fec7 && cd .. && cd .. git clone --depth=1 https://github.com/google/effcee external/effcee git clone --depth=1 https://github.com/google/re2 external/re2 -curl -L http://llvm.org/svn/llvm-project/cfe/trunk/tools/clang-format/clang-format-diff.py -o utils/clang-format-diff.py; +# The --fail flag causes the command to fail on HTTP error response codes, like 404. +curl -L --fail https://raw.githubusercontent.com/llvm/llvm-project/main/clang/tools/clang-format/clang-format-diff.py -o utils/clang-format-diff.py echo $(date): Check formatting... ./utils/check_code_format.sh; diff --git a/kokoro/scripts/windows/build.bat b/kokoro/scripts/windows/build.bat index a4ce792a..c964320d 100644 --- a/kokoro/scripts/windows/build.bat +++ b/kokoro/scripts/windows/build.bat @@ -41,9 +41,6 @@ if %VS_VERSION% == 2017 ( ) else if %VS_VERSION% == 2015 ( call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64 echo "Using VS 2015..." -) else if %VS_VERSION% == 2013 ( - call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" x64 - echo "Using VS 2013..." ) cd %SRC% @@ -62,15 +59,8 @@ if "%KOKORO_GITHUB_COMMIT%." == "." ( set CMAKE_FLAGS=-DCMAKE_INSTALL_PREFIX=%KOKORO_ARTIFACTS_DIR%\install -GNinja -DCMAKE_BUILD_TYPE=%BUILD_TYPE% -DRE2_BUILD_TESTING=OFF -DCMAKE_C_COMPILER=cl.exe -DCMAKE_CXX_COMPILER=cl.exe -:: Skip building tests for VS2013 -if %VS_VERSION% == 2013 ( - set CMAKE_FLAGS=%CMAKE_FLAGS% -DSPIRV_SKIP_TESTS=ON -) - -:: Skip building spirv-fuzz for VS2013; it relies on protobufs which VS2013 cannot handle. -if %VS_VERSION% NEQ 2013 ( - set CMAKE_FLAGS=%CMAKE_FLAGS% -DSPIRV_BUILD_FUZZER=ON -) +:: Build spirv-fuzz +set CMAKE_FLAGS=%CMAKE_FLAGS% -DSPIRV_BUILD_FUZZER=ON cmake %CMAKE_FLAGS% .. @@ -85,13 +75,11 @@ echo "Build Completed %DATE% %TIME%" setlocal ENABLEDELAYEDEXPANSION :: ################################################ -:: Run the tests (We no longer run tests on VS2013) +:: Run the tests :: ################################################ echo "Running Tests... %DATE% %TIME%" -if %VS_VERSION% NEQ 2013 ( - ctest -C %BUILD_TYPE% --output-on-failure --timeout 300 - if !ERRORLEVEL! NEQ 0 exit /b !ERRORLEVEL! -) +ctest -C %BUILD_TYPE% --output-on-failure --timeout 300 +if !ERRORLEVEL! NEQ 0 exit /b !ERRORLEVEL! echo "Tests Completed %DATE% %TIME%" :: ################################################ @@ -105,5 +93,4 @@ zip -r install.zip install rm -rf %SRC%\build rm -rf %SRC%\external -exit /b 0 - +exit /b 0
\ No newline at end of file diff --git a/kokoro/windows-msvc-2013-release/continuous.cfg b/kokoro/windows-msvc-2013-release/continuous.cfg deleted file mode 100644 index 5dfcba63..00000000 --- a/kokoro/windows-msvc-2013-release/continuous.cfg +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) 2018 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. - -# Continuous build configuration. -build_file: "SPIRV-Tools/kokoro/windows-msvc-2013-release/build.bat" diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 65087f2c..6633bc91 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -359,7 +359,7 @@ function(spirv_tools_default_target_options target) ) set_property(TARGET ${target} PROPERTY FOLDER "SPIRV-Tools libraries") spvtools_check_symbol_exports(${target}) - add_dependencies(${target} core_tables enum_string_mapping extinst_tables) + add_dependencies(${target} spirv-tools-build-version core_tables enum_string_mapping extinst_tables) endfunction() # Always build ${SPIRV_TOOLS}-shared. This is expected distro packages, and @@ -402,6 +402,12 @@ if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") endif() endif() +if (ANDROID) + foreach(target ${SPIRV_TOOLS_TARGETS}) + target_link_libraries(${target} PRIVATE android log) + endforeach() +endif() + if(ENABLE_SPIRV_TOOLS_INSTALL) install(TARGETS ${SPIRV_TOOLS_TARGETS} EXPORT ${SPIRV_TOOLS}Targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} diff --git a/source/binary.cpp b/source/binary.cpp index 7448721b..090cccfe 100644 --- a/source/binary.cpp +++ b/source/binary.cpp @@ -657,12 +657,18 @@ spv_result_t Parser::parseOperand(size_t inst_offset, case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION: case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_IMPORTED_ENTITY: case SPV_OPERAND_TYPE_FPDENORM_MODE: - case SPV_OPERAND_TYPE_FPOPERATION_MODE: { + case SPV_OPERAND_TYPE_FPOPERATION_MODE: + case SPV_OPERAND_TYPE_QUANTIZATION_MODES: + case SPV_OPERAND_TYPE_OVERFLOW_MODES: + case SPV_OPERAND_TYPE_PACKED_VECTOR_FORMAT: + case SPV_OPERAND_TYPE_OPTIONAL_PACKED_VECTOR_FORMAT: { // A single word that is a plain enum value. // Map an optional operand type to its corresponding concrete type. if (type == SPV_OPERAND_TYPE_OPTIONAL_ACCESS_QUALIFIER) parsed_operand.type = SPV_OPERAND_TYPE_ACCESS_QUALIFIER; + if (type == SPV_OPERAND_TYPE_OPTIONAL_PACKED_VECTOR_FORMAT) + parsed_operand.type = SPV_OPERAND_TYPE_PACKED_VECTOR_FORMAT; spv_operand_desc entry; if (grammar_.lookupOperand(type, word, &entry)) { diff --git a/source/disassemble.cpp b/source/disassemble.cpp index 966a59c7..c553988f 100644 --- a/source/disassemble.cpp +++ b/source/disassemble.cpp @@ -328,7 +328,9 @@ void Disassembler::EmitOperand(const spv_parsed_instruction_t& inst, case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION: case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_IMPORTED_ENTITY: case SPV_OPERAND_TYPE_FPDENORM_MODE: - case SPV_OPERAND_TYPE_FPOPERATION_MODE: { + case SPV_OPERAND_TYPE_FPOPERATION_MODE: + case SPV_OPERAND_TYPE_QUANTIZATION_MODES: + case SPV_OPERAND_TYPE_OVERFLOW_MODES: { spv_operand_desc entry; if (grammar_.lookupOperand(operand.type, word, &entry)) assert(false && "should have caught this earlier"); @@ -345,7 +347,17 @@ void Disassembler::EmitOperand(const spv_parsed_instruction_t& inst, EmitMaskOperand(operand.type, word); break; default: - assert(false && "unhandled or invalid case"); + if (spvOperandIsConcreteMask(operand.type)) { + EmitMaskOperand(operand.type, word); + } else if (spvOperandIsConcrete(operand.type)) { + spv_operand_desc entry; + if (grammar_.lookupOperand(operand.type, word, &entry)) + assert(false && "should have caught this earlier"); + stream_ << entry->name; + } else { + assert(false && "unhandled or invalid case"); + } + break; } ResetColor(); } diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt index d3aa9f1e..a8061662 100644 --- a/source/fuzz/CMakeLists.txt +++ b/source/fuzz/CMakeLists.txt @@ -37,6 +37,7 @@ if(SPIRV_BUILD_FUZZER) set(SPIRV_TOOLS_FUZZ_SOURCES added_function_reducer.h + available_instructions.h call_graph.h comparator_deep_blocks_first.h counter_overflow_id_source.h @@ -100,6 +101,7 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass_outline_functions.h fuzzer_pass_permute_blocks.h fuzzer_pass_permute_function_parameters.h + fuzzer_pass_permute_function_variables.h fuzzer_pass_permute_instructions.h fuzzer_pass_permute_phi_operands.h fuzzer_pass_propagate_instructions_down.h @@ -119,6 +121,7 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass_split_blocks.h fuzzer_pass_swap_commutable_operands.h fuzzer_pass_swap_conditional_branch_operands.h + fuzzer_pass_swap_functions.h fuzzer_pass_toggle_access_chain_instruction.h fuzzer_pass_wrap_regions_in_selections.h fuzzer_util.h @@ -221,6 +224,8 @@ if(SPIRV_BUILD_FUZZER) transformation_store.h transformation_swap_commutable_operands.h transformation_swap_conditional_branch_operands.h + transformation_swap_function_variables.h + transformation_swap_two_functions.h transformation_toggle_access_chain_instruction.h transformation_vector_shuffle.h transformation_wrap_early_terminator_in_function.h @@ -229,6 +234,7 @@ if(SPIRV_BUILD_FUZZER) ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.h added_function_reducer.cpp + available_instructions.cpp call_graph.cpp counter_overflow_id_source.cpp data_descriptor.cpp @@ -290,6 +296,7 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass_outline_functions.cpp fuzzer_pass_permute_blocks.cpp fuzzer_pass_permute_function_parameters.cpp + fuzzer_pass_permute_function_variables.cpp fuzzer_pass_permute_instructions.cpp fuzzer_pass_permute_phi_operands.cpp fuzzer_pass_propagate_instructions_down.cpp @@ -309,6 +316,7 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass_split_blocks.cpp fuzzer_pass_swap_commutable_operands.cpp fuzzer_pass_swap_conditional_branch_operands.cpp + fuzzer_pass_swap_functions.cpp fuzzer_pass_toggle_access_chain_instruction.cpp fuzzer_pass_wrap_regions_in_selections.cpp fuzzer_util.cpp @@ -409,6 +417,8 @@ if(SPIRV_BUILD_FUZZER) transformation_store.cpp transformation_swap_commutable_operands.cpp transformation_swap_conditional_branch_operands.cpp + transformation_swap_function_variables.cpp + transformation_swap_two_functions.cpp transformation_toggle_access_chain_instruction.cpp transformation_vector_shuffle.cpp transformation_wrap_early_terminator_in_function.cpp diff --git a/source/fuzz/available_instructions.cpp b/source/fuzz/available_instructions.cpp new file mode 100644 index 00000000..e25ed900 --- /dev/null +++ b/source/fuzz/available_instructions.cpp @@ -0,0 +1,191 @@ +// Copyright (c) 2021 Alastair F. Donaldson +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/available_instructions.h" +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +AvailableInstructions::AvailableInstructions( + opt::IRContext* ir_context, + const std::function<bool(opt::IRContext*, opt::Instruction*)>& predicate) + : ir_context_(ir_context) { + // Consider all global declarations + for (auto& global : ir_context->module()->types_values()) { + if (predicate(ir_context, &global)) { + available_globals_.push_back(&global); + } + } + + // Consider every function + for (auto& function : *ir_context->module()) { + // Identify those function parameters that satisfy the predicate. + std::vector<opt::Instruction*> available_params_for_function; + function.ForEachParam( + [&predicate, ir_context, + &available_params_for_function](opt::Instruction* param) { + if (predicate(ir_context, param)) { + available_params_for_function.push_back(param); + } + }); + + // Consider every reachable block in the function. + auto dominator_analysis = ir_context->GetDominatorAnalysis(&function); + for (auto& block : function) { + if (!fuzzerutil::BlockIsReachableInItsFunction(ir_context, &block)) { + // The block is not reachable. + continue; + } + if (&block == &*function.begin()) { + // The function entry block is special: only the relevant globals and + // function parameters are available at its entry point. + num_available_at_block_entry_.insert( + {&block, + static_cast<uint32_t>(available_params_for_function.size() + + available_globals_.size())}); + } else { + // |block| is not the entry block and is reachable, so it must have an + // immediate dominator. The number of instructions available on entry to + // |block| is thus the number of instructions available on entry to the + // immediate dominator + the number of instructions generated_by_block + // by the immediate dominator. + auto immediate_dominator = + dominator_analysis->ImmediateDominator(&block); + assert(immediate_dominator != nullptr && + "The block is reachable so should have an immediate dominator."); + assert(generated_by_block_.count(immediate_dominator) != 0 && + "Immediate dominator should have already been processed."); + assert(num_available_at_block_entry_.count(immediate_dominator) != 0 && + "Immediate dominator should have already been processed."); + num_available_at_block_entry_.insert( + {&block, + static_cast<uint32_t>( + generated_by_block_.at(immediate_dominator).size()) + + num_available_at_block_entry_.at(immediate_dominator)}); + } + // Now consider each instruction in the block. + std::vector<opt::Instruction*> generated_by_block; + for (auto& inst : block) { + assert(num_available_at_block_entry_.count(&block) != 0 && + "Block should have already been processed."); + // The number of available instructions before |inst| is the number + // available at the start of the block + the number of relevant + // instructions generated by the block so far. + num_available_before_instruction_.insert( + {&inst, num_available_at_block_entry_.at(&block) + + static_cast<uint32_t>(generated_by_block.size())}); + if (predicate(ir_context, &inst)) { + // This instruction satisfies the predicate, so note that it is + // generated by |block|. + generated_by_block.push_back(&inst); + } + } + generated_by_block_.emplace(&block, std::move(generated_by_block)); + } + available_params_.emplace(&function, + std::move(available_params_for_function)); + } +} + +AvailableInstructions::AvailableBeforeInstruction +AvailableInstructions::GetAvailableBeforeInstruction( + opt::Instruction* inst) const { + assert(num_available_before_instruction_.count(inst) != 0 && + "Availability can only be queried for reachable instructions."); + return {*this, inst}; +} + +AvailableInstructions::AvailableBeforeInstruction::AvailableBeforeInstruction( + const AvailableInstructions& available_instructions, opt::Instruction* inst) + : available_instructions_(available_instructions), inst_(inst) {} + +uint32_t AvailableInstructions::AvailableBeforeInstruction::size() const { + return available_instructions_.num_available_before_instruction_.at(inst_); +} + +bool AvailableInstructions::AvailableBeforeInstruction::empty() const { + return size() == 0; +} + +opt::Instruction* AvailableInstructions::AvailableBeforeInstruction::operator[]( + uint32_t index) const { + assert(index < size() && "Index out of bounds."); + + // First, check the cache to see whether we can return the available + // instruction in constant time. + auto cached_result = index_cache.find(index); + if (cached_result != index_cache.end()) { + return cached_result->second; + } + + // Next check whether the index falls into the global region. + if (index < available_instructions_.available_globals_.size()) { + auto result = available_instructions_.available_globals_[index]; + index_cache.insert({index, result}); + return result; + } + + auto block = available_instructions_.ir_context_->get_instr_block(inst_); + auto function = block->GetParent(); + + // Next check whether the index falls into the available instructions that + // correspond to function parameters. + if (index < + available_instructions_.available_globals_.size() + + available_instructions_.available_params_.at(function).size()) { + auto result = available_instructions_.available_params_.at( + function)[index - available_instructions_.available_globals_.size()]; + index_cache.insert({index, result}); + return result; + } + + auto dominator_analysis = + available_instructions_.ir_context_->GetDominatorAnalysis(function); + + // Now the expensive part (which is why we have the cache): walk the dominator + // tree backwards starting from the block containing |inst_| until we get to + // the block in which the instruction corresponding to |index| exists. + for (auto* ancestor = block; true; + ancestor = dominator_analysis->ImmediateDominator(ancestor)) { + uint32_t num_available_at_ancestor_entry = + available_instructions_.num_available_at_block_entry_.at(ancestor); + if (index_cache.count(num_available_at_ancestor_entry) == 0) { + // This is the first time we have traversed this block, so we populate the + // cache with the index of each instruction, so that if a future index + // query relates to indices associated with this block we can return the + // result in constant time. + auto& generated_by_ancestor = + available_instructions_.generated_by_block_.at(ancestor); + for (uint32_t local_index = 0; local_index < generated_by_ancestor.size(); + local_index++) { + index_cache.insert({num_available_at_ancestor_entry + local_index, + generated_by_ancestor[local_index]}); + } + } + if (index >= num_available_at_ancestor_entry) { + // This block contains the instruction we want, so by now it will be in + // the cache. + return index_cache.at(index); + } + assert(ancestor != &*function->begin() && + "By construction we should find a block associated with the index."); + } + + assert(false && "Unreachable."); + return nullptr; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/available_instructions.h b/source/fuzz/available_instructions.h new file mode 100644 index 00000000..5c0b4175 --- /dev/null +++ b/source/fuzz/available_instructions.h @@ -0,0 +1,111 @@ +// Copyright (c) 2021 Alastair F. Donaldson +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_AVAILABLE_INSTRUCTIONS_H_ +#define SOURCE_FUZZ_AVAILABLE_INSTRUCTIONS_H_ + +#include <unordered_map> +#include <vector> + +#include "source/opt/instruction.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +// A class for allowing efficient querying of the instruction that satisfy a +// particular predicate that are available before a given instruction. +// Availability information is only computed for instructions in *reachable* +// basic blocks. +class AvailableInstructions { + public: + // The outer class captures availability information for a whole module, and + // each instance of this inner class captures availability for a particular + // instruction. + class AvailableBeforeInstruction { + public: + AvailableBeforeInstruction( + const AvailableInstructions& available_instructions, + opt::Instruction* inst); + + // Returns the number of instructions that are available before the + // instruction associated with this class. + uint32_t size() const; + + // Returns true if and only if |size()| is 0. + bool empty() const; + + // Requires |index| < |size()|. Returns the ith available instruction. + opt::Instruction* operator[](uint32_t index) const; + + private: + // A references to an instance of the outer class. + const AvailableInstructions& available_instructions_; + + // The instruction for which availability information is captured. + opt::Instruction* inst_; + + // A cache to improve the efficiency of the [] operator. The [] operator + // requires walking the instruction's dominator tree to find an instruction + // at a particular index, which is a linear time operation. By inserting all + // instructions that are traversed during this search into a cache, future + // lookups will take constant time unless they require traversing the + // dominator tree more deeply. + mutable std::unordered_map<uint32_t, opt::Instruction*> index_cache; + }; + + // Constructs availability instructions for |ir_context|, where instructions + // are only available if they satisfy |predicate|. + AvailableInstructions( + opt::IRContext* ir_context, + const std::function<bool(opt::IRContext*, opt::Instruction*)>& predicate); + + // Yields instruction availability for |inst|. + AvailableBeforeInstruction GetAvailableBeforeInstruction( + opt::Instruction* inst) const; + + private: + // The module in which all instructions are contained. + opt::IRContext* ir_context_; + + // The global instructions that satisfy the predicate. + std::vector<opt::Instruction*> available_globals_; + + // Per function, the parameters that satisfy the predicate. + std::unordered_map<opt::Function*, std::vector<opt::Instruction*>> + available_params_; + + // The number of instructions that satisfy the predicate and that are + // available at the entry to a block. For the entry block of a function this + // is the number of available globals + the number of available function + // parameters. For any other block it is the number of available instructions + // for the blocks immediate dominator + the number of instructions generated + // by the immediate dominator. + std::unordered_map<opt::BasicBlock*, uint32_t> num_available_at_block_entry_; + + // For each block this records those instructions in the block that satisfy + // the predicate. + std::unordered_map<opt::BasicBlock*, std::vector<opt::Instruction*>> + generated_by_block_; + + // For each instruction this records how many instructions satisfying the + // predicate are available before the instruction. + std::unordered_map<opt::Instruction*, uint32_t> + num_available_before_instruction_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_AVAILABLE_INSTRUCTIONS_H_ diff --git a/source/fuzz/force_render_red.cpp b/source/fuzz/force_render_red.cpp index fd0587a3..3267487a 100644 --- a/source/fuzz/force_render_red.cpp +++ b/source/fuzz/force_render_red.cpp @@ -19,12 +19,10 @@ #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/transformation_context.h" #include "source/fuzz/transformation_replace_constant_with_uniform.h" -#include "source/fuzz/uniform_buffer_element_descriptor.h" #include "source/opt/build_module.h" #include "source/opt/ir_context.h" #include "source/opt/types.h" #include "source/util/make_unique.h" -#include "tools/util/cli_consumer.h" namespace spvtools { namespace fuzz { @@ -160,8 +158,8 @@ bool ForceRenderRed( const spv_target_env& target_env, spv_validator_options validator_options, const std::vector<uint32_t>& binary_in, const spvtools::fuzz::protobufs::FactSequence& initial_facts, + const MessageConsumer& message_consumer, std::vector<uint32_t>* binary_out) { - auto message_consumer = spvtools::utils::CLIMessageConsumer; spvtools::SpirvTools tools(target_env); if (!tools.IsValid()) { message_consumer(SPV_MSG_ERROR, nullptr, {}, diff --git a/source/fuzz/force_render_red.h b/source/fuzz/force_render_red.h index b51c72b4..5b8eab1b 100644 --- a/source/fuzz/force_render_red.h +++ b/source/fuzz/force_render_red.h @@ -41,7 +41,7 @@ bool ForceRenderRed( const spv_target_env& target_env, spv_validator_options validator_options, const std::vector<uint32_t>& binary_in, const spvtools::fuzz::protobufs::FactSequence& initial_facts, - std::vector<uint32_t>* binary_out); + const MessageConsumer& message_consumer, std::vector<uint32_t>* binary_out); } // namespace fuzz } // namespace spvtools diff --git a/source/fuzz/fuzzer.cpp b/source/fuzz/fuzzer.cpp index 40da4974..fe88a55b 100644 --- a/source/fuzz/fuzzer.cpp +++ b/source/fuzz/fuzzer.cpp @@ -17,9 +17,7 @@ #include <cassert> #include <memory> #include <numeric> -#include <sstream> -#include "source/fuzz/fact_manager/fact_manager.h" #include "source/fuzz/fuzzer_context.h" #include "source/fuzz/fuzzer_pass_add_access_chains.h" #include "source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h" @@ -69,6 +67,7 @@ #include "source/fuzz/fuzzer_pass_outline_functions.h" #include "source/fuzz/fuzzer_pass_permute_blocks.h" #include "source/fuzz/fuzzer_pass_permute_function_parameters.h" +#include "source/fuzz/fuzzer_pass_permute_function_variables.h" #include "source/fuzz/fuzzer_pass_permute_instructions.h" #include "source/fuzz/fuzzer_pass_permute_phi_operands.h" #include "source/fuzz/fuzzer_pass_propagate_instructions_down.h" @@ -88,12 +87,10 @@ #include "source/fuzz/fuzzer_pass_split_blocks.h" #include "source/fuzz/fuzzer_pass_swap_commutable_operands.h" #include "source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h" +#include "source/fuzz/fuzzer_pass_swap_functions.h" #include "source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h" #include "source/fuzz/fuzzer_pass_wrap_regions_in_selections.h" #include "source/fuzz/pass_management/repeated_pass_manager.h" -#include "source/fuzz/pass_management/repeated_pass_manager_looped_with_recommendations.h" -#include "source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.h" -#include "source/fuzz/pass_management/repeated_pass_manager_simple.h" #include "source/fuzz/pass_management/repeated_pass_recommender_standard.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/transformation_context.h" @@ -104,35 +101,149 @@ namespace spvtools { namespace fuzz { -namespace { -const uint32_t kIdBoundGap = 100; - -} // namespace - -Fuzzer::Fuzzer(spv_target_env target_env, MessageConsumer consumer, - const std::vector<uint32_t>& binary_in, - const protobufs::FactSequence& initial_facts, +Fuzzer::Fuzzer(std::unique_ptr<opt::IRContext> ir_context, + std::unique_ptr<TransformationContext> transformation_context, + std::unique_ptr<FuzzerContext> fuzzer_context, + MessageConsumer consumer, const std::vector<fuzzerutil::ModuleSupplier>& donor_suppliers, - std::unique_ptr<RandomGenerator> random_generator, bool enable_all_passes, RepeatedPassStrategy repeated_pass_strategy, bool validate_after_each_fuzzer_pass, spv_validator_options validator_options) - : target_env_(target_env), - consumer_(std::move(consumer)), - binary_in_(binary_in), - initial_facts_(initial_facts), - donor_suppliers_(donor_suppliers), - random_generator_(std::move(random_generator)), + : consumer_(std::move(consumer)), enable_all_passes_(enable_all_passes), - repeated_pass_strategy_(repeated_pass_strategy), validate_after_each_fuzzer_pass_(validate_after_each_fuzzer_pass), validator_options_(validator_options), num_repeated_passes_applied_(0), - ir_context_(nullptr), - fuzzer_context_(nullptr), - transformation_context_(nullptr), - transformation_sequence_out_() {} + is_valid_(true), + ir_context_(std::move(ir_context)), + transformation_context_(std::move(transformation_context)), + fuzzer_context_(std::move(fuzzer_context)), + transformation_sequence_out_(), + pass_instances_(), + repeated_pass_recommender_(nullptr), + repeated_pass_manager_(nullptr), + final_passes_() { + assert(ir_context_ && "IRContext is not initialized"); + assert(fuzzer_context_ && "FuzzerContext is not initialized"); + assert(transformation_context_ && "TransformationContext is not initialized"); + assert(fuzzerutil::IsValidAndWellFormed(ir_context_.get(), validator_options_, + consumer_) && + "IRContext is invalid"); + + // The following passes are likely to be very useful: many other passes + // introduce synonyms, irrelevant ids and constants that these passes can work + // with. We thus enable them with high probability. + MaybeAddRepeatedPass<FuzzerPassObfuscateConstants>(90, &pass_instances_); + MaybeAddRepeatedPass<FuzzerPassApplyIdSynonyms>(90, &pass_instances_); + MaybeAddRepeatedPass<FuzzerPassReplaceIrrelevantIds>(90, &pass_instances_); + + do { + // Each call to MaybeAddRepeatedPass randomly decides whether the given pass + // should be enabled, and adds an instance of the pass to |pass_instances| + // if it is enabled. + MaybeAddRepeatedPass<FuzzerPassAddAccessChains>(&pass_instances_); + MaybeAddRepeatedPass<FuzzerPassAddBitInstructionSynonyms>(&pass_instances_); + MaybeAddRepeatedPass<FuzzerPassAddCompositeExtract>(&pass_instances_); + MaybeAddRepeatedPass<FuzzerPassAddCompositeInserts>(&pass_instances_); + MaybeAddRepeatedPass<FuzzerPassAddCompositeTypes>(&pass_instances_); + MaybeAddRepeatedPass<FuzzerPassAddCopyMemory>(&pass_instances_); + MaybeAddRepeatedPass<FuzzerPassAddDeadBlocks>(&pass_instances_); + MaybeAddRepeatedPass<FuzzerPassAddDeadBreaks>(&pass_instances_); + MaybeAddRepeatedPass<FuzzerPassAddDeadContinues>(&pass_instances_); + MaybeAddRepeatedPass<FuzzerPassAddEquationInstructions>(&pass_instances_); + MaybeAddRepeatedPass<FuzzerPassAddFunctionCalls>(&pass_instances_); + MaybeAddRepeatedPass<FuzzerPassAddGlobalVariables>(&pass_instances_); + MaybeAddRepeatedPass<FuzzerPassAddImageSampleUnusedComponents>( + &pass_instances_); + MaybeAddRepeatedPass<FuzzerPassAddLoads>(&pass_instances_); + MaybeAddRepeatedPass<FuzzerPassAddLocalVariables>(&pass_instances_); + MaybeAddRepeatedPass<FuzzerPassAddLoopPreheaders>(&pass_instances_); + MaybeAddRepeatedPass<FuzzerPassAddLoopsToCreateIntConstantSynonyms>( + &pass_instances_); + MaybeAddRepeatedPass<FuzzerPassAddOpPhiSynonyms>(&pass_instances_); + MaybeAddRepeatedPass<FuzzerPassAddParameters>(&pass_instances_); + MaybeAddRepeatedPass<FuzzerPassAddRelaxedDecorations>(&pass_instances_); + MaybeAddRepeatedPass<FuzzerPassAddStores>(&pass_instances_); + MaybeAddRepeatedPass<FuzzerPassAddSynonyms>(&pass_instances_); + MaybeAddRepeatedPass<FuzzerPassAddVectorShuffleInstructions>( + &pass_instances_); + MaybeAddRepeatedPass<FuzzerPassConstructComposites>(&pass_instances_); + MaybeAddRepeatedPass<FuzzerPassCopyObjects>(&pass_instances_); + MaybeAddRepeatedPass<FuzzerPassDonateModules>(&pass_instances_, + donor_suppliers); + MaybeAddRepeatedPass<FuzzerPassDuplicateRegionsWithSelections>( + &pass_instances_); + MaybeAddRepeatedPass<FuzzerPassExpandVectorReductions>(&pass_instances_); + MaybeAddRepeatedPass<FuzzerPassFlattenConditionalBranches>( + &pass_instances_); + MaybeAddRepeatedPass<FuzzerPassInlineFunctions>(&pass_instances_); + MaybeAddRepeatedPass<FuzzerPassInvertComparisonOperators>(&pass_instances_); + MaybeAddRepeatedPass<FuzzerPassMakeVectorOperationsDynamic>( + &pass_instances_); + MaybeAddRepeatedPass<FuzzerPassMergeBlocks>(&pass_instances_); + MaybeAddRepeatedPass<FuzzerPassMergeFunctionReturns>(&pass_instances_); + MaybeAddRepeatedPass<FuzzerPassMutatePointers>(&pass_instances_); + MaybeAddRepeatedPass<FuzzerPassOutlineFunctions>(&pass_instances_); + MaybeAddRepeatedPass<FuzzerPassPermuteBlocks>(&pass_instances_); + MaybeAddRepeatedPass<FuzzerPassPermuteFunctionParameters>(&pass_instances_); + MaybeAddRepeatedPass<FuzzerPassPermuteInstructions>(&pass_instances_); + MaybeAddRepeatedPass<FuzzerPassPropagateInstructionsDown>(&pass_instances_); + MaybeAddRepeatedPass<FuzzerPassPropagateInstructionsUp>(&pass_instances_); + MaybeAddRepeatedPass<FuzzerPassPushIdsThroughVariables>(&pass_instances_); + MaybeAddRepeatedPass<FuzzerPassReplaceAddsSubsMulsWithCarryingExtended>( + &pass_instances_); + MaybeAddRepeatedPass<FuzzerPassReplaceBranchesFromDeadBlocksWithExits>( + &pass_instances_); + MaybeAddRepeatedPass<FuzzerPassReplaceCopyMemoriesWithLoadsStores>( + &pass_instances_); + MaybeAddRepeatedPass<FuzzerPassReplaceCopyObjectsWithStoresLoads>( + &pass_instances_); + MaybeAddRepeatedPass<FuzzerPassReplaceLoadsStoresWithCopyMemories>( + &pass_instances_); + MaybeAddRepeatedPass<FuzzerPassReplaceParameterWithGlobal>( + &pass_instances_); + MaybeAddRepeatedPass<FuzzerPassReplaceLinearAlgebraInstructions>( + &pass_instances_); + MaybeAddRepeatedPass<FuzzerPassReplaceOpPhiIdsFromDeadPredecessors>( + &pass_instances_); + MaybeAddRepeatedPass<FuzzerPassReplaceOpSelectsWithConditionalBranches>( + &pass_instances_); + MaybeAddRepeatedPass<FuzzerPassReplaceParamsWithStruct>(&pass_instances_); + MaybeAddRepeatedPass<FuzzerPassSplitBlocks>(&pass_instances_); + MaybeAddRepeatedPass<FuzzerPassSwapBranchConditionalOperands>( + &pass_instances_); + MaybeAddRepeatedPass<FuzzerPassWrapRegionsInSelections>(&pass_instances_); + // There is a theoretical possibility that no pass instances were created + // until now; loop again if so. + } while (pass_instances_.GetPasses().empty()); + + repeated_pass_recommender_ = MakeUnique<RepeatedPassRecommenderStandard>( + &pass_instances_, fuzzer_context_.get()); + repeated_pass_manager_ = RepeatedPassManager::Create( + repeated_pass_strategy, fuzzer_context_.get(), &pass_instances_, + repeated_pass_recommender_.get()); + + MaybeAddFinalPass<FuzzerPassAdjustBranchWeights>(&final_passes_); + MaybeAddFinalPass<FuzzerPassAdjustFunctionControls>(&final_passes_); + MaybeAddFinalPass<FuzzerPassAdjustLoopControls>(&final_passes_); + MaybeAddFinalPass<FuzzerPassAdjustMemoryOperandsMasks>(&final_passes_); + MaybeAddFinalPass<FuzzerPassAdjustSelectionControls>(&final_passes_); + MaybeAddFinalPass<FuzzerPassAddNoContractionDecorations>(&final_passes_); + if (!fuzzer_context_->IsWgslCompatible()) { + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/4214): + // this is disabled temporarily due to some issues in the Tint compiler. + // Enable it back when the issues are resolved. + MaybeAddFinalPass<FuzzerPassInterchangeSignednessOfIntegerOperands>( + &final_passes_); + } + MaybeAddFinalPass<FuzzerPassInterchangeZeroLikeConstants>(&final_passes_); + MaybeAddFinalPass<FuzzerPassPermuteFunctionVariables>(&final_passes_); + MaybeAddFinalPass<FuzzerPassPermutePhiOperands>(&final_passes_); + MaybeAddFinalPass<FuzzerPassSwapCommutableOperands>(&final_passes_); + MaybeAddFinalPass<FuzzerPassSwapFunctions>(&final_passes_); + MaybeAddFinalPass<FuzzerPassToggleAccessChainInstruction>(&final_passes_); +} Fuzzer::~Fuzzer() = default; @@ -165,240 +276,107 @@ bool Fuzzer::ApplyPassAndCheckValidity(FuzzerPass* pass) const { consumer_); } -Fuzzer::FuzzerResult Fuzzer::Run() { - // Check compatibility between the library version being linked with and the - // header files being used. - GOOGLE_PROTOBUF_VERIFY_VERSION; +opt::IRContext* Fuzzer::GetIRContext() { return ir_context_.get(); } - assert(ir_context_ == nullptr && fuzzer_context_ == nullptr && - transformation_context_ == nullptr && - transformation_sequence_out_.transformation_size() == 0 && - "'Run' must not be invoked more than once."); - - spvtools::SpirvTools tools(target_env_); - tools.SetMessageConsumer(consumer_); - if (!tools.IsValid()) { - consumer_(SPV_MSG_ERROR, nullptr, {}, - "Failed to create SPIRV-Tools interface; stopping."); - return {Fuzzer::FuzzerResultStatus::kFailedToCreateSpirvToolsInterface, - std::vector<uint32_t>(), protobufs::TransformationSequence()}; - } - - // Initial binary should be valid. - if (!tools.Validate(&binary_in_[0], binary_in_.size(), validator_options_)) { - consumer_(SPV_MSG_ERROR, nullptr, {}, - "Initial binary is invalid; stopping."); - return {Fuzzer::FuzzerResultStatus::kInitialBinaryInvalid, - std::vector<uint32_t>(), protobufs::TransformationSequence()}; - } - - // Build the module from the input binary. - ir_context_ = - BuildModule(target_env_, consumer_, binary_in_.data(), binary_in_.size()); - assert(ir_context_); - - // The fuzzer will introduce new ids into the module. The module's id bound - // gives the smallest id that can be used for this purpose. We add an offset - // to this so that there is a sizeable gap between the ids used in the - // original module and the ids used for fuzzing, as a readability aid. - // - // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2541) consider the - // case where the maximum id bound is reached. - auto minimum_fresh_id = ir_context_->module()->id_bound() + kIdBoundGap; - fuzzer_context_ = - MakeUnique<FuzzerContext>(random_generator_.get(), minimum_fresh_id); - - transformation_context_ = MakeUnique<TransformationContext>( - MakeUnique<FactManager>(ir_context_.get()), validator_options_); - transformation_context_->GetFactManager()->AddInitialFacts(consumer_, - initial_facts_); +const protobufs::TransformationSequence& Fuzzer::GetTransformationSequence() + const { + return transformation_sequence_out_; +} - RepeatedPassInstances pass_instances{}; +Fuzzer::Result Fuzzer::Run(uint32_t num_of_transformations_to_apply) { + assert(is_valid_ && "The module was invalidated during the previous fuzzing"); - // The following passes are likely to be very useful: many other passes - // introduce synonyms, irrelevant ids and constants that these passes can work - // with. We thus enable them with high probability. - MaybeAddRepeatedPass<FuzzerPassObfuscateConstants>(90, &pass_instances); - MaybeAddRepeatedPass<FuzzerPassApplyIdSynonyms>(90, &pass_instances); - MaybeAddRepeatedPass<FuzzerPassReplaceIrrelevantIds>(90, &pass_instances); + const auto initial_num_of_transformations = + static_cast<uint32_t>(transformation_sequence_out_.transformation_size()); + auto status = Status::kComplete; do { - // Each call to MaybeAddRepeatedPass randomly decides whether the given pass - // should be enabled, and adds an instance of the pass to |pass_instances| - // if it is enabled. - MaybeAddRepeatedPass<FuzzerPassAddAccessChains>(&pass_instances); - MaybeAddRepeatedPass<FuzzerPassAddBitInstructionSynonyms>(&pass_instances); - MaybeAddRepeatedPass<FuzzerPassAddCompositeExtract>(&pass_instances); - MaybeAddRepeatedPass<FuzzerPassAddCompositeInserts>(&pass_instances); - MaybeAddRepeatedPass<FuzzerPassAddCompositeTypes>(&pass_instances); - MaybeAddRepeatedPass<FuzzerPassAddCopyMemory>(&pass_instances); - MaybeAddRepeatedPass<FuzzerPassAddDeadBlocks>(&pass_instances); - MaybeAddRepeatedPass<FuzzerPassAddDeadBreaks>(&pass_instances); - MaybeAddRepeatedPass<FuzzerPassAddDeadContinues>(&pass_instances); - MaybeAddRepeatedPass<FuzzerPassAddEquationInstructions>(&pass_instances); - MaybeAddRepeatedPass<FuzzerPassAddFunctionCalls>(&pass_instances); - MaybeAddRepeatedPass<FuzzerPassAddGlobalVariables>(&pass_instances); - MaybeAddRepeatedPass<FuzzerPassAddImageSampleUnusedComponents>( - &pass_instances); - MaybeAddRepeatedPass<FuzzerPassAddLoads>(&pass_instances); - MaybeAddRepeatedPass<FuzzerPassAddLocalVariables>(&pass_instances); - MaybeAddRepeatedPass<FuzzerPassAddLoopPreheaders>(&pass_instances); - MaybeAddRepeatedPass<FuzzerPassAddLoopsToCreateIntConstantSynonyms>( - &pass_instances); - MaybeAddRepeatedPass<FuzzerPassAddOpPhiSynonyms>(&pass_instances); - MaybeAddRepeatedPass<FuzzerPassAddParameters>(&pass_instances); - MaybeAddRepeatedPass<FuzzerPassAddRelaxedDecorations>(&pass_instances); - MaybeAddRepeatedPass<FuzzerPassAddStores>(&pass_instances); - MaybeAddRepeatedPass<FuzzerPassAddSynonyms>(&pass_instances); - MaybeAddRepeatedPass<FuzzerPassAddVectorShuffleInstructions>( - &pass_instances); - MaybeAddRepeatedPass<FuzzerPassConstructComposites>(&pass_instances); - MaybeAddRepeatedPass<FuzzerPassCopyObjects>(&pass_instances); - MaybeAddRepeatedPass<FuzzerPassDonateModules>(&pass_instances, - donor_suppliers_); - MaybeAddRepeatedPass<FuzzerPassDuplicateRegionsWithSelections>( - &pass_instances); - MaybeAddRepeatedPass<FuzzerPassExpandVectorReductions>(&pass_instances); - MaybeAddRepeatedPass<FuzzerPassFlattenConditionalBranches>(&pass_instances); - MaybeAddRepeatedPass<FuzzerPassInlineFunctions>(&pass_instances); - MaybeAddRepeatedPass<FuzzerPassInvertComparisonOperators>(&pass_instances); - MaybeAddRepeatedPass<FuzzerPassMakeVectorOperationsDynamic>( - &pass_instances); - MaybeAddRepeatedPass<FuzzerPassMergeBlocks>(&pass_instances); - MaybeAddRepeatedPass<FuzzerPassMergeFunctionReturns>(&pass_instances); - MaybeAddRepeatedPass<FuzzerPassMutatePointers>(&pass_instances); - MaybeAddRepeatedPass<FuzzerPassOutlineFunctions>(&pass_instances); - MaybeAddRepeatedPass<FuzzerPassPermuteBlocks>(&pass_instances); - MaybeAddRepeatedPass<FuzzerPassPermuteFunctionParameters>(&pass_instances); - MaybeAddRepeatedPass<FuzzerPassPermuteInstructions>(&pass_instances); - MaybeAddRepeatedPass<FuzzerPassPropagateInstructionsDown>(&pass_instances); - MaybeAddRepeatedPass<FuzzerPassPropagateInstructionsUp>(&pass_instances); - MaybeAddRepeatedPass<FuzzerPassPushIdsThroughVariables>(&pass_instances); - MaybeAddRepeatedPass<FuzzerPassReplaceAddsSubsMulsWithCarryingExtended>( - &pass_instances); - MaybeAddRepeatedPass<FuzzerPassReplaceBranchesFromDeadBlocksWithExits>( - &pass_instances); - MaybeAddRepeatedPass<FuzzerPassReplaceCopyMemoriesWithLoadsStores>( - &pass_instances); - MaybeAddRepeatedPass<FuzzerPassReplaceCopyObjectsWithStoresLoads>( - &pass_instances); - MaybeAddRepeatedPass<FuzzerPassReplaceLoadsStoresWithCopyMemories>( - &pass_instances); - MaybeAddRepeatedPass<FuzzerPassReplaceParameterWithGlobal>(&pass_instances); - MaybeAddRepeatedPass<FuzzerPassReplaceLinearAlgebraInstructions>( - &pass_instances); - MaybeAddRepeatedPass<FuzzerPassReplaceOpPhiIdsFromDeadPredecessors>( - &pass_instances); - MaybeAddRepeatedPass<FuzzerPassReplaceOpSelectsWithConditionalBranches>( - &pass_instances); - MaybeAddRepeatedPass<FuzzerPassReplaceParamsWithStruct>(&pass_instances); - MaybeAddRepeatedPass<FuzzerPassSplitBlocks>(&pass_instances); - MaybeAddRepeatedPass<FuzzerPassSwapBranchConditionalOperands>( - &pass_instances); - MaybeAddRepeatedPass<FuzzerPassWrapRegionsInSelections>(&pass_instances); - // There is a theoretical possibility that no pass instances were created - // until now; loop again if so. - } while (pass_instances.GetPasses().empty()); - - RepeatedPassRecommenderStandard pass_recommender(&pass_instances, - fuzzer_context_.get()); + if (!ApplyPassAndCheckValidity( + repeated_pass_manager_->ChoosePass(transformation_sequence_out_))) { + status = Status::kFuzzerPassLedToInvalidModule; + break; + } - std::unique_ptr<RepeatedPassManager> repeated_pass_manager = nullptr; - switch (repeated_pass_strategy_) { - case RepeatedPassStrategy::kSimple: - repeated_pass_manager = MakeUnique<RepeatedPassManagerSimple>( - fuzzer_context_.get(), &pass_instances); + // Check that the module is small enough. + if (ir_context_->module()->id_bound() >= + fuzzer_context_->GetIdBoundLimit()) { + status = Status::kModuleTooBig; break; - case RepeatedPassStrategy::kLoopedWithRecommendations: - repeated_pass_manager = - MakeUnique<RepeatedPassManagerLoopedWithRecommendations>( - fuzzer_context_.get(), &pass_instances, &pass_recommender); + } + + auto transformations_applied_so_far = static_cast<uint32_t>( + transformation_sequence_out_.transformation_size()); + assert(transformations_applied_so_far >= initial_num_of_transformations && + "Number of transformations cannot decrease"); + + // Check if we've already applied the maximum number of transformations. + if (transformations_applied_so_far >= + fuzzer_context_->GetTransformationLimit()) { + status = Status::kTransformationLimitReached; break; - case RepeatedPassStrategy::kRandomWithRecommendations: - repeated_pass_manager = - MakeUnique<RepeatedPassManagerRandomWithRecommendations>( - fuzzer_context_.get(), &pass_instances, &pass_recommender); + } + + // Check that we've not got stuck (this can happen if the only available + // fuzzer passes are not able to apply any transformations, or can only + // apply very few transformations). + if (num_repeated_passes_applied_ >= + fuzzer_context_->GetTransformationLimit()) { + status = Status::kFuzzerStuck; break; - } + } - do { - if (!ApplyPassAndCheckValidity( - repeated_pass_manager->ChoosePass(transformation_sequence_out_))) { - return {Fuzzer::FuzzerResultStatus::kFuzzerPassLedToInvalidModule, - std::vector<uint32_t>(), protobufs::TransformationSequence()}; + // Check whether we've exceeded the number of transformations we can apply + // in a single call to this method. + if (num_of_transformations_to_apply != 0 && + transformations_applied_so_far - initial_num_of_transformations >= + num_of_transformations_to_apply) { + status = Status::kComplete; + break; } - } while (ShouldContinueFuzzing()); - // Now apply some passes that it does not make sense to apply repeatedly, - // as they do not unlock other passes. - std::vector<std::unique_ptr<FuzzerPass>> final_passes; - MaybeAddFinalPass<FuzzerPassAdjustBranchWeights>(&final_passes); - MaybeAddFinalPass<FuzzerPassAdjustFunctionControls>(&final_passes); - MaybeAddFinalPass<FuzzerPassAdjustLoopControls>(&final_passes); - MaybeAddFinalPass<FuzzerPassAdjustMemoryOperandsMasks>(&final_passes); - MaybeAddFinalPass<FuzzerPassAdjustSelectionControls>(&final_passes); - MaybeAddFinalPass<FuzzerPassAddNoContractionDecorations>(&final_passes); - MaybeAddFinalPass<FuzzerPassInterchangeSignednessOfIntegerOperands>( - &final_passes); - MaybeAddFinalPass<FuzzerPassInterchangeZeroLikeConstants>(&final_passes); - MaybeAddFinalPass<FuzzerPassPermutePhiOperands>(&final_passes); - MaybeAddFinalPass<FuzzerPassSwapCommutableOperands>(&final_passes); - MaybeAddFinalPass<FuzzerPassToggleAccessChainInstruction>(&final_passes); - for (auto& pass : final_passes) { - if (!ApplyPassAndCheckValidity(pass.get())) { - return {Fuzzer::FuzzerResultStatus::kFuzzerPassLedToInvalidModule, - std::vector<uint32_t>(), protobufs::TransformationSequence()}; + } while (ShouldContinueRepeatedPasses(num_of_transformations_to_apply == 0)); + + if (status != Status::kFuzzerPassLedToInvalidModule) { + // We apply this transformations despite the fact that we might exceed + // |num_of_transformations_to_apply|. This is not a problem for us since + // these fuzzer passes are relatively simple yet might trigger some bugs. + for (auto& pass : final_passes_) { + if (!ApplyPassAndCheckValidity(pass.get())) { + status = Status::kFuzzerPassLedToInvalidModule; + break; + } } } - // Encode the module as a binary. - std::vector<uint32_t> binary_out; - ir_context_->module()->ToBinary(&binary_out, false); - return {Fuzzer::FuzzerResultStatus::kComplete, std::move(binary_out), - std::move(transformation_sequence_out_)}; + is_valid_ = status != Status::kFuzzerPassLedToInvalidModule; + return {status, static_cast<uint32_t>( + transformation_sequence_out_.transformation_size()) != + initial_num_of_transformations}; } -bool Fuzzer::ShouldContinueFuzzing() { - // There's a risk that fuzzing could get stuck, if none of the enabled fuzzer - // passes are able to apply any transformations. To guard against this we - // count the number of times some repeated pass has been applied and ensure - // that fuzzing stops if the number of repeated passes hits the limit on the - // number of transformations that can be applied. - assert( - num_repeated_passes_applied_ <= - fuzzer_context_->GetTransformationLimit() && - "The number of repeated passes applied must not exceed its upper limit."); - if (ir_context_->module()->id_bound() >= fuzzer_context_->GetIdBoundLimit()) { - return false; - } - if (num_repeated_passes_applied_ == - fuzzer_context_->GetTransformationLimit()) { - // Stop because fuzzing has got stuck. - return false; - } - auto transformations_applied_so_far = - static_cast<uint32_t>(transformation_sequence_out_.transformation_size()); - if (transformations_applied_so_far >= - fuzzer_context_->GetTransformationLimit()) { - // Stop because we have reached the transformation limit. - return false; - } - // If we have applied T transformations so far, and the limit on the number of - // transformations to apply is L (where T < L), the chance that we will - // continue fuzzing is: - // - // 1 - T/(2*L) - // - // That is, the chance of continuing decreases as more transformations are - // applied. Using 2*L instead of L increases the number of transformations - // that are applied on average. - auto chance_of_continuing = static_cast<uint32_t>( - 100.0 * (1.0 - (static_cast<double>(transformations_applied_so_far) / - (2.0 * static_cast<double>( - fuzzer_context_->GetTransformationLimit()))))); - if (!fuzzer_context_->ChoosePercentage(chance_of_continuing)) { - // We have probabilistically decided to stop. - return false; +bool Fuzzer::ShouldContinueRepeatedPasses( + bool continue_fuzzing_probabilistically) { + if (continue_fuzzing_probabilistically) { + // If we have applied T transformations so far, and the limit on the number + // of transformations to apply is L (where T < L), the chance that we will + // continue fuzzing is: + // + // 1 - T/(2*L) + // + // That is, the chance of continuing decreases as more transformations are + // applied. Using 2*L instead of L increases the number of transformations + // that are applied on average. + auto transformations_applied_so_far = static_cast<uint32_t>( + transformation_sequence_out_.transformation_size()); + auto chance_of_continuing = static_cast<uint32_t>( + 100.0 * + (1.0 - (static_cast<double>(transformations_applied_so_far) / + (2.0 * static_cast<double>( + fuzzer_context_->GetTransformationLimit()))))); + if (!fuzzer_context_->ChoosePercentage(chance_of_continuing)) { + // We have probabilistically decided to stop. + return false; + } } // Continue fuzzing! num_repeated_passes_applied_++; diff --git a/source/fuzz/fuzzer.h b/source/fuzz/fuzzer.h index 774457f5..1e7d6860 100644 --- a/source/fuzz/fuzzer.h +++ b/source/fuzz/fuzzer.h @@ -23,6 +23,7 @@ #include "source/fuzz/fuzzer_pass.h" #include "source/fuzz/fuzzer_util.h" #include "source/fuzz/pass_management/repeated_pass_instances.h" +#include "source/fuzz/pass_management/repeated_pass_manager.h" #include "source/fuzz/pass_management/repeated_pass_recommender.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/random_generator.h" @@ -37,33 +38,28 @@ namespace fuzz { class Fuzzer { public: // Possible statuses that can result from running the fuzzer. - enum class FuzzerResultStatus { + enum class Status { kComplete, - kFailedToCreateSpirvToolsInterface, + kModuleTooBig, + kTransformationLimitReached, + kFuzzerStuck, kFuzzerPassLedToInvalidModule, - kInitialBinaryInvalid, }; - struct FuzzerResult { - FuzzerResultStatus status; - std::vector<uint32_t> transformed_binary; - protobufs::TransformationSequence applied_transformations; - }; + struct Result { + // Status of the fuzzing session. + Status status; - // Each field of this enum corresponds to an available repeated pass - // strategy, and is used to decide which kind of RepeatedPassManager object - // to create. - enum class RepeatedPassStrategy { - kSimple, - kRandomWithRecommendations, - kLoopedWithRecommendations + // Equals to true if new transformations were applied during the previous + // fuzzing session. + bool is_changed; }; - Fuzzer(spv_target_env target_env, MessageConsumer consumer, - const std::vector<uint32_t>& binary_in, - const protobufs::FactSequence& initial_facts, + Fuzzer(std::unique_ptr<opt::IRContext> ir_context, + std::unique_ptr<TransformationContext> transformation_context, + std::unique_ptr<FuzzerContext> fuzzer_context, + MessageConsumer consumer, const std::vector<fuzzerutil::ModuleSupplier>& donor_suppliers, - std::unique_ptr<RandomGenerator> random_generator, bool enable_all_passes, RepeatedPassStrategy repeated_pass_strategy, bool validate_after_each_fuzzer_pass, spv_validator_options validator_options); @@ -76,15 +72,23 @@ class Fuzzer { ~Fuzzer(); - // Transforms |binary_in_| by running a number of randomized fuzzer passes. - // Initial facts about the input binary and the context in which it will - // execute are provided via |initial_facts_|. A source of donor modules to be - // used by transformations is provided via |donor_suppliers_|. On success, - // returns a successful result status together with the transformed binary and - // the sequence of transformations that were applied. Otherwise, returns an - // appropriate result status together with an empty binary and empty - // transformation sequence. - FuzzerResult Run(); + // Transforms |ir_context_| by running a number of randomized fuzzer passes. + // Initial facts about the input binary and the context in which it will be + // executed are provided with |transformation_context_|. + // |num_of_transformations| is equal to the maximum number of transformations + // applied in a single call to this method. This parameter is ignored if its + // value is equal to 0. Because fuzzing cannot stop mid way through a fuzzer + // pass, fuzzing will stop after the fuzzer pass that exceeds + // |num_of_transformations| has completed, so that the total number of + // transformations may be somewhat larger than this number. + Result Run(uint32_t num_of_transformations_to_apply); + + // Returns the current IR context. It may be invalid if the Run method + // returned Status::kFuzzerPassLedToInvalidModule previously. + opt::IRContext* GetIRContext(); + + // Returns the sequence of applied transformations. + const protobufs::TransformationSequence& GetTransformationSequence() const; private: // A convenience method to add a repeated fuzzer pass to |pass_instances| with @@ -119,7 +123,9 @@ class Fuzzer { // Decides whether to apply more repeated passes. The probability decreases as // the number of transformations that have been applied increases. - bool ShouldContinueFuzzing(); + // The described probability is only applied if + // |continue_fuzzing_probabilistically| is true. + bool ShouldContinueRepeatedPasses(bool continue_fuzzing_probabilistically); // Applies |pass|, which must be a pass constructed with |ir_context|. // If |validate_after_each_fuzzer_pass_| is not set, true is always returned. @@ -128,57 +134,59 @@ class Fuzzer { // instruction has a distinct unique id. bool ApplyPassAndCheckValidity(FuzzerPass* pass) const; - // Target environment. - const spv_target_env target_env_; - // Message consumer that will be invoked once for each message communicated // from the library. - MessageConsumer consumer_; - - // The initial binary to which fuzzing should be applied. - const std::vector<uint32_t>& binary_in_; - - // Initial facts known to hold in advance of applying any transformations. - const protobufs::FactSequence& initial_facts_; - - // A source of modules whose contents can be donated into the module being - // fuzzed. - const std::vector<fuzzerutil::ModuleSupplier>& donor_suppliers_; - - // Random number generator to control decision making during fuzzing. - std::unique_ptr<RandomGenerator> random_generator_; + const MessageConsumer consumer_; // Determines whether all passes should be enabled, vs. having passes be // probabilistically enabled. - bool enable_all_passes_; - - // Controls which type of RepeatedPassManager object to create. - RepeatedPassStrategy repeated_pass_strategy_; + const bool enable_all_passes_; // Determines whether the validator should be invoked after every fuzzer pass. - bool validate_after_each_fuzzer_pass_; + const bool validate_after_each_fuzzer_pass_; // Options to control validation. - spv_validator_options validator_options_; + const spv_validator_options validator_options_; // The number of repeated fuzzer passes that have been applied is kept track // of, in order to enforce a hard limit on the number of times such passes // can be applied. uint32_t num_repeated_passes_applied_; + // We use this to determine whether we can continue fuzzing incrementally + // since the previous call to the Run method could've returned + // kFuzzerPassLedToInvalidModule. + bool is_valid_; + // Intermediate representation for the module being fuzzed, which gets // mutated as fuzzing proceeds. std::unique_ptr<opt::IRContext> ir_context_; + // Contextual information that is required in order to apply + // transformations. + std::unique_ptr<TransformationContext> transformation_context_; + // Provides probabilities that control the fuzzing process. std::unique_ptr<FuzzerContext> fuzzer_context_; - // Contextual information that is required in order to apply transformations. - std::unique_ptr<TransformationContext> transformation_context_; - - // The sequence of transformations that have been applied during fuzzing. It + // The sequence of transformations that have been applied during fuzzing. It // is initially empty and grows as fuzzer passes are applied. protobufs::TransformationSequence transformation_sequence_out_; + + // This object contains instances of all fuzzer passes that will participate + // in the fuzzing. + RepeatedPassInstances pass_instances_; + + // This object defines the recommendation logic for fuzzer passes. + std::unique_ptr<RepeatedPassRecommender> repeated_pass_recommender_; + + // This object manager a list of fuzzer pass and their available + // recommendations. + std::unique_ptr<RepeatedPassManager> repeated_pass_manager_; + + // Some passes that it does not make sense to apply repeatedly, as they do not + // unlock other passes. + std::vector<std::unique_ptr<FuzzerPass>> final_passes_; }; } // namespace fuzz diff --git a/source/fuzz/fuzzer_context.cpp b/source/fuzz/fuzzer_context.cpp index 47bf4e25..9e9650f6 100644 --- a/source/fuzz/fuzzer_context.cpp +++ b/source/fuzz/fuzzer_context.cpp @@ -21,6 +21,12 @@ namespace fuzz { namespace { +// An offset between the 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. +const uint32_t kIdBoundGap = 100; + // Limits to help control the overall fuzzing process and rein in individual // fuzzer passes. const uint32_t kIdBoundLimit = 50000; @@ -114,6 +120,8 @@ const std::pair<uint32_t, uint32_t> kChanceOfMovingBlockDown = {20, 50}; const std::pair<uint32_t, uint32_t> kChanceOfMutatingPointer = {20, 90}; const std::pair<uint32_t, uint32_t> kChanceOfObfuscatingConstant = {10, 90}; const std::pair<uint32_t, uint32_t> kChanceOfOutliningFunction = {10, 90}; +const std::pair<uint32_t, uint32_t> kChanceOfPermutingFunctionVariables = {30, + 90}; const std::pair<uint32_t, uint32_t> kChanceOfPermutingInstructions = {20, 70}; const std::pair<uint32_t, uint32_t> kChanceOfPermutingParameters = {30, 90}; const std::pair<uint32_t, uint32_t> kChanceOfPermutingPhiOperands = {30, 90}; @@ -145,8 +153,11 @@ const std::pair<uint32_t, uint32_t> kChanceOfReplacingParametersWithGlobals = { const std::pair<uint32_t, uint32_t> kChanceOfReplacingParametersWithStruct = { 20, 40}; const std::pair<uint32_t, uint32_t> kChanceOfSplittingBlock = {40, 95}; +const std::pair<uint32_t, uint32_t> + kChanceOfSwappingAnotherPairOfFunctionVariables = {30, 90}; const std::pair<uint32_t, uint32_t> kChanceOfSwappingConditionalBranchOperands = {10, 70}; +const std::pair<uint32_t, uint32_t> kChanceOfSwappingFunctions = {10, 90}; const std::pair<uint32_t, uint32_t> kChanceOfTogglingAccessChainInstruction = { 20, 90}; const std::pair<uint32_t, uint32_t> kChanceOfWrappingRegionInSelection = {70, @@ -177,10 +188,11 @@ const std::function<bool(uint32_t, RandomGenerator*)> } // namespace -FuzzerContext::FuzzerContext(RandomGenerator* random_generator, - uint32_t min_fresh_id) - : random_generator_(random_generator), +FuzzerContext::FuzzerContext(std::unique_ptr<RandomGenerator> random_generator, + uint32_t min_fresh_id, bool is_wgsl_compatible) + : random_generator_(std::move(random_generator)), next_fresh_id_(min_fresh_id), + is_wgsl_compatible_(is_wgsl_compatible), max_equivalence_class_size_for_data_synonym_fact_closure_( kDefaultMaxEquivalenceClassSizeForDataSynonymFactClosure), max_loop_control_partial_count_(kDefaultMaxLoopControlPartialCount), @@ -308,6 +320,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator, ChooseBetweenMinAndMax(kChanceOfObfuscatingConstant); chance_of_outlining_function_ = ChooseBetweenMinAndMax(kChanceOfOutliningFunction); + chance_of_permuting_function_variables_ = + ChooseBetweenMinAndMax(kChanceOfPermutingFunctionVariables); chance_of_permuting_instructions_ = ChooseBetweenMinAndMax(kChanceOfPermutingInstructions); chance_of_permuting_parameters_ = @@ -345,8 +359,12 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator, chance_of_replacing_parameters_with_struct_ = ChooseBetweenMinAndMax(kChanceOfReplacingParametersWithStruct); chance_of_splitting_block_ = ChooseBetweenMinAndMax(kChanceOfSplittingBlock); + chance_of_swapping_another_pair_of_function_variables_ = + ChooseBetweenMinAndMax(kChanceOfSwappingAnotherPairOfFunctionVariables); chance_of_swapping_conditional_branch_operands_ = ChooseBetweenMinAndMax(kChanceOfSwappingConditionalBranchOperands); + chance_of_swapping_functions_ = + ChooseBetweenMinAndMax(kChanceOfSwappingFunctions); chance_of_toggling_access_chain_instruction_ = ChooseBetweenMinAndMax(kChanceOfTogglingAccessChainInstruction); chance_of_wrapping_region_in_selection_ = @@ -403,5 +421,9 @@ uint32_t FuzzerContext::GetTransformationLimit() const { return kTransformationLimit; } +uint32_t FuzzerContext::GetMinFreshId(opt::IRContext* ir_context) { + return ir_context->module()->id_bound() + kIdBoundGap; +} + } // namespace fuzz } // namespace spvtools diff --git a/source/fuzz/fuzzer_context.h b/source/fuzz/fuzzer_context.h index 8c510418..ef2cc2c5 100644 --- a/source/fuzz/fuzzer_context.h +++ b/source/fuzz/fuzzer_context.h @@ -21,6 +21,7 @@ #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" #include "source/fuzz/random_generator.h" #include "source/opt/function.h" +#include "source/opt/ir_context.h" namespace spvtools { namespace fuzz { @@ -32,7 +33,8 @@ class FuzzerContext { public: // Constructs a fuzzer context with a given random generator and the minimum // value that can be used for fresh ids. - FuzzerContext(RandomGenerator* random_generator, uint32_t min_fresh_id); + FuzzerContext(std::unique_ptr<RandomGenerator> random_generator, + uint32_t min_fresh_id, bool is_wgsl_compatible); ~FuzzerContext(); @@ -64,7 +66,7 @@ class FuzzerContext { } // Randomly shuffles a |sequence| between |lo| and |hi| indices inclusively. - // |lo| and |hi| must be valid indices to the |sequence| + // |lo| and |hi| must be valid indices to the |sequence|. template <typename T> void Shuffle(std::vector<T>* sequence, size_t lo, size_t hi) const { auto& array = *sequence; @@ -89,7 +91,7 @@ class FuzzerContext { } } - // Ramdomly shuffles a |sequence| + // Randomly shuffles a |sequence|. template <typename T> void Shuffle(std::vector<T>* sequence) const { if (!sequence->empty()) { @@ -102,7 +104,7 @@ class FuzzerContext { uint32_t GetFreshId(); // Returns a vector of |count| fresh ids. - std::vector<uint32_t> GetFreshIds(const uint32_t count); + std::vector<uint32_t> GetFreshIds(uint32_t count); // A suggested limit on the id bound for the module being fuzzed. This is // useful for deciding when to stop the overall fuzzing process. Furthermore, @@ -115,6 +117,14 @@ class FuzzerContext { // fuzzer passes. uint32_t GetTransformationLimit() const; + // Returns the minimum fresh id that can be used given the |ir_context|. + static uint32_t GetMinFreshId(opt::IRContext* ir_context); + + // Returns true if all transformations should be compatible with WGSL. + bool IsWgslCompatible() const { + return is_wgsl_compatible_; + } + // Probabilities associated with applying various transformations. // Keep them in alphabetical order. uint32_t GetChanceOfAcceptingRepeatedPassRecommendation() const { @@ -293,6 +303,9 @@ class FuzzerContext { uint32_t GetChanceOfOutliningFunction() const { return chance_of_outlining_function_; } + uint32_t GetChanceOfPermutingFunctionVariables() const { + return chance_of_permuting_function_variables_; + } uint32_t GetChanceOfPermutingInstructions() const { return chance_of_permuting_instructions_; } @@ -350,9 +363,17 @@ class FuzzerContext { uint32_t GetChanceOfSplittingBlock() const { return chance_of_splitting_block_; } + uint32_t GetChanceOfSwappingAnotherPairOfFunctionVariables() const { + return chance_of_swapping_another_pair_of_function_variables_; + } uint32_t GetChanceOfSwappingConditionalBranchOperands() const { return chance_of_swapping_conditional_branch_operands_; } + + uint32_t GetChanceOfSwappingFunctions() const { + return chance_of_swapping_functions_; + } + uint32_t GetChanceOfTogglingAccessChainInstruction() const { return chance_of_toggling_access_chain_instruction_; } @@ -442,15 +463,18 @@ class FuzzerContext { return random_generator_->RandomUint32(max_unused_component_count) + 1; } bool GoDeeperInConstantObfuscation(uint32_t depth) { - return go_deeper_in_constant_obfuscation_(depth, random_generator_); + return go_deeper_in_constant_obfuscation_(depth, random_generator_.get()); } private: // The source of randomness. - RandomGenerator* random_generator_; + std::unique_ptr<RandomGenerator> random_generator_; // The next fresh id to be issued. uint32_t next_fresh_id_; + // True if all transformations should be compatible with WGSL spec. + bool is_wgsl_compatible_; + // Probabilities associated with applying various transformations. // Keep them in alphabetical order. uint32_t chance_of_accepting_repeated_pass_recommendation_; @@ -513,6 +537,7 @@ class FuzzerContext { uint32_t chance_of_mutating_pointer_; uint32_t chance_of_obfuscating_constant_; uint32_t chance_of_outlining_function_; + uint32_t chance_of_permuting_function_variables_; uint32_t chance_of_permuting_instructions_; uint32_t chance_of_permuting_parameters_; uint32_t chance_of_permuting_phi_operands_; @@ -532,7 +557,9 @@ class FuzzerContext { uint32_t chance_of_replacing_parameters_with_globals_; uint32_t chance_of_replacing_parameters_with_struct_; uint32_t chance_of_splitting_block_; + uint32_t chance_of_swapping_another_pair_of_function_variables_; uint32_t chance_of_swapping_conditional_branch_operands_; + uint32_t chance_of_swapping_functions_; uint32_t chance_of_toggling_access_chain_instruction_; uint32_t chance_of_wrapping_region_in_selection_; diff --git a/source/fuzz/fuzzer_pass.cpp b/source/fuzz/fuzzer_pass.cpp index 486f18fa..f67efc66 100644 --- a/source/fuzz/fuzzer_pass.cpp +++ b/source/fuzz/fuzzer_pass.cpp @@ -184,6 +184,35 @@ void FuzzerPass::ForEachInstructionWithInstructionDescriptor( } } +void FuzzerPass::ApplyTransformation(const Transformation& transformation) { + assert(transformation.IsApplicable(GetIRContext(), + *GetTransformationContext()) && + "Transformation should be applicable by construction."); + transformation.Apply(GetIRContext(), GetTransformationContext()); + auto transformation_message = transformation.ToMessage(); + assert(transformation_message.transformation_case() != + protobufs::Transformation::TRANSFORMATION_NOT_SET && + "Bad transformation."); + *GetTransformations()->add_transformation() = + std::move(transformation_message); +} + +bool FuzzerPass::MaybeApplyTransformation( + const Transformation& transformation) { + if (transformation.IsApplicable(GetIRContext(), + *GetTransformationContext())) { + transformation.Apply(GetIRContext(), GetTransformationContext()); + auto transformation_message = transformation.ToMessage(); + assert(transformation_message.transformation_case() != + protobufs::Transformation::TRANSFORMATION_NOT_SET && + "Bad transformation."); + *GetTransformations()->add_transformation() = + std::move(transformation_message); + return true; + } + return false; +} + uint32_t FuzzerPass::FindOrCreateBoolType() { if (auto existing_id = fuzzerutil::MaybeGetBoolType(GetIRContext())) { return existing_id; diff --git a/source/fuzz/fuzzer_pass.h b/source/fuzz/fuzzer_pass.h index da3f2390..ec254855 100644 --- a/source/fuzz/fuzzer_pass.h +++ b/source/fuzz/fuzzer_pass.h @@ -106,37 +106,13 @@ class FuzzerPass { // A generic helper for applying a transformation that should be applicable // by construction, and adding it to the sequence of applied transformations. - void ApplyTransformation(const Transformation& transformation) { - assert(transformation.IsApplicable(GetIRContext(), - *GetTransformationContext()) && - "Transformation should be applicable by construction."); - transformation.Apply(GetIRContext(), GetTransformationContext()); - protobufs::Transformation transformation_message = - transformation.ToMessage(); - assert(transformation_message.transformation_case() != - protobufs::Transformation::TRANSFORMATION_NOT_SET && - "Bad transformation."); - *GetTransformations()->add_transformation() = transformation_message; - } + void ApplyTransformation(const Transformation& transformation); // A generic helper for applying a transformation only if it is applicable. // If it is applicable, the transformation is applied and then added to the // sequence of applied transformations and the function returns true. // Otherwise, the function returns false. - bool MaybeApplyTransformation(const Transformation& transformation) { - if (transformation.IsApplicable(GetIRContext(), - *GetTransformationContext())) { - transformation.Apply(GetIRContext(), GetTransformationContext()); - protobufs::Transformation transformation_message = - transformation.ToMessage(); - assert(transformation_message.transformation_case() != - protobufs::Transformation::TRANSFORMATION_NOT_SET && - "Bad transformation."); - *GetTransformations()->add_transformation() = transformation_message; - return true; - } - return false; - } + bool MaybeApplyTransformation(const Transformation& transformation); // Returns the id of an OpTypeBool instruction. If such an instruction does // not exist, a transformation is applied to add it. diff --git a/source/fuzz/fuzzer_pass_add_access_chains.cpp b/source/fuzz/fuzzer_pass_add_access_chains.cpp index 11155f23..c498642c 100644 --- a/source/fuzz/fuzzer_pass_add_access_chains.cpp +++ b/source/fuzz/fuzzer_pass_add_access_chains.cpp @@ -27,8 +27,6 @@ FuzzerPassAddAccessChains::FuzzerPassAddAccessChains( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassAddAccessChains::~FuzzerPassAddAccessChains() = default; - void FuzzerPassAddAccessChains::Apply() { ForEachInstructionWithInstructionDescriptor( [this](opt::Function* function, opt::BasicBlock* block, diff --git a/source/fuzz/fuzzer_pass_add_access_chains.h b/source/fuzz/fuzzer_pass_add_access_chains.h index 8649296f..e80c2c6c 100644 --- a/source/fuzz/fuzzer_pass_add_access_chains.h +++ b/source/fuzz/fuzzer_pass_add_access_chains.h @@ -30,8 +30,6 @@ class FuzzerPassAddAccessChains : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassAddAccessChains(); - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp b/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp index 7b9ac4e2..78abf5be 100644 --- a/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp +++ b/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp @@ -28,9 +28,6 @@ FuzzerPassAddBitInstructionSynonyms::FuzzerPassAddBitInstructionSynonyms( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassAddBitInstructionSynonyms::~FuzzerPassAddBitInstructionSynonyms() = - default; - void FuzzerPassAddBitInstructionSynonyms::Apply() { for (auto& function : *GetIRContext()->module()) { for (auto& block : function) { @@ -48,22 +45,11 @@ void FuzzerPassAddBitInstructionSynonyms::Apply() { continue; } - // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3557): - // Right now we only support certain operations. When this issue is - // addressed the following conditional can use the function - // |spvOpcodeIsBit|. - if (instruction.opcode() != SpvOpBitwiseOr && - instruction.opcode() != SpvOpBitwiseXor && - instruction.opcode() != SpvOpBitwiseAnd && - instruction.opcode() != SpvOpNot) { - continue; - } - - // Right now, only integer operands are supported. - if (GetIRContext() - ->get_type_mgr() - ->GetType(instruction.type_id()) - ->AsVector()) { + // Make sure fuzzer never applies a transformation to a bitwise + // instruction with differently signed operands, only integer operands + // are supported and bitwise operations are supported only. + if (!TransformationAddBitInstructionSynonym::IsInstructionSupported( + GetIRContext(), &instruction)) { continue; } diff --git a/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h b/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h index 0194425d..28f95779 100644 --- a/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h +++ b/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h @@ -30,8 +30,6 @@ class FuzzerPassAddBitInstructionSynonyms : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassAddBitInstructionSynonyms(); - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_composite_extract.cpp b/source/fuzz/fuzzer_pass_add_composite_extract.cpp index 132a49da..19f99024 100644 --- a/source/fuzz/fuzzer_pass_add_composite_extract.cpp +++ b/source/fuzz/fuzzer_pass_add_composite_extract.cpp @@ -14,6 +14,7 @@ #include "source/fuzz/fuzzer_pass_add_composite_extract.h" +#include "source/fuzz/available_instructions.h" #include "source/fuzz/fuzzer_context.h" #include "source/fuzz/fuzzer_util.h" #include "source/fuzz/instruction_descriptor.h" @@ -29,8 +30,6 @@ FuzzerPassAddCompositeExtract::FuzzerPassAddCompositeExtract( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassAddCompositeExtract::~FuzzerPassAddCompositeExtract() = default; - void FuzzerPassAddCompositeExtract::Apply() { std::vector<const protobufs::DataDescriptor*> composite_synonyms; for (const auto* dd : @@ -41,14 +40,16 @@ void FuzzerPassAddCompositeExtract::Apply() { } } - // We don't want to invalidate the module every time we apply this - // transformation since rebuilding DominatorAnalysis can be expensive, so we - // collect up the transformations we wish to apply and apply them all later. - std::vector<TransformationCompositeExtract> transformations; + AvailableInstructions available_composites( + GetIRContext(), [](opt::IRContext* ir_context, opt::Instruction* inst) { + return inst->type_id() && inst->result_id() && + fuzzerutil::IsCompositeType( + ir_context->get_type_mgr()->GetType(inst->type_id())); + }); ForEachInstructionWithInstructionDescriptor( - [this, &composite_synonyms, &transformations]( - opt::Function* function, opt::BasicBlock* block, + [this, &available_composites, &composite_synonyms]( + opt::Function* /*unused*/, opt::BasicBlock* /*unused*/, opt::BasicBlock::iterator inst_it, const protobufs::InstructionDescriptor& instruction_descriptor) { if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCompositeExtract, @@ -61,14 +62,6 @@ void FuzzerPassAddCompositeExtract::Apply() { return; } - auto available_composites = FindAvailableInstructions( - function, block, inst_it, - [](opt::IRContext* ir_context, opt::Instruction* inst) { - return inst->type_id() && inst->result_id() && - fuzzerutil::IsCompositeType( - ir_context->get_type_mgr()->GetType(inst->type_id())); - }); - std::vector<const protobufs::DataDescriptor*> available_synonyms; for (const auto* dd : composite_synonyms) { if (fuzzerutil::IdIsAvailableBeforeInstruction( @@ -77,18 +70,21 @@ void FuzzerPassAddCompositeExtract::Apply() { } } - if (available_synonyms.empty() && available_composites.empty()) { + auto candidate_composites = + available_composites.GetAvailableBeforeInstruction(&*inst_it); + + if (available_synonyms.empty() && candidate_composites.empty()) { return; } uint32_t composite_id = 0; std::vector<uint32_t> indices; - if (available_synonyms.empty() || (!available_composites.empty() && + if (available_synonyms.empty() || (!candidate_composites.empty() && GetFuzzerContext()->ChooseEven())) { const auto* inst = - available_composites[GetFuzzerContext()->RandomIndex( - available_composites)]; + candidate_composites[GetFuzzerContext()->RandomIndex( + candidate_composites)]; composite_id = inst->result_id(); auto type_id = inst->type_id(); @@ -153,14 +149,10 @@ void FuzzerPassAddCompositeExtract::Apply() { assert(composite_id != 0 && !indices.empty() && "Composite object should have been chosen correctly"); - transformations.emplace_back(instruction_descriptor, - GetFuzzerContext()->GetFreshId(), - composite_id, indices); + ApplyTransformation(TransformationCompositeExtract( + instruction_descriptor, GetFuzzerContext()->GetFreshId(), + composite_id, indices)); }); - - for (const auto& transformation : transformations) { - ApplyTransformation(transformation); - } } } // namespace fuzz diff --git a/source/fuzz/fuzzer_pass_add_composite_extract.h b/source/fuzz/fuzzer_pass_add_composite_extract.h index 8bcb825f..32ac190a 100644 --- a/source/fuzz/fuzzer_pass_add_composite_extract.h +++ b/source/fuzz/fuzzer_pass_add_composite_extract.h @@ -29,8 +29,6 @@ class FuzzerPassAddCompositeExtract : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassAddCompositeExtract() override; - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_composite_inserts.cpp b/source/fuzz/fuzzer_pass_add_composite_inserts.cpp index e58c754c..cf314d30 100644 --- a/source/fuzz/fuzzer_pass_add_composite_inserts.cpp +++ b/source/fuzz/fuzzer_pass_add_composite_inserts.cpp @@ -29,8 +29,6 @@ FuzzerPassAddCompositeInserts::FuzzerPassAddCompositeInserts( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassAddCompositeInserts::~FuzzerPassAddCompositeInserts() = default; - void FuzzerPassAddCompositeInserts::Apply() { ForEachInstructionWithInstructionDescriptor( [this](opt::Function* function, opt::BasicBlock* block, diff --git a/source/fuzz/fuzzer_pass_add_composite_inserts.h b/source/fuzz/fuzzer_pass_add_composite_inserts.h index c4f51034..4d511f6c 100644 --- a/source/fuzz/fuzzer_pass_add_composite_inserts.h +++ b/source/fuzz/fuzzer_pass_add_composite_inserts.h @@ -29,7 +29,6 @@ class FuzzerPassAddCompositeInserts : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassAddCompositeInserts(); void Apply() override; // Checks if any component of a composite is a pointer. diff --git a/source/fuzz/fuzzer_pass_add_composite_types.cpp b/source/fuzz/fuzzer_pass_add_composite_types.cpp index c4d8d1c9..3dfbd690 100644 --- a/source/fuzz/fuzzer_pass_add_composite_types.cpp +++ b/source/fuzz/fuzzer_pass_add_composite_types.cpp @@ -28,8 +28,6 @@ FuzzerPassAddCompositeTypes::FuzzerPassAddCompositeTypes( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassAddCompositeTypes::~FuzzerPassAddCompositeTypes() = default; - void FuzzerPassAddCompositeTypes::Apply() { MaybeAddMissingVectorTypes(); MaybeAddMissingMatrixTypes(); @@ -125,7 +123,9 @@ uint32_t FuzzerPassAddCompositeTypes::ChooseScalarOrCompositeType() { break; case SpvOpTypeStruct: { if (!fuzzerutil::MembersHaveBuiltInDecoration(GetIRContext(), - inst.result_id())) { + inst.result_id()) && + !fuzzerutil::HasBlockOrBufferBlockDecoration(GetIRContext(), + inst.result_id())) { candidates.push_back(inst.result_id()); } } break; diff --git a/source/fuzz/fuzzer_pass_add_composite_types.h b/source/fuzz/fuzzer_pass_add_composite_types.h index 87bc0ff3..89d48f8a 100644 --- a/source/fuzz/fuzzer_pass_add_composite_types.h +++ b/source/fuzz/fuzzer_pass_add_composite_types.h @@ -29,8 +29,6 @@ class FuzzerPassAddCompositeTypes : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassAddCompositeTypes(); - void Apply() override; private: diff --git a/source/fuzz/fuzzer_pass_add_copy_memory.cpp b/source/fuzz/fuzzer_pass_add_copy_memory.cpp index d98619c2..b654927f 100644 --- a/source/fuzz/fuzzer_pass_add_copy_memory.cpp +++ b/source/fuzz/fuzzer_pass_add_copy_memory.cpp @@ -29,8 +29,6 @@ FuzzerPassAddCopyMemory::FuzzerPassAddCopyMemory( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassAddCopyMemory::~FuzzerPassAddCopyMemory() = default; - void FuzzerPassAddCopyMemory::Apply() { ForEachInstructionWithInstructionDescriptor( [this](opt::Function* function, opt::BasicBlock* block, diff --git a/source/fuzz/fuzzer_pass_add_copy_memory.h b/source/fuzz/fuzzer_pass_add_copy_memory.h index 321e4a1d..0f7db0c7 100644 --- a/source/fuzz/fuzzer_pass_add_copy_memory.h +++ b/source/fuzz/fuzzer_pass_add_copy_memory.h @@ -29,8 +29,6 @@ class FuzzerPassAddCopyMemory : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassAddCopyMemory() override; - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_dead_blocks.cpp b/source/fuzz/fuzzer_pass_add_dead_blocks.cpp index 84ed1fb9..8c166a20 100644 --- a/source/fuzz/fuzzer_pass_add_dead_blocks.cpp +++ b/source/fuzz/fuzzer_pass_add_dead_blocks.cpp @@ -14,12 +14,20 @@ #include "source/fuzz/fuzzer_pass_add_dead_blocks.h" +#include <algorithm> + #include "source/fuzz/fuzzer_util.h" #include "source/fuzz/transformation_add_dead_block.h" namespace spvtools { namespace fuzz { +namespace { + +const size_t kMaxTransformationsInOnePass = 100U; + +} // namespace + FuzzerPassAddDeadBlocks::FuzzerPassAddDeadBlocks( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, @@ -27,8 +35,6 @@ FuzzerPassAddDeadBlocks::FuzzerPassAddDeadBlocks( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassAddDeadBlocks::~FuzzerPassAddDeadBlocks() = default; - void FuzzerPassAddDeadBlocks::Apply() { // We iterate over all blocks in the module collecting up those at which we // might add a branch to a new dead block. We then loop over all such @@ -57,9 +63,18 @@ void FuzzerPassAddDeadBlocks::Apply() { GetFuzzerContext()->GetFreshId(), block.id(), condition_value)); } } - // Apply all those transformations that are in fact applicable. - for (auto& transformation : candidate_transformations) { - MaybeApplyTransformation(transformation); + // Applying transformations can be expensive as each transformation requires + // dominator information and also invalidates dominator information. We thus + // limit the number of transformations that one application of this fuzzer + // pass can apply. We choose to do this after identifying all the + // transformations that we *might* want to apply, rather than breaking the + // above loops once the limit is reached, to avoid biasing towards + // transformations that target early parts of the module. + GetFuzzerContext()->Shuffle(&candidate_transformations); + for (size_t i = 0; i < std::min(kMaxTransformationsInOnePass, + candidate_transformations.size()); + i++) { + MaybeApplyTransformation(candidate_transformations[i]); } } diff --git a/source/fuzz/fuzzer_pass_add_dead_blocks.h b/source/fuzz/fuzzer_pass_add_dead_blocks.h index d78f0883..a87c05c3 100644 --- a/source/fuzz/fuzzer_pass_add_dead_blocks.h +++ b/source/fuzz/fuzzer_pass_add_dead_blocks.h @@ -29,8 +29,6 @@ class FuzzerPassAddDeadBlocks : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassAddDeadBlocks(); - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_dead_breaks.cpp b/source/fuzz/fuzzer_pass_add_dead_breaks.cpp index e726c63e..0c18da90 100644 --- a/source/fuzz/fuzzer_pass_add_dead_breaks.cpp +++ b/source/fuzz/fuzzer_pass_add_dead_breaks.cpp @@ -28,8 +28,6 @@ FuzzerPassAddDeadBreaks::FuzzerPassAddDeadBreaks( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassAddDeadBreaks::~FuzzerPassAddDeadBreaks() = default; - void FuzzerPassAddDeadBreaks::Apply() { // We first collect up lots of possibly-applicable transformations. std::vector<TransformationAddDeadBreak> candidate_transformations; diff --git a/source/fuzz/fuzzer_pass_add_dead_breaks.h b/source/fuzz/fuzzer_pass_add_dead_breaks.h index c379eed5..d1086fc4 100644 --- a/source/fuzz/fuzzer_pass_add_dead_breaks.h +++ b/source/fuzz/fuzzer_pass_add_dead_breaks.h @@ -28,8 +28,6 @@ class FuzzerPassAddDeadBreaks : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassAddDeadBreaks(); - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_dead_continues.cpp b/source/fuzz/fuzzer_pass_add_dead_continues.cpp index 24617ae3..1ab40b73 100644 --- a/source/fuzz/fuzzer_pass_add_dead_continues.cpp +++ b/source/fuzz/fuzzer_pass_add_dead_continues.cpp @@ -28,8 +28,6 @@ FuzzerPassAddDeadContinues::FuzzerPassAddDeadContinues( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassAddDeadContinues::~FuzzerPassAddDeadContinues() = default; - void FuzzerPassAddDeadContinues::Apply() { // Consider every block in every function. for (auto& function : *GetIRContext()->module()) { diff --git a/source/fuzz/fuzzer_pass_add_dead_continues.h b/source/fuzz/fuzzer_pass_add_dead_continues.h index b2acb935..bf0009e4 100644 --- a/source/fuzz/fuzzer_pass_add_dead_continues.h +++ b/source/fuzz/fuzzer_pass_add_dead_continues.h @@ -28,8 +28,6 @@ class FuzzerPassAddDeadContinues : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassAddDeadContinues(); - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_equation_instructions.cpp b/source/fuzz/fuzzer_pass_add_equation_instructions.cpp index 6376c9fc..15554b7a 100644 --- a/source/fuzz/fuzzer_pass_add_equation_instructions.cpp +++ b/source/fuzz/fuzzer_pass_add_equation_instructions.cpp @@ -49,9 +49,6 @@ FuzzerPassAddEquationInstructions::FuzzerPassAddEquationInstructions( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassAddEquationInstructions::~FuzzerPassAddEquationInstructions() = - default; - void FuzzerPassAddEquationInstructions::Apply() { ForEachInstructionWithInstructionDescriptor( [this](opt::Function* function, opt::BasicBlock* block, diff --git a/source/fuzz/fuzzer_pass_add_equation_instructions.h b/source/fuzz/fuzzer_pass_add_equation_instructions.h index 9ce581eb..dbec5bac 100644 --- a/source/fuzz/fuzzer_pass_add_equation_instructions.h +++ b/source/fuzz/fuzzer_pass_add_equation_instructions.h @@ -31,8 +31,6 @@ class FuzzerPassAddEquationInstructions : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassAddEquationInstructions(); - void Apply() override; private: diff --git a/source/fuzz/fuzzer_pass_add_function_calls.cpp b/source/fuzz/fuzzer_pass_add_function_calls.cpp index 74005579..2240696b 100644 --- a/source/fuzz/fuzzer_pass_add_function_calls.cpp +++ b/source/fuzz/fuzzer_pass_add_function_calls.cpp @@ -30,8 +30,6 @@ FuzzerPassAddFunctionCalls::FuzzerPassAddFunctionCalls( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassAddFunctionCalls::~FuzzerPassAddFunctionCalls() = default; - void FuzzerPassAddFunctionCalls::Apply() { ForEachInstructionWithInstructionDescriptor( [this](opt::Function* function, opt::BasicBlock* block, diff --git a/source/fuzz/fuzzer_pass_add_function_calls.h b/source/fuzz/fuzzer_pass_add_function_calls.h index 4ed87911..081510c1 100644 --- a/source/fuzz/fuzzer_pass_add_function_calls.h +++ b/source/fuzz/fuzzer_pass_add_function_calls.h @@ -29,8 +29,6 @@ class FuzzerPassAddFunctionCalls : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassAddFunctionCalls(); - void Apply() override; private: diff --git a/source/fuzz/fuzzer_pass_add_global_variables.cpp b/source/fuzz/fuzzer_pass_add_global_variables.cpp index 9a45a374..06797413 100644 --- a/source/fuzz/fuzzer_pass_add_global_variables.cpp +++ b/source/fuzz/fuzzer_pass_add_global_variables.cpp @@ -27,8 +27,6 @@ FuzzerPassAddGlobalVariables::FuzzerPassAddGlobalVariables( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassAddGlobalVariables::~FuzzerPassAddGlobalVariables() = default; - void FuzzerPassAddGlobalVariables::Apply() { SpvStorageClass variable_storage_class = SpvStorageClassPrivate; for (auto& entry_point : GetIRContext()->module()->entry_points()) { diff --git a/source/fuzz/fuzzer_pass_add_global_variables.h b/source/fuzz/fuzzer_pass_add_global_variables.h index a907d360..3745c5c3 100644 --- a/source/fuzz/fuzzer_pass_add_global_variables.h +++ b/source/fuzz/fuzzer_pass_add_global_variables.h @@ -29,8 +29,6 @@ class FuzzerPassAddGlobalVariables : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassAddGlobalVariables(); - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp b/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp index 3095bb6e..c10db720 100644 --- a/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp +++ b/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp @@ -31,9 +31,6 @@ FuzzerPassAddImageSampleUnusedComponents:: : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassAddImageSampleUnusedComponents:: - ~FuzzerPassAddImageSampleUnusedComponents() = default; - void FuzzerPassAddImageSampleUnusedComponents::Apply() { // SPIR-V module to help understand the transformation. // diff --git a/source/fuzz/fuzzer_pass_add_image_sample_unused_components.h b/source/fuzz/fuzzer_pass_add_image_sample_unused_components.h index 26374c32..f5dea8bf 100644 --- a/source/fuzz/fuzzer_pass_add_image_sample_unused_components.h +++ b/source/fuzz/fuzzer_pass_add_image_sample_unused_components.h @@ -30,8 +30,6 @@ class FuzzerPassAddImageSampleUnusedComponents : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassAddImageSampleUnusedComponents(); - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_loads.cpp b/source/fuzz/fuzzer_pass_add_loads.cpp index 256255bc..2e50da26 100644 --- a/source/fuzz/fuzzer_pass_add_loads.cpp +++ b/source/fuzz/fuzzer_pass_add_loads.cpp @@ -27,8 +27,6 @@ FuzzerPassAddLoads::FuzzerPassAddLoads( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassAddLoads::~FuzzerPassAddLoads() = default; - void FuzzerPassAddLoads::Apply() { ForEachInstructionWithInstructionDescriptor( [this](opt::Function* function, opt::BasicBlock* block, diff --git a/source/fuzz/fuzzer_pass_add_loads.h b/source/fuzz/fuzzer_pass_add_loads.h index c4d5b27e..3913c624 100644 --- a/source/fuzz/fuzzer_pass_add_loads.h +++ b/source/fuzz/fuzzer_pass_add_loads.h @@ -28,8 +28,6 @@ class FuzzerPassAddLoads : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassAddLoads(); - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_local_variables.cpp b/source/fuzz/fuzzer_pass_add_local_variables.cpp index ef8b5d0e..a50b0b07 100644 --- a/source/fuzz/fuzzer_pass_add_local_variables.cpp +++ b/source/fuzz/fuzzer_pass_add_local_variables.cpp @@ -28,8 +28,6 @@ FuzzerPassAddLocalVariables::FuzzerPassAddLocalVariables( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassAddLocalVariables::~FuzzerPassAddLocalVariables() = default; - void FuzzerPassAddLocalVariables::Apply() { auto basic_type_ids_and_pointers = GetAvailableBasicTypesAndPointers(SpvStorageClassFunction); diff --git a/source/fuzz/fuzzer_pass_add_local_variables.h b/source/fuzz/fuzzer_pass_add_local_variables.h index 08d26d8c..d73dae29 100644 --- a/source/fuzz/fuzzer_pass_add_local_variables.h +++ b/source/fuzz/fuzzer_pass_add_local_variables.h @@ -29,8 +29,6 @@ class FuzzerPassAddLocalVariables : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassAddLocalVariables(); - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_loop_preheaders.cpp b/source/fuzz/fuzzer_pass_add_loop_preheaders.cpp index bdc31513..1cfed866 100644 --- a/source/fuzz/fuzzer_pass_add_loop_preheaders.cpp +++ b/source/fuzz/fuzzer_pass_add_loop_preheaders.cpp @@ -27,8 +27,6 @@ FuzzerPassAddLoopPreheaders::FuzzerPassAddLoopPreheaders( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassAddLoopPreheaders::~FuzzerPassAddLoopPreheaders() = default; - void FuzzerPassAddLoopPreheaders::Apply() { for (auto& function : *GetIRContext()->module()) { // Keep track of all the loop headers we want to add a preheader to. diff --git a/source/fuzz/fuzzer_pass_add_loop_preheaders.h b/source/fuzz/fuzzer_pass_add_loop_preheaders.h index a8350567..8ac2dac8 100644 --- a/source/fuzz/fuzzer_pass_add_loop_preheaders.h +++ b/source/fuzz/fuzzer_pass_add_loop_preheaders.h @@ -32,8 +32,6 @@ class FuzzerPassAddLoopPreheaders : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassAddLoopPreheaders(); - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.cpp b/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.cpp index 31a57798..69e0697f 100644 --- a/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.cpp +++ b/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.cpp @@ -33,9 +33,6 @@ FuzzerPassAddLoopsToCreateIntConstantSynonyms:: : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassAddLoopsToCreateIntConstantSynonyms:: - ~FuzzerPassAddLoopsToCreateIntConstantSynonyms() = default; - void FuzzerPassAddLoopsToCreateIntConstantSynonyms::Apply() { std::vector<uint32_t> constants; diff --git a/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h b/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h index ee98c4e7..2eacef5f 100644 --- a/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h +++ b/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h @@ -30,8 +30,6 @@ class FuzzerPassAddLoopsToCreateIntConstantSynonyms : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassAddLoopsToCreateIntConstantSynonyms(); - void Apply() override; private: diff --git a/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp b/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp index 09627d09..d0753100 100644 --- a/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp +++ b/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp @@ -26,9 +26,6 @@ FuzzerPassAddNoContractionDecorations::FuzzerPassAddNoContractionDecorations( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassAddNoContractionDecorations:: - ~FuzzerPassAddNoContractionDecorations() = default; - void FuzzerPassAddNoContractionDecorations::Apply() { // Consider every instruction in every block in every function. for (auto& function : *GetIRContext()->module()) { diff --git a/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h b/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h index c0c1e09d..74212d87 100644 --- a/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h +++ b/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h @@ -28,8 +28,6 @@ class FuzzerPassAddNoContractionDecorations : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassAddNoContractionDecorations() override; - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp b/source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp index 2b339ca0..be6e7ea1 100644 --- a/source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp +++ b/source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp @@ -27,8 +27,6 @@ FuzzerPassAddOpPhiSynonyms::FuzzerPassAddOpPhiSynonyms( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassAddOpPhiSynonyms::~FuzzerPassAddOpPhiSynonyms() = default; - void FuzzerPassAddOpPhiSynonyms::Apply() { // Get a list of synonymous ids with the same type that can be used in the // same OpPhi instruction. diff --git a/source/fuzz/fuzzer_pass_add_opphi_synonyms.h b/source/fuzz/fuzzer_pass_add_opphi_synonyms.h index 6c7d7522..9077118e 100644 --- a/source/fuzz/fuzzer_pass_add_opphi_synonyms.h +++ b/source/fuzz/fuzzer_pass_add_opphi_synonyms.h @@ -30,8 +30,6 @@ class FuzzerPassAddOpPhiSynonyms : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassAddOpPhiSynonyms() override; - void Apply() override; // Computes the equivalence classes for the non-pointer and non-irrelevant ids diff --git a/source/fuzz/fuzzer_pass_add_parameters.cpp b/source/fuzz/fuzzer_pass_add_parameters.cpp index 35532e90..784653d8 100644 --- a/source/fuzz/fuzzer_pass_add_parameters.cpp +++ b/source/fuzz/fuzzer_pass_add_parameters.cpp @@ -29,8 +29,6 @@ FuzzerPassAddParameters::FuzzerPassAddParameters( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassAddParameters::~FuzzerPassAddParameters() = default; - void FuzzerPassAddParameters::Apply() { // Compute type candidates for the new parameter. std::vector<uint32_t> type_candidates; diff --git a/source/fuzz/fuzzer_pass_add_parameters.h b/source/fuzz/fuzzer_pass_add_parameters.h index f1261ae5..47dde390 100644 --- a/source/fuzz/fuzzer_pass_add_parameters.h +++ b/source/fuzz/fuzzer_pass_add_parameters.h @@ -32,8 +32,6 @@ class FuzzerPassAddParameters : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassAddParameters() override; - void Apply() override; private: diff --git a/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp b/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp index a2497df7..58c6d1b2 100644 --- a/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp +++ b/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp @@ -26,8 +26,6 @@ FuzzerPassAddRelaxedDecorations::FuzzerPassAddRelaxedDecorations( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassAddRelaxedDecorations::~FuzzerPassAddRelaxedDecorations() = default; - void FuzzerPassAddRelaxedDecorations::Apply() { // Consider every instruction in every block in every function. for (auto& function : *GetIRContext()->module()) { diff --git a/source/fuzz/fuzzer_pass_add_relaxed_decorations.h b/source/fuzz/fuzzer_pass_add_relaxed_decorations.h index 897b8216..723c4a0a 100644 --- a/source/fuzz/fuzzer_pass_add_relaxed_decorations.h +++ b/source/fuzz/fuzzer_pass_add_relaxed_decorations.h @@ -28,8 +28,6 @@ class FuzzerPassAddRelaxedDecorations : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassAddRelaxedDecorations() override; - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_stores.cpp b/source/fuzz/fuzzer_pass_add_stores.cpp index 46efc643..f89428d3 100644 --- a/source/fuzz/fuzzer_pass_add_stores.cpp +++ b/source/fuzz/fuzzer_pass_add_stores.cpp @@ -27,8 +27,6 @@ FuzzerPassAddStores::FuzzerPassAddStores( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassAddStores::~FuzzerPassAddStores() = default; - void FuzzerPassAddStores::Apply() { ForEachInstructionWithInstructionDescriptor( [this](opt::Function* function, opt::BasicBlock* block, diff --git a/source/fuzz/fuzzer_pass_add_stores.h b/source/fuzz/fuzzer_pass_add_stores.h index 55ec67f2..9519c385 100644 --- a/source/fuzz/fuzzer_pass_add_stores.h +++ b/source/fuzz/fuzzer_pass_add_stores.h @@ -30,8 +30,6 @@ class FuzzerPassAddStores : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassAddStores(); - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_synonyms.cpp b/source/fuzz/fuzzer_pass_add_synonyms.cpp index 2fa07000..fd866f98 100644 --- a/source/fuzz/fuzzer_pass_add_synonyms.cpp +++ b/source/fuzz/fuzzer_pass_add_synonyms.cpp @@ -29,8 +29,6 @@ FuzzerPassAddSynonyms::FuzzerPassAddSynonyms( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassAddSynonyms::~FuzzerPassAddSynonyms() = default; - void FuzzerPassAddSynonyms::Apply() { ForEachInstructionWithInstructionDescriptor( [this](opt::Function* function, opt::BasicBlock* block, @@ -81,6 +79,8 @@ void FuzzerPassAddSynonyms::Apply() { case protobufs::TransformationAddSynonym::ADD_ZERO: case protobufs::TransformationAddSynonym::SUB_ZERO: case protobufs::TransformationAddSynonym::LOGICAL_OR: + case protobufs::TransformationAddSynonym::BITWISE_OR: + case protobufs::TransformationAddSynonym::BITWISE_XOR: // Create a zero constant to be used as an operand of the synonymous // instruction. FindOrCreateZeroConstant(existing_synonym->type_id(), false); diff --git a/source/fuzz/fuzzer_pass_add_synonyms.h b/source/fuzz/fuzzer_pass_add_synonyms.h index dcfb9389..ccf4a886 100644 --- a/source/fuzz/fuzzer_pass_add_synonyms.h +++ b/source/fuzz/fuzzer_pass_add_synonyms.h @@ -29,8 +29,6 @@ class FuzzerPassAddSynonyms : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassAddSynonyms() override; - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp b/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp index 453448ba..e2eaaa0a 100644 --- a/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp +++ b/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp @@ -28,9 +28,6 @@ FuzzerPassAddVectorShuffleInstructions::FuzzerPassAddVectorShuffleInstructions( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassAddVectorShuffleInstructions:: - ~FuzzerPassAddVectorShuffleInstructions() = default; - void FuzzerPassAddVectorShuffleInstructions::Apply() { ForEachInstructionWithInstructionDescriptor( [this](opt::Function* function, opt::BasicBlock* block, diff --git a/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h b/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h index 99b9f244..c5af374c 100644 --- a/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h +++ b/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h @@ -28,8 +28,6 @@ class FuzzerPassAddVectorShuffleInstructions : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassAddVectorShuffleInstructions(); - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_adjust_branch_weights.cpp b/source/fuzz/fuzzer_pass_adjust_branch_weights.cpp index 1d6d4347..3c4f3803 100644 --- a/source/fuzz/fuzzer_pass_adjust_branch_weights.cpp +++ b/source/fuzz/fuzzer_pass_adjust_branch_weights.cpp @@ -28,8 +28,6 @@ FuzzerPassAdjustBranchWeights::FuzzerPassAdjustBranchWeights( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassAdjustBranchWeights::~FuzzerPassAdjustBranchWeights() = default; - void FuzzerPassAdjustBranchWeights::Apply() { // For all OpBranchConditional instructions, // randomly applies the transformation. diff --git a/source/fuzz/fuzzer_pass_adjust_branch_weights.h b/source/fuzz/fuzzer_pass_adjust_branch_weights.h index 5b2b33f1..de2f33d3 100644 --- a/source/fuzz/fuzzer_pass_adjust_branch_weights.h +++ b/source/fuzz/fuzzer_pass_adjust_branch_weights.h @@ -30,8 +30,6 @@ class FuzzerPassAdjustBranchWeights : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassAdjustBranchWeights(); - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_adjust_function_controls.cpp b/source/fuzz/fuzzer_pass_adjust_function_controls.cpp index aa62d2f8..b38bd212 100644 --- a/source/fuzz/fuzzer_pass_adjust_function_controls.cpp +++ b/source/fuzz/fuzzer_pass_adjust_function_controls.cpp @@ -26,8 +26,6 @@ FuzzerPassAdjustFunctionControls::FuzzerPassAdjustFunctionControls( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassAdjustFunctionControls::~FuzzerPassAdjustFunctionControls() = default; - void FuzzerPassAdjustFunctionControls::Apply() { // Consider every function in the module. for (auto& function : *GetIRContext()->module()) { diff --git a/source/fuzz/fuzzer_pass_adjust_function_controls.h b/source/fuzz/fuzzer_pass_adjust_function_controls.h index 5fdbe432..5ef32a18 100644 --- a/source/fuzz/fuzzer_pass_adjust_function_controls.h +++ b/source/fuzz/fuzzer_pass_adjust_function_controls.h @@ -28,8 +28,6 @@ class FuzzerPassAdjustFunctionControls : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassAdjustFunctionControls() override; - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp b/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp index f7addffe..0f7cf037 100644 --- a/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp +++ b/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp @@ -26,8 +26,6 @@ FuzzerPassAdjustLoopControls::FuzzerPassAdjustLoopControls( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassAdjustLoopControls::~FuzzerPassAdjustLoopControls() = default; - void FuzzerPassAdjustLoopControls::Apply() { // Consider every merge instruction in the module (via looking through all // functions and blocks). diff --git a/source/fuzz/fuzzer_pass_adjust_loop_controls.h b/source/fuzz/fuzzer_pass_adjust_loop_controls.h index f133b2d1..4ca670b7 100644 --- a/source/fuzz/fuzzer_pass_adjust_loop_controls.h +++ b/source/fuzz/fuzzer_pass_adjust_loop_controls.h @@ -28,8 +28,6 @@ class FuzzerPassAdjustLoopControls : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassAdjustLoopControls() override; - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp b/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp index 68f0ca7e..778d43ff 100644 --- a/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp +++ b/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp @@ -27,9 +27,6 @@ FuzzerPassAdjustMemoryOperandsMasks::FuzzerPassAdjustMemoryOperandsMasks( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassAdjustMemoryOperandsMasks::~FuzzerPassAdjustMemoryOperandsMasks() = - default; - void FuzzerPassAdjustMemoryOperandsMasks::Apply() { // Consider every block in every function. for (auto& function : *GetIRContext()->module()) { diff --git a/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h b/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h index 699dcb5a..a068b8dd 100644 --- a/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h +++ b/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h @@ -29,8 +29,6 @@ class FuzzerPassAdjustMemoryOperandsMasks : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassAdjustMemoryOperandsMasks(); - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp b/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp index 83b1854e..d9b4e293 100644 --- a/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp +++ b/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp @@ -26,9 +26,6 @@ FuzzerPassAdjustSelectionControls::FuzzerPassAdjustSelectionControls( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassAdjustSelectionControls::~FuzzerPassAdjustSelectionControls() = - default; - void FuzzerPassAdjustSelectionControls::Apply() { // Consider every merge instruction in the module (via looking through all // functions and blocks). diff --git a/source/fuzz/fuzzer_pass_adjust_selection_controls.h b/source/fuzz/fuzzer_pass_adjust_selection_controls.h index 910b40d6..6931f942 100644 --- a/source/fuzz/fuzzer_pass_adjust_selection_controls.h +++ b/source/fuzz/fuzzer_pass_adjust_selection_controls.h @@ -28,8 +28,6 @@ class FuzzerPassAdjustSelectionControls : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassAdjustSelectionControls() override; - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp b/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp index 38553d25..71208313 100644 --- a/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp +++ b/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp @@ -32,8 +32,6 @@ FuzzerPassApplyIdSynonyms::FuzzerPassApplyIdSynonyms( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassApplyIdSynonyms::~FuzzerPassApplyIdSynonyms() = default; - void FuzzerPassApplyIdSynonyms::Apply() { // Compute a closure of data synonym facts, to enrich the pool of synonyms // that are available. diff --git a/source/fuzz/fuzzer_pass_apply_id_synonyms.h b/source/fuzz/fuzzer_pass_apply_id_synonyms.h index 5deac105..b402b509 100644 --- a/source/fuzz/fuzzer_pass_apply_id_synonyms.h +++ b/source/fuzz/fuzzer_pass_apply_id_synonyms.h @@ -30,8 +30,6 @@ class FuzzerPassApplyIdSynonyms : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassApplyIdSynonyms() override; - void Apply() override; private: diff --git a/source/fuzz/fuzzer_pass_construct_composites.cpp b/source/fuzz/fuzzer_pass_construct_composites.cpp index 584fa1a9..1a174cf1 100644 --- a/source/fuzz/fuzzer_pass_construct_composites.cpp +++ b/source/fuzz/fuzzer_pass_construct_composites.cpp @@ -16,6 +16,7 @@ #include <memory> +#include "source/fuzz/available_instructions.h" #include "source/fuzz/fuzzer_util.h" #include "source/fuzz/transformation_composite_construct.h" @@ -29,8 +30,6 @@ FuzzerPassConstructComposites::FuzzerPassConstructComposites( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassConstructComposites::~FuzzerPassConstructComposites() = default; - void FuzzerPassConstructComposites::Apply() { // Gather up the ids of all composite types, but skip block-/buffer // block-decorated struct types. @@ -44,12 +43,40 @@ void FuzzerPassConstructComposites::Apply() { } } + if (composite_type_ids.empty()) { + // There are no composite types, so this fuzzer pass cannot do anything. + return; + } + + AvailableInstructions available_composite_constituents( + GetIRContext(), + [this](opt::IRContext* ir_context, opt::Instruction* inst) -> bool { + if (!inst->result_id() || !inst->type_id()) { + return false; + } + + // If the id is irrelevant, we can use it since it will not + // participate in DataSynonym fact. Otherwise, we should be able + // to produce a synonym out of the id. + return GetTransformationContext()->GetFactManager()->IdIsIrrelevant( + inst->result_id()) || + fuzzerutil::CanMakeSynonymOf(ir_context, + *GetTransformationContext(), inst); + }); + ForEachInstructionWithInstructionDescriptor( - [this, &composite_type_ids]( - opt::Function* function, opt::BasicBlock* block, + [this, &available_composite_constituents, &composite_type_ids]( + opt::Function* /*unused*/, opt::BasicBlock* /*unused*/, opt::BasicBlock::iterator inst_it, const protobufs::InstructionDescriptor& instruction_descriptor) -> void { + // Randomly decide whether to try inserting a composite construction + // here. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfConstructingComposite())) { + return; + } + // Check whether it is legitimate to insert a composite construction // before the instruction. if (!fuzzerutil::CanInsertOpcodeBeforeInstruction( @@ -57,36 +84,21 @@ void FuzzerPassConstructComposites::Apply() { return; } - // Randomly decide whether to try inserting an object copy here. - if (!GetFuzzerContext()->ChoosePercentage( - GetFuzzerContext()->GetChanceOfConstructingComposite())) { - return; - } - // For each instruction that is available at this program point (i.e. an // instruction that is global or whose definition strictly dominates the // program point) and suitable for making a synonym of, associate it // with the id of its result type. TypeIdToInstructions type_id_to_available_instructions; - auto available_instructions = FindAvailableInstructions( - function, block, inst_it, - [this](opt::IRContext* ir_context, opt::Instruction* inst) { - if (!inst->result_id() || !inst->type_id()) { - return false; - } - - // If the id is irrelevant, we can use it since it will not - // participate in DataSynonym fact. Otherwise, we should be able - // to produce a synonym out of the id. - return GetTransformationContext() - ->GetFactManager() - ->IdIsIrrelevant(inst->result_id()) || - fuzzerutil::CanMakeSynonymOf( - ir_context, *GetTransformationContext(), inst); - }); - for (auto instruction : available_instructions) { - RecordAvailableInstruction(instruction, - &type_id_to_available_instructions); + auto available_instructions = + available_composite_constituents.GetAvailableBeforeInstruction( + &*inst_it); + for (uint32_t available_instruction_index = 0; + available_instruction_index < available_instructions.size(); + available_instruction_index++) { + opt::Instruction* inst = + available_instructions[available_instruction_index]; + type_id_to_available_instructions[inst->type_id()].push_back( + inst->result_id()); } // At this point, |composite_type_ids| captures all the composite types @@ -94,68 +106,41 @@ void FuzzerPassConstructComposites::Apply() { // captures all the available result ids we might use, organized by // type. - // Now we try to find a composite that we can construct. We might not - // manage, if there is a paucity of available ingredients in the module - // (e.g. if our only available composite was a boolean vector and we had - // no instructions generating boolean result types available). - // - // If we succeed, |chosen_composite_type| will end up being non-zero, - // and |constructor_arguments| will end up giving us result ids suitable - // for constructing a composite of that type. Otherwise these variables - // will remain 0 and null respectively. - uint32_t chosen_composite_type = 0; - std::vector<uint32_t> constructor_arguments; + // Now we choose a composite type to construct, building it from + // available constituent components and using zero constants if suitable + // components are not available. - // Initially, all composite type ids are available for us to try. Keep - // trying until we run out of options. - auto composites_to_try_constructing = composite_type_ids; - while (!composites_to_try_constructing.empty()) { - // Remove a composite type from the composite types left for us to - // try. - auto next_composite_to_try_constructing = - GetFuzzerContext()->RemoveAtRandomIndex( - &composites_to_try_constructing); - - // Now try to construct a composite of this type, using an appropriate - // helper method depending on the kind of composite type. - auto composite_type_inst = GetIRContext()->get_def_use_mgr()->GetDef( - next_composite_to_try_constructing); - switch (composite_type_inst->opcode()) { - case SpvOpTypeArray: - constructor_arguments = FindComponentsToConstructArray( - *composite_type_inst, type_id_to_available_instructions); - break; - case SpvOpTypeMatrix: - constructor_arguments = FindComponentsToConstructMatrix( - *composite_type_inst, type_id_to_available_instructions); - break; - case SpvOpTypeStruct: - constructor_arguments = FindComponentsToConstructStruct( - *composite_type_inst, type_id_to_available_instructions); - break; - case SpvOpTypeVector: - constructor_arguments = FindComponentsToConstructVector( - *composite_type_inst, type_id_to_available_instructions); - break; - default: - assert(false && - "The space of possible composite types should be covered " - "by the above cases."); - break; - } - if (!constructor_arguments.empty()) { - // We succeeded! Note the composite type we finally settled on, and - // exit from the loop. - chosen_composite_type = next_composite_to_try_constructing; + std::vector<uint32_t> constructor_arguments; + uint32_t chosen_composite_type = + composite_type_ids[GetFuzzerContext()->RandomIndex( + composite_type_ids)]; + + // Construct a composite of this type, using an appropriate helper + // method depending on the kind of composite type. + auto composite_type_inst = + GetIRContext()->get_def_use_mgr()->GetDef(chosen_composite_type); + switch (composite_type_inst->opcode()) { + case SpvOpTypeArray: + constructor_arguments = FindComponentsToConstructArray( + *composite_type_inst, type_id_to_available_instructions); + break; + case SpvOpTypeMatrix: + constructor_arguments = FindComponentsToConstructMatrix( + *composite_type_inst, type_id_to_available_instructions); + break; + case SpvOpTypeStruct: + constructor_arguments = FindComponentsToConstructStruct( + *composite_type_inst, type_id_to_available_instructions); + break; + case SpvOpTypeVector: + constructor_arguments = FindComponentsToConstructVector( + *composite_type_inst, type_id_to_available_instructions); + break; + default: + assert(false && + "The space of possible composite types should be covered " + "by the above cases."); break; - } - } - - if (!chosen_composite_type) { - // We did not manage to make a composite; return 0 to indicate that no - // instructions were added. - assert(constructor_arguments.empty()); - return; } assert(!constructor_arguments.empty()); @@ -166,15 +151,6 @@ void FuzzerPassConstructComposites::Apply() { }); } -void FuzzerPassConstructComposites::RecordAvailableInstruction( - opt::Instruction* inst, - TypeIdToInstructions* type_id_to_available_instructions) { - if (type_id_to_available_instructions->count(inst->type_id()) == 0) { - (*type_id_to_available_instructions)[inst->type_id()] = {}; - } - type_id_to_available_instructions->at(inst->type_id()).push_back(inst); -} - std::vector<uint32_t> FuzzerPassConstructComposites::FindComponentsToConstructArray( const opt::Instruction& array_type_instruction, @@ -190,13 +166,6 @@ FuzzerPassConstructComposites::FindComponentsToConstructArray( auto available_instructions = type_id_to_available_instructions.find(element_type_id); - if (available_instructions == type_id_to_available_instructions.cend()) { - // If there are not any instructions available that compute the element type - // of the array then we are not in a position to construct a composite with - // this array type. - return {}; - } - uint32_t array_length = GetIRContext() ->get_def_use_mgr() @@ -205,10 +174,14 @@ FuzzerPassConstructComposites::FindComponentsToConstructArray( std::vector<uint32_t> result; for (uint32_t index = 0; index < array_length; index++) { - result.push_back(available_instructions - ->second[GetFuzzerContext()->RandomIndex( - available_instructions->second)] - ->result_id()); + if (available_instructions == type_id_to_available_instructions.cend()) { + // No suitable instructions are available, so use a zero constant + result.push_back(FindOrCreateZeroConstant(element_type_id, true)); + } else { + result.push_back( + available_instructions->second[GetFuzzerContext()->RandomIndex( + available_instructions->second)]); + } } return result; } @@ -228,19 +201,17 @@ FuzzerPassConstructComposites::FindComponentsToConstructMatrix( auto available_instructions = type_id_to_available_instructions.find(element_type_id); - if (available_instructions == type_id_to_available_instructions.cend()) { - // If there are not any instructions available that compute the element type - // of the matrix then we are not in a position to construct a composite with - // this matrix type. - return {}; - } std::vector<uint32_t> result; for (uint32_t index = 0; index < matrix_type_instruction.GetSingleWordInOperand(1); index++) { - result.push_back(available_instructions - ->second[GetFuzzerContext()->RandomIndex( - available_instructions->second)] - ->result_id()); + if (available_instructions == type_id_to_available_instructions.cend()) { + // No suitable components are available, so use a zero constant. + result.push_back(FindOrCreateZeroConstant(element_type_id, true)); + } else { + result.push_back( + available_instructions->second[GetFuzzerContext()->RandomIndex( + available_instructions->second)]); + } } return result; } @@ -263,14 +234,14 @@ FuzzerPassConstructComposites::FindComponentsToConstructStruct( auto available_instructions = type_id_to_available_instructions.find(element_type_id); if (available_instructions == type_id_to_available_instructions.cend()) { - // If there are no such instructions, we cannot construct a composite of - // this struct type. - return {}; + // No suitable component is available for this element type, so use a zero + // constant. + result.push_back(FindOrCreateZeroConstant(element_type_id, true)); + } else { + result.push_back( + available_instructions->second[GetFuzzerContext()->RandomIndex( + available_instructions->second)]); } - result.push_back(available_instructions - ->second[GetFuzzerContext()->RandomIndex( - available_instructions->second)] - ->result_id()); } return result; } @@ -325,12 +296,13 @@ FuzzerPassConstructComposites::FindComponentsToConstructVector( // (otherwise there will not be space left for a vec3). uint32_t vector_slots_used = 0; - // The instructions we will use to construct the vector, in no particular - // order at this stage. - std::vector<opt::Instruction*> instructions_to_use; + + // The instructions result ids we will use to construct the vector, in no + // particular order at this stage. + std::vector<uint32_t> result; while (vector_slots_used < element_count) { - std::vector<opt::Instruction*> instructions_to_choose_from; + std::vector<uint32_t> instructions_to_choose_from; for (auto& entry : smaller_vector_type_id_to_width) { if (entry.second > std::min(element_count - 1, element_count - vector_slots_used)) { @@ -345,19 +317,16 @@ FuzzerPassConstructComposites::FindComponentsToConstructVector( available_instructions->second.begin(), available_instructions->second.end()); } - if (instructions_to_choose_from.empty()) { - // We may get unlucky and find that there are not any instructions to - // choose from. In this case we give up constructing a composite of this - // vector type. It might be that we could construct the composite in - // another manner, so we could opt to retry a few times here, but it is - // simpler to just give up on the basis that this will not happen - // frequently. - return {}; - } - auto instruction_to_use = - instructions_to_choose_from[GetFuzzerContext()->RandomIndex( - instructions_to_choose_from)]; - instructions_to_use.push_back(instruction_to_use); + // If there are no instructions to choose from then use a zero constant, + // otherwise select one of the instructions at random. + uint32_t id_of_instruction_to_use = + instructions_to_choose_from.empty() + ? FindOrCreateZeroConstant(element_type_id, true) + : instructions_to_choose_from[GetFuzzerContext()->RandomIndex( + instructions_to_choose_from)]; + opt::Instruction* instruction_to_use = + GetIRContext()->get_def_use_mgr()->GetDef(id_of_instruction_to_use); + result.push_back(instruction_to_use->result_id()); auto chosen_type = GetIRContext()->get_type_mgr()->GetType(instruction_to_use->type_id()); if (chosen_type->AsVector()) { @@ -373,14 +342,7 @@ FuzzerPassConstructComposites::FindComponentsToConstructVector( } assert(vector_slots_used == element_count); - std::vector<uint32_t> result; - std::vector<uint32_t> operands; - while (!instructions_to_use.empty()) { - auto index = GetFuzzerContext()->RandomIndex(instructions_to_use); - result.push_back(instructions_to_use[index]->result_id()); - instructions_to_use.erase(instructions_to_use.begin() + index); - } - assert(result.size() > 1); + GetFuzzerContext()->Shuffle(&result); return result; } diff --git a/source/fuzz/fuzzer_pass_construct_composites.h b/source/fuzz/fuzzer_pass_construct_composites.h index c140bded..333ac931 100644 --- a/source/fuzz/fuzzer_pass_construct_composites.h +++ b/source/fuzz/fuzzer_pass_construct_composites.h @@ -15,7 +15,7 @@ #ifndef SOURCE_FUZZ_FUZZER_PASS_CONSTRUCT_COMPOSITES_H_ #define SOURCE_FUZZ_FUZZER_PASS_CONSTRUCT_COMPOSITES_H_ -#include <map> +#include <unordered_map> #include <vector> #include "source/fuzz/fuzzer_pass.h" @@ -31,23 +31,12 @@ class FuzzerPassConstructComposites : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassConstructComposites(); - void Apply() override; private: - // Used to map a type id to relevant instructions whose result type matches - // the type id. - typedef std::map<uint32_t, std::vector<opt::Instruction*>> - TypeIdToInstructions; - - // Considers all instructions that are available at |inst| - instructions - // whose results could be packed into a composite - and updates - // |type_id_to_available_instructions| so that each such instruction is - // associated with its the id of its result type. - void RecordAvailableInstruction( - opt::Instruction* inst, - TypeIdToInstructions* type_id_to_available_instructions); + // Used to map a type id to the ids of relevant instructions of the type. + using TypeIdToInstructions = + std::unordered_map<uint32_t, std::vector<uint32_t>>; // Requires that |array_type_instruction| has opcode OpTypeArray. // Attempts to find suitable instruction result ids from the values of diff --git a/source/fuzz/fuzzer_pass_copy_objects.cpp b/source/fuzz/fuzzer_pass_copy_objects.cpp index 9f7bbd63..09b5368a 100644 --- a/source/fuzz/fuzzer_pass_copy_objects.cpp +++ b/source/fuzz/fuzzer_pass_copy_objects.cpp @@ -28,8 +28,6 @@ FuzzerPassCopyObjects::FuzzerPassCopyObjects( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassCopyObjects::~FuzzerPassCopyObjects() = default; - void FuzzerPassCopyObjects::Apply() { ForEachInstructionWithInstructionDescriptor( [this](opt::Function* function, opt::BasicBlock* block, diff --git a/source/fuzz/fuzzer_pass_copy_objects.h b/source/fuzz/fuzzer_pass_copy_objects.h index 8de382e7..461cd979 100644 --- a/source/fuzz/fuzzer_pass_copy_objects.h +++ b/source/fuzz/fuzzer_pass_copy_objects.h @@ -28,8 +28,6 @@ class FuzzerPassCopyObjects : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassCopyObjects(); - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_donate_modules.cpp b/source/fuzz/fuzzer_pass_donate_modules.cpp index 4cd48045..2f2ed50a 100644 --- a/source/fuzz/fuzzer_pass_donate_modules.cpp +++ b/source/fuzz/fuzzer_pass_donate_modules.cpp @@ -50,8 +50,6 @@ FuzzerPassDonateModules::FuzzerPassDonateModules( transformations), donor_suppliers_(donor_suppliers) {} -FuzzerPassDonateModules::~FuzzerPassDonateModules() = default; - void FuzzerPassDonateModules::Apply() { // If there are no donor suppliers, this fuzzer pass is a no-op. if (donor_suppliers_.empty()) { @@ -1202,11 +1200,14 @@ bool FuzzerPassDonateModules::MaybeAddLivesafeFunction( false); } - // Add the function in a livesafe manner. - ApplyTransformation(TransformationAddFunction( + // Try to add the function in a livesafe manner. This may fail due to edge + // cases, e.g. where adding loop limiters changes dominance such that the + // module becomes invalid. It would be ideal to handle all such edge cases, + // but as they are rare it is more pragmatic to bail out of making the + // function livesafe if the transformation's precondition fails to hold. + return MaybeApplyTransformation(TransformationAddFunction( donated_instructions, loop_limiter_variable_id, loop_limit, loop_limiters, kill_unreachable_return_value_id, access_chain_clamping_info)); - return true; } } // namespace fuzz diff --git a/source/fuzz/fuzzer_pass_donate_modules.h b/source/fuzz/fuzzer_pass_donate_modules.h index 0424cece..1581a8a4 100644 --- a/source/fuzz/fuzzer_pass_donate_modules.h +++ b/source/fuzz/fuzzer_pass_donate_modules.h @@ -33,8 +33,6 @@ class FuzzerPassDonateModules : public FuzzerPass { protobufs::TransformationSequence* transformations, const std::vector<fuzzerutil::ModuleSupplier>& donor_suppliers); - ~FuzzerPassDonateModules(); - void Apply() override; // Donates the global declarations and functions of |donor_ir_context| into diff --git a/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.cpp b/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.cpp index 1651a9d5..e08d65b1 100644 --- a/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.cpp +++ b/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.cpp @@ -29,9 +29,6 @@ FuzzerPassDuplicateRegionsWithSelections:: : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassDuplicateRegionsWithSelections:: - ~FuzzerPassDuplicateRegionsWithSelections() = default; - void FuzzerPassDuplicateRegionsWithSelections::Apply() { // Iterate over all of the functions in the module. for (auto& function : *GetIRContext()->module()) { diff --git a/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h b/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h index 3fae6983..7cb1197d 100644 --- a/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h +++ b/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h @@ -31,8 +31,6 @@ class FuzzerPassDuplicateRegionsWithSelections : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassDuplicateRegionsWithSelections() override; - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_expand_vector_reductions.cpp b/source/fuzz/fuzzer_pass_expand_vector_reductions.cpp index 1416fe0b..e25dcbc3 100644 --- a/source/fuzz/fuzzer_pass_expand_vector_reductions.cpp +++ b/source/fuzz/fuzzer_pass_expand_vector_reductions.cpp @@ -28,8 +28,6 @@ FuzzerPassExpandVectorReductions::FuzzerPassExpandVectorReductions( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassExpandVectorReductions::~FuzzerPassExpandVectorReductions() = default; - void FuzzerPassExpandVectorReductions::Apply() { for (auto& function : *GetIRContext()->module()) { for (auto& block : function) { diff --git a/source/fuzz/fuzzer_pass_expand_vector_reductions.h b/source/fuzz/fuzzer_pass_expand_vector_reductions.h index ae3238b6..ed0225df 100644 --- a/source/fuzz/fuzzer_pass_expand_vector_reductions.h +++ b/source/fuzz/fuzzer_pass_expand_vector_reductions.h @@ -30,8 +30,6 @@ class FuzzerPassExpandVectorReductions : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassExpandVectorReductions(); - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp b/source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp index 1e21aa50..84da7a74 100644 --- a/source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp +++ b/source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp @@ -30,9 +30,6 @@ FuzzerPassFlattenConditionalBranches::FuzzerPassFlattenConditionalBranches( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassFlattenConditionalBranches::~FuzzerPassFlattenConditionalBranches() = - default; - void FuzzerPassFlattenConditionalBranches::Apply() { for (auto& function : *GetIRContext()->module()) { // Get all the selection headers that we want to flatten. We need to collect diff --git a/source/fuzz/fuzzer_pass_flatten_conditional_branches.h b/source/fuzz/fuzzer_pass_flatten_conditional_branches.h index 76f7782c..e7d7dc36 100644 --- a/source/fuzz/fuzzer_pass_flatten_conditional_branches.h +++ b/source/fuzz/fuzzer_pass_flatten_conditional_branches.h @@ -27,8 +27,6 @@ class FuzzerPassFlattenConditionalBranches : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassFlattenConditionalBranches() override; - void Apply() override; private: diff --git a/source/fuzz/fuzzer_pass_inline_functions.cpp b/source/fuzz/fuzzer_pass_inline_functions.cpp index 90160d83..405afd8b 100644 --- a/source/fuzz/fuzzer_pass_inline_functions.cpp +++ b/source/fuzz/fuzzer_pass_inline_functions.cpp @@ -29,8 +29,6 @@ FuzzerPassInlineFunctions::FuzzerPassInlineFunctions( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassInlineFunctions::~FuzzerPassInlineFunctions() = default; - void FuzzerPassInlineFunctions::Apply() { // |function_call_instructions| are the instructions that will be inlined. // First, they will be collected and then do the inlining in another loop. diff --git a/source/fuzz/fuzzer_pass_inline_functions.h b/source/fuzz/fuzzer_pass_inline_functions.h index 37295d1c..a6ed8cad 100644 --- a/source/fuzz/fuzzer_pass_inline_functions.h +++ b/source/fuzz/fuzzer_pass_inline_functions.h @@ -30,8 +30,6 @@ class FuzzerPassInlineFunctions : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassInlineFunctions() override; - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp b/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp index 0e40b496..675cae9a 100644 --- a/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp +++ b/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp @@ -31,10 +31,10 @@ FuzzerPassInterchangeSignednessOfIntegerOperands:: : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassInterchangeSignednessOfIntegerOperands:: - ~FuzzerPassInterchangeSignednessOfIntegerOperands() = default; - void FuzzerPassInterchangeSignednessOfIntegerOperands::Apply() { + assert(!GetFuzzerContext()->IsWgslCompatible() && + "Cannot interchange signedness in WGSL"); + // Make vector keeping track of all the uses we want to replace. // This is a vector of pairs, where the first element is an id use descriptor // identifying the use of a constant id and the second is the id that should diff --git a/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h b/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h index 06882f47..11b8fb6e 100644 --- a/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h +++ b/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h @@ -34,8 +34,6 @@ class FuzzerPassInterchangeSignednessOfIntegerOperands : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassInterchangeSignednessOfIntegerOperands() override; - void Apply() override; private: diff --git a/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp b/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp index 20575e11..5d0a2223 100644 --- a/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp +++ b/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp @@ -29,9 +29,6 @@ FuzzerPassInterchangeZeroLikeConstants::FuzzerPassInterchangeZeroLikeConstants( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassInterchangeZeroLikeConstants:: - ~FuzzerPassInterchangeZeroLikeConstants() = default; - uint32_t FuzzerPassInterchangeZeroLikeConstants::FindOrCreateToggledConstant( opt::Instruction* declaration) { // |declaration| must not be a specialization constant because we do not know diff --git a/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h b/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h index ef0f7655..012f03d3 100644 --- a/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h +++ b/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h @@ -35,8 +35,6 @@ class FuzzerPassInterchangeZeroLikeConstants : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassInterchangeZeroLikeConstants() override; - void Apply() override; private: diff --git a/source/fuzz/fuzzer_pass_invert_comparison_operators.cpp b/source/fuzz/fuzzer_pass_invert_comparison_operators.cpp index de4ff1de..542748e9 100644 --- a/source/fuzz/fuzzer_pass_invert_comparison_operators.cpp +++ b/source/fuzz/fuzzer_pass_invert_comparison_operators.cpp @@ -28,9 +28,6 @@ FuzzerPassInvertComparisonOperators::FuzzerPassInvertComparisonOperators( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassInvertComparisonOperators::~FuzzerPassInvertComparisonOperators() = - default; - void FuzzerPassInvertComparisonOperators::Apply() { GetIRContext()->module()->ForEachInst([this](const opt::Instruction* inst) { if (!TransformationInvertComparisonOperator::IsInversionSupported( diff --git a/source/fuzz/fuzzer_pass_invert_comparison_operators.h b/source/fuzz/fuzzer_pass_invert_comparison_operators.h index 9c80bbbb..5f015c28 100644 --- a/source/fuzz/fuzzer_pass_invert_comparison_operators.h +++ b/source/fuzz/fuzzer_pass_invert_comparison_operators.h @@ -29,8 +29,6 @@ class FuzzerPassInvertComparisonOperators : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassInvertComparisonOperators() override; - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp b/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp index f4f2a802..7bf07a49 100644 --- a/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp +++ b/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp @@ -28,9 +28,6 @@ FuzzerPassMakeVectorOperationsDynamic::FuzzerPassMakeVectorOperationsDynamic( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassMakeVectorOperationsDynamic:: - ~FuzzerPassMakeVectorOperationsDynamic() = default; - void FuzzerPassMakeVectorOperationsDynamic::Apply() { for (auto& function : *GetIRContext()->module()) { for (auto& block : function) { diff --git a/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h b/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h index dd51cde7..da27825a 100644 --- a/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h +++ b/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h @@ -29,8 +29,6 @@ class FuzzerPassMakeVectorOperationsDynamic : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassMakeVectorOperationsDynamic() override; - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_merge_blocks.cpp b/source/fuzz/fuzzer_pass_merge_blocks.cpp index e66fc444..a8ec784c 100644 --- a/source/fuzz/fuzzer_pass_merge_blocks.cpp +++ b/source/fuzz/fuzzer_pass_merge_blocks.cpp @@ -28,8 +28,6 @@ FuzzerPassMergeBlocks::FuzzerPassMergeBlocks( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassMergeBlocks::~FuzzerPassMergeBlocks() = default; - void FuzzerPassMergeBlocks::Apply() { // First we populate a sequence of transformations that we might consider // applying. diff --git a/source/fuzz/fuzzer_pass_merge_blocks.h b/source/fuzz/fuzzer_pass_merge_blocks.h index 1a6c2c27..66cf4c64 100644 --- a/source/fuzz/fuzzer_pass_merge_blocks.h +++ b/source/fuzz/fuzzer_pass_merge_blocks.h @@ -28,8 +28,6 @@ class FuzzerPassMergeBlocks : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassMergeBlocks(); - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_merge_function_returns.cpp b/source/fuzz/fuzzer_pass_merge_function_returns.cpp index fc9c74d0..ee82eca5 100644 --- a/source/fuzz/fuzzer_pass_merge_function_returns.cpp +++ b/source/fuzz/fuzzer_pass_merge_function_returns.cpp @@ -30,8 +30,6 @@ FuzzerPassMergeFunctionReturns::FuzzerPassMergeFunctionReturns( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassMergeFunctionReturns::~FuzzerPassMergeFunctionReturns() = default; - void FuzzerPassMergeFunctionReturns::Apply() { // The pass might add new functions to the module (due to wrapping early // terminator instructions in function calls), so we record the functions that @@ -174,8 +172,9 @@ void FuzzerPassMergeFunctionReturns::Apply() { } // Get the ids needed by the transformation. - uint32_t outer_header_id = GetFuzzerContext()->GetFreshId(); - uint32_t outer_return_id = GetFuzzerContext()->GetFreshId(); + const uint32_t outer_header_id = GetFuzzerContext()->GetFreshId(); + const uint32_t unreachable_continue_id = GetFuzzerContext()->GetFreshId(); + const uint32_t outer_return_id = GetFuzzerContext()->GetFreshId(); bool function_is_void = GetIRContext()->get_type_mgr()->GetType(function->type_id())->AsVoid(); @@ -211,8 +210,8 @@ void FuzzerPassMergeFunctionReturns::Apply() { // Apply the transformation if it is applicable (it could be inapplicable if // adding new predecessors to merge blocks breaks dominance rules). MaybeApplyTransformation(TransformationMergeFunctionReturns( - function->result_id(), outer_header_id, outer_return_id, return_val_id, - returnable_val_id, merge_blocks_info)); + function->result_id(), outer_header_id, unreachable_continue_id, + outer_return_id, return_val_id, returnable_val_id, merge_blocks_info)); } } diff --git a/source/fuzz/fuzzer_pass_merge_function_returns.h b/source/fuzz/fuzzer_pass_merge_function_returns.h index 3b5a668f..ddd2c9dc 100644 --- a/source/fuzz/fuzzer_pass_merge_function_returns.h +++ b/source/fuzz/fuzzer_pass_merge_function_returns.h @@ -33,8 +33,6 @@ class FuzzerPassMergeFunctionReturns : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassMergeFunctionReturns(); - void Apply() override; private: diff --git a/source/fuzz/fuzzer_pass_mutate_pointers.cpp b/source/fuzz/fuzzer_pass_mutate_pointers.cpp index 89f5f5c0..f021a978 100644 --- a/source/fuzz/fuzzer_pass_mutate_pointers.cpp +++ b/source/fuzz/fuzzer_pass_mutate_pointers.cpp @@ -28,8 +28,6 @@ FuzzerPassMutatePointers::FuzzerPassMutatePointers( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassMutatePointers::~FuzzerPassMutatePointers() = default; - void FuzzerPassMutatePointers::Apply() { ForEachInstructionWithInstructionDescriptor( [this](opt::Function* function, opt::BasicBlock* block, diff --git a/source/fuzz/fuzzer_pass_mutate_pointers.h b/source/fuzz/fuzzer_pass_mutate_pointers.h index f77523ef..5ef6a2ab 100644 --- a/source/fuzz/fuzzer_pass_mutate_pointers.h +++ b/source/fuzz/fuzzer_pass_mutate_pointers.h @@ -28,8 +28,6 @@ class FuzzerPassMutatePointers : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassMutatePointers() override; - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_obfuscate_constants.cpp b/source/fuzz/fuzzer_pass_obfuscate_constants.cpp index d87662ee..32318e89 100644 --- a/source/fuzz/fuzzer_pass_obfuscate_constants.cpp +++ b/source/fuzz/fuzzer_pass_obfuscate_constants.cpp @@ -34,8 +34,6 @@ FuzzerPassObfuscateConstants::FuzzerPassObfuscateConstants( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassObfuscateConstants::~FuzzerPassObfuscateConstants() = default; - void FuzzerPassObfuscateConstants::ObfuscateBoolConstantViaConstantPair( uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use, const std::vector<SpvOp>& greater_than_opcodes, diff --git a/source/fuzz/fuzzer_pass_obfuscate_constants.h b/source/fuzz/fuzzer_pass_obfuscate_constants.h index d48b37f6..82b1092c 100644 --- a/source/fuzz/fuzzer_pass_obfuscate_constants.h +++ b/source/fuzz/fuzzer_pass_obfuscate_constants.h @@ -32,8 +32,6 @@ class FuzzerPassObfuscateConstants : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassObfuscateConstants() override; - void Apply() override; private: diff --git a/source/fuzz/fuzzer_pass_outline_functions.cpp b/source/fuzz/fuzzer_pass_outline_functions.cpp index 42101256..bfde61f7 100644 --- a/source/fuzz/fuzzer_pass_outline_functions.cpp +++ b/source/fuzz/fuzzer_pass_outline_functions.cpp @@ -31,8 +31,6 @@ FuzzerPassOutlineFunctions::FuzzerPassOutlineFunctions( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassOutlineFunctions::~FuzzerPassOutlineFunctions() = default; - void FuzzerPassOutlineFunctions::Apply() { std::vector<opt::Function*> original_functions; for (auto& function : *GetIRContext()->module()) { diff --git a/source/fuzz/fuzzer_pass_outline_functions.h b/source/fuzz/fuzzer_pass_outline_functions.h index 02022aa7..45e52ff4 100644 --- a/source/fuzz/fuzzer_pass_outline_functions.h +++ b/source/fuzz/fuzzer_pass_outline_functions.h @@ -29,8 +29,6 @@ class FuzzerPassOutlineFunctions : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassOutlineFunctions(); - void Apply() override; // Returns a block suitable to be an entry block for a region that can be diff --git a/source/fuzz/fuzzer_pass_permute_blocks.cpp b/source/fuzz/fuzzer_pass_permute_blocks.cpp index 24c16fbb..769c49f0 100644 --- a/source/fuzz/fuzzer_pass_permute_blocks.cpp +++ b/source/fuzz/fuzzer_pass_permute_blocks.cpp @@ -26,8 +26,6 @@ FuzzerPassPermuteBlocks::FuzzerPassPermuteBlocks( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassPermuteBlocks::~FuzzerPassPermuteBlocks() = default; - void FuzzerPassPermuteBlocks::Apply() { // For now we do something very simple: we randomly decide whether to move a // block, and for each block that we do move, we push it down as far as we diff --git a/source/fuzz/fuzzer_pass_permute_blocks.h b/source/fuzz/fuzzer_pass_permute_blocks.h index e5a672cb..e40178e6 100644 --- a/source/fuzz/fuzzer_pass_permute_blocks.h +++ b/source/fuzz/fuzzer_pass_permute_blocks.h @@ -29,8 +29,6 @@ class FuzzerPassPermuteBlocks : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassPermuteBlocks() override; - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_permute_function_parameters.cpp b/source/fuzz/fuzzer_pass_permute_function_parameters.cpp index de6b03f3..9a61bea0 100644 --- a/source/fuzz/fuzzer_pass_permute_function_parameters.cpp +++ b/source/fuzz/fuzzer_pass_permute_function_parameters.cpp @@ -32,9 +32,6 @@ FuzzerPassPermuteFunctionParameters::FuzzerPassPermuteFunctionParameters( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassPermuteFunctionParameters::~FuzzerPassPermuteFunctionParameters() = - default; - void FuzzerPassPermuteFunctionParameters::Apply() { for (const auto& function : *GetIRContext()->module()) { uint32_t function_id = function.result_id(); diff --git a/source/fuzz/fuzzer_pass_permute_function_parameters.h b/source/fuzz/fuzzer_pass_permute_function_parameters.h index bc1031cb..a4bf2ca5 100644 --- a/source/fuzz/fuzzer_pass_permute_function_parameters.h +++ b/source/fuzz/fuzzer_pass_permute_function_parameters.h @@ -34,8 +34,6 @@ class FuzzerPassPermuteFunctionParameters : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassPermuteFunctionParameters() override; - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_permute_function_variables.cpp b/source/fuzz/fuzzer_pass_permute_function_variables.cpp new file mode 100644 index 00000000..a4e19e3b --- /dev/null +++ b/source/fuzz/fuzzer_pass_permute_function_variables.cpp @@ -0,0 +1,73 @@ +// Copyright (c) 2021 Mostafa Ashraf +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_permute_function_variables.h" + +#include <algorithm> +#include <numeric> +#include <vector> + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_swap_function_variables.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassPermuteFunctionVariables::FuzzerPassPermuteFunctionVariables( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} // Here we call parent constructor. + +void FuzzerPassPermuteFunctionVariables::Apply() { + // Permuting OpVariable instructions in each function. + for (auto& function : *GetIRContext()->module()) { + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfPermutingFunctionVariables())) { + continue; + } + + auto first_block = function.entry().get(); + + std::vector<opt::Instruction*> variables; + for (auto& instruction : *first_block) { + if (instruction.opcode() == SpvOpVariable) { + variables.push_back(&instruction); + } + } + if (variables.size() <= 1) { + continue; + } + do { + uint32_t instruction_1_index = GetFuzzerContext()->RandomIndex(variables); + uint32_t instruction_2_index = GetFuzzerContext()->RandomIndex(variables); + + if (instruction_1_index != instruction_2_index) { + ApplyTransformation(TransformationSwapFunctionVariables( + variables[instruction_1_index]->result_id(), + variables[instruction_2_index]->result_id())); + } + + } while (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfSwappingAnotherPairOfFunctionVariables()) && + variables.size() > 2); + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/fuzzer_pass_permute_function_variables.h b/source/fuzz/fuzzer_pass_permute_function_variables.h new file mode 100644 index 00000000..47f1de28 --- /dev/null +++ b/source/fuzz/fuzzer_pass_permute_function_variables.h @@ -0,0 +1,37 @@ +// Copyright (c) 2021 Mostafa Ashraf +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_PERMUTE_FUNCTION_VARIABLES_H_ +#define SOURCE_FUZZ_FUZZER_PASS_PERMUTE_FUNCTION_VARIABLES_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// This fuzzer pass permutes variables in functions in the module. +class FuzzerPassPermuteFunctionVariables : public FuzzerPass { + public: + FuzzerPassPermuteFunctionVariables( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_PERMUTE_FUNCTION_VARIABLES_H_ diff --git a/source/fuzz/fuzzer_pass_permute_instructions.cpp b/source/fuzz/fuzzer_pass_permute_instructions.cpp index 6867053c..f17e0187 100644 --- a/source/fuzz/fuzzer_pass_permute_instructions.cpp +++ b/source/fuzz/fuzzer_pass_permute_instructions.cpp @@ -29,8 +29,6 @@ FuzzerPassPermuteInstructions::FuzzerPassPermuteInstructions( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassPermuteInstructions::~FuzzerPassPermuteInstructions() = default; - void FuzzerPassPermuteInstructions::Apply() { // We are iterating over all instructions in all basic blocks. for (auto& function : *GetIRContext()->module()) { diff --git a/source/fuzz/fuzzer_pass_permute_instructions.h b/source/fuzz/fuzzer_pass_permute_instructions.h index e02ddfae..027101dd 100644 --- a/source/fuzz/fuzzer_pass_permute_instructions.h +++ b/source/fuzz/fuzzer_pass_permute_instructions.h @@ -29,8 +29,6 @@ class FuzzerPassPermuteInstructions : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassPermuteInstructions() override; - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_permute_phi_operands.cpp b/source/fuzz/fuzzer_pass_permute_phi_operands.cpp index c379c535..f2cc5231 100644 --- a/source/fuzz/fuzzer_pass_permute_phi_operands.cpp +++ b/source/fuzz/fuzzer_pass_permute_phi_operands.cpp @@ -32,8 +32,6 @@ FuzzerPassPermutePhiOperands::FuzzerPassPermutePhiOperands( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassPermutePhiOperands::~FuzzerPassPermutePhiOperands() = default; - void FuzzerPassPermutePhiOperands::Apply() { ForEachInstructionWithInstructionDescriptor( [this](opt::Function* /*unused*/, opt::BasicBlock* /*unused*/, diff --git a/source/fuzz/fuzzer_pass_permute_phi_operands.h b/source/fuzz/fuzzer_pass_permute_phi_operands.h index 974c2c1e..79999562 100644 --- a/source/fuzz/fuzzer_pass_permute_phi_operands.h +++ b/source/fuzz/fuzzer_pass_permute_phi_operands.h @@ -29,8 +29,6 @@ class FuzzerPassPermutePhiOperands : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassPermutePhiOperands() override; - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_propagate_instructions_down.cpp b/source/fuzz/fuzzer_pass_propagate_instructions_down.cpp index 7a115aec..af27a5da 100644 --- a/source/fuzz/fuzzer_pass_propagate_instructions_down.cpp +++ b/source/fuzz/fuzzer_pass_propagate_instructions_down.cpp @@ -27,9 +27,6 @@ FuzzerPassPropagateInstructionsDown::FuzzerPassPropagateInstructionsDown( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassPropagateInstructionsDown::~FuzzerPassPropagateInstructionsDown() = - default; - void FuzzerPassPropagateInstructionsDown::Apply() { for (const auto& function : *GetIRContext()->module()) { std::vector<const opt::BasicBlock*> reachable_blocks; diff --git a/source/fuzz/fuzzer_pass_propagate_instructions_down.h b/source/fuzz/fuzzer_pass_propagate_instructions_down.h index 536bf000..a2a0aac8 100644 --- a/source/fuzz/fuzzer_pass_propagate_instructions_down.h +++ b/source/fuzz/fuzzer_pass_propagate_instructions_down.h @@ -28,8 +28,6 @@ class FuzzerPassPropagateInstructionsDown : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassPropagateInstructionsDown() override; - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_propagate_instructions_up.cpp b/source/fuzz/fuzzer_pass_propagate_instructions_up.cpp index 16ec680e..8cd7437b 100644 --- a/source/fuzz/fuzzer_pass_propagate_instructions_up.cpp +++ b/source/fuzz/fuzzer_pass_propagate_instructions_up.cpp @@ -29,9 +29,6 @@ FuzzerPassPropagateInstructionsUp::FuzzerPassPropagateInstructionsUp( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassPropagateInstructionsUp::~FuzzerPassPropagateInstructionsUp() = - default; - void FuzzerPassPropagateInstructionsUp::Apply() { for (const auto& function : *GetIRContext()->module()) { for (const auto& block : function) { diff --git a/source/fuzz/fuzzer_pass_propagate_instructions_up.h b/source/fuzz/fuzzer_pass_propagate_instructions_up.h index d915b31e..b89be48d 100644 --- a/source/fuzz/fuzzer_pass_propagate_instructions_up.h +++ b/source/fuzz/fuzzer_pass_propagate_instructions_up.h @@ -29,8 +29,6 @@ class FuzzerPassPropagateInstructionsUp : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassPropagateInstructionsUp() override; - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp b/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp index 8d9acaa0..54e589c9 100644 --- a/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp +++ b/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp @@ -28,9 +28,6 @@ FuzzerPassPushIdsThroughVariables::FuzzerPassPushIdsThroughVariables( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassPushIdsThroughVariables::~FuzzerPassPushIdsThroughVariables() = - default; - void FuzzerPassPushIdsThroughVariables::Apply() { ForEachInstructionWithInstructionDescriptor( [this](opt::Function* function, opt::BasicBlock* block, diff --git a/source/fuzz/fuzzer_pass_push_ids_through_variables.h b/source/fuzz/fuzzer_pass_push_ids_through_variables.h index 3ad54042..53008ee2 100644 --- a/source/fuzz/fuzzer_pass_push_ids_through_variables.h +++ b/source/fuzz/fuzzer_pass_push_ids_through_variables.h @@ -30,8 +30,6 @@ class FuzzerPassPushIdsThroughVariables : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassPushIdsThroughVariables() override; - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp b/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp index 139dc6e2..8a83d3bc 100644 --- a/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp +++ b/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp @@ -33,9 +33,6 @@ FuzzerPassReplaceAddsSubsMulsWithCarryingExtended:: : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassReplaceAddsSubsMulsWithCarryingExtended:: - ~FuzzerPassReplaceAddsSubsMulsWithCarryingExtended() = default; - void FuzzerPassReplaceAddsSubsMulsWithCarryingExtended::Apply() { std::vector<opt::Instruction> instructions_for_transformation; for (auto& function : *GetIRContext()->module()) { diff --git a/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h b/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h index dd39e6b0..0e29a6c6 100644 --- a/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h +++ b/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h @@ -31,8 +31,6 @@ class FuzzerPassReplaceAddsSubsMulsWithCarryingExtended : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassReplaceAddsSubsMulsWithCarryingExtended() override; - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.cpp b/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.cpp index e6bebead..a516f3d4 100644 --- a/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.cpp +++ b/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.cpp @@ -32,9 +32,6 @@ FuzzerPassReplaceBranchesFromDeadBlocksWithExits:: : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassReplaceBranchesFromDeadBlocksWithExits:: - ~FuzzerPassReplaceBranchesFromDeadBlocksWithExits() = default; - void FuzzerPassReplaceBranchesFromDeadBlocksWithExits::Apply() { // OpKill can only be used as a terminator in a function that is guaranteed // to be executed with the Fragment execution model. We conservatively only diff --git a/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h b/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h index 62164b32..ab7e00e5 100644 --- a/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h +++ b/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h @@ -30,8 +30,6 @@ class FuzzerPassReplaceBranchesFromDeadBlocksWithExits : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassReplaceBranchesFromDeadBlocksWithExits() override; - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp b/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp index 68471466..f17339a9 100644 --- a/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp +++ b/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp @@ -30,9 +30,6 @@ FuzzerPassReplaceCopyMemoriesWithLoadsStores:: : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassReplaceCopyMemoriesWithLoadsStores:: - ~FuzzerPassReplaceCopyMemoriesWithLoadsStores() = default; - void FuzzerPassReplaceCopyMemoriesWithLoadsStores::Apply() { GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) { // Randomly decide whether to replace the OpCopyMemory. diff --git a/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h b/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h index 9c24ac73..cffe1cb9 100644 --- a/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h +++ b/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h @@ -30,8 +30,6 @@ class FuzzerPassReplaceCopyMemoriesWithLoadsStores : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassReplaceCopyMemoriesWithLoadsStores() override; - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp b/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp index e3729245..24f2255b 100644 --- a/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp +++ b/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp @@ -30,9 +30,6 @@ FuzzerPassReplaceCopyObjectsWithStoresLoads:: : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassReplaceCopyObjectsWithStoresLoads:: - ~FuzzerPassReplaceCopyObjectsWithStoresLoads() = default; - void FuzzerPassReplaceCopyObjectsWithStoresLoads::Apply() { GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) { // Randomly decide whether to replace OpCopyObject. diff --git a/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h b/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h index ae03a450..e7b11ce1 100644 --- a/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h +++ b/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h @@ -30,8 +30,6 @@ class FuzzerPassReplaceCopyObjectsWithStoresLoads : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassReplaceCopyObjectsWithStoresLoads() override; - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp b/source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp index 432addbc..7e9d7baa 100644 --- a/source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp +++ b/source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp @@ -31,8 +31,6 @@ FuzzerPassReplaceIrrelevantIds::FuzzerPassReplaceIrrelevantIds( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassReplaceIrrelevantIds::~FuzzerPassReplaceIrrelevantIds() = default; - void FuzzerPassReplaceIrrelevantIds::Apply() { // Keep track of the irrelevant ids. This includes all the ids that are // irrelevant according to the fact manager and that are still present in the diff --git a/source/fuzz/fuzzer_pass_replace_irrelevant_ids.h b/source/fuzz/fuzzer_pass_replace_irrelevant_ids.h index ab3f01d1..1dc6b5d3 100644 --- a/source/fuzz/fuzzer_pass_replace_irrelevant_ids.h +++ b/source/fuzz/fuzzer_pass_replace_irrelevant_ids.h @@ -30,8 +30,6 @@ class FuzzerPassReplaceIrrelevantIds : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassReplaceIrrelevantIds(); - void Apply() override; }; } // namespace fuzz diff --git a/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp b/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp index c3e65789..0890c2fe 100644 --- a/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp +++ b/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp @@ -30,9 +30,6 @@ FuzzerPassReplaceLinearAlgebraInstructions:: : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassReplaceLinearAlgebraInstructions:: - ~FuzzerPassReplaceLinearAlgebraInstructions() = default; - void FuzzerPassReplaceLinearAlgebraInstructions::Apply() { // For each instruction, checks whether it is a linear algebra instruction. In // this case, the transformation is randomly applied. diff --git a/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h b/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h index 2c6126cd..5d2f2042 100644 --- a/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h +++ b/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h @@ -29,8 +29,6 @@ class FuzzerPassReplaceLinearAlgebraInstructions : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassReplaceLinearAlgebraInstructions(); - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp b/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp index 7690ac41..f2cf80fa 100644 --- a/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp +++ b/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp @@ -31,9 +31,6 @@ FuzzerPassReplaceLoadsStoresWithCopyMemories:: : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassReplaceLoadsStoresWithCopyMemories:: - ~FuzzerPassReplaceLoadsStoresWithCopyMemories() = default; - void FuzzerPassReplaceLoadsStoresWithCopyMemories::Apply() { // We look for matching pairs of instructions OpLoad and // OpStore within the same block. Potential instructions OpLoad to be matched diff --git a/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h b/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h index 67871db2..f30fc2b7 100644 --- a/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h +++ b/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h @@ -30,8 +30,6 @@ class FuzzerPassReplaceLoadsStoresWithCopyMemories : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassReplaceLoadsStoresWithCopyMemories() override; - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp b/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp index 433cf744..b0a3d57c 100644 --- a/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp +++ b/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp @@ -28,9 +28,6 @@ FuzzerPassReplaceOpPhiIdsFromDeadPredecessors:: : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassReplaceOpPhiIdsFromDeadPredecessors:: - ~FuzzerPassReplaceOpPhiIdsFromDeadPredecessors() = default; - void FuzzerPassReplaceOpPhiIdsFromDeadPredecessors::Apply() { // Keep a vector of the transformations to apply. std::vector<TransformationReplaceOpPhiIdFromDeadPredecessor> transformations; diff --git a/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h b/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h index 972c5f9d..a2bc1886 100644 --- a/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h +++ b/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h @@ -29,8 +29,6 @@ class FuzzerPassReplaceOpPhiIdsFromDeadPredecessors : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassReplaceOpPhiIdsFromDeadPredecessors(); - void Apply() override; }; } // namespace fuzz diff --git a/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.cpp b/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.cpp index c3db0ef1..10bb90ad 100644 --- a/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.cpp +++ b/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.cpp @@ -31,9 +31,6 @@ FuzzerPassReplaceOpSelectsWithConditionalBranches:: : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassReplaceOpSelectsWithConditionalBranches:: - ~FuzzerPassReplaceOpSelectsWithConditionalBranches() = default; - void FuzzerPassReplaceOpSelectsWithConditionalBranches::Apply() { // Keep track of the instructions that we want to replace. We need to collect // them in a vector, since it's not safe to modify the module while iterating diff --git a/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h b/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h index 04c6cc6d..ec743890 100644 --- a/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h +++ b/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h @@ -29,8 +29,6 @@ class FuzzerPassReplaceOpSelectsWithConditionalBranches : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassReplaceOpSelectsWithConditionalBranches() override; - void Apply() override; private: diff --git a/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp b/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp index 6b3a63ba..5c256bb0 100644 --- a/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp +++ b/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp @@ -31,9 +31,6 @@ FuzzerPassReplaceParameterWithGlobal::FuzzerPassReplaceParameterWithGlobal( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassReplaceParameterWithGlobal::~FuzzerPassReplaceParameterWithGlobal() = - default; - void FuzzerPassReplaceParameterWithGlobal::Apply() { for (const auto& function : *GetIRContext()->module()) { if (fuzzerutil::FunctionIsEntryPoint(GetIRContext(), diff --git a/source/fuzz/fuzzer_pass_replace_parameter_with_global.h b/source/fuzz/fuzzer_pass_replace_parameter_with_global.h index 25011bdc..2ae49469 100644 --- a/source/fuzz/fuzzer_pass_replace_parameter_with_global.h +++ b/source/fuzz/fuzzer_pass_replace_parameter_with_global.h @@ -29,8 +29,6 @@ class FuzzerPassReplaceParameterWithGlobal : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassReplaceParameterWithGlobal() override; - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp b/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp index 0e0610f7..c045e19f 100644 --- a/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp +++ b/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp @@ -31,9 +31,6 @@ FuzzerPassReplaceParamsWithStruct::FuzzerPassReplaceParamsWithStruct( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassReplaceParamsWithStruct::~FuzzerPassReplaceParamsWithStruct() = - default; - void FuzzerPassReplaceParamsWithStruct::Apply() { for (const auto& function : *GetIRContext()->module()) { auto params = diff --git a/source/fuzz/fuzzer_pass_replace_params_with_struct.h b/source/fuzz/fuzzer_pass_replace_params_with_struct.h index ed1aa6b5..f17f5207 100644 --- a/source/fuzz/fuzzer_pass_replace_params_with_struct.h +++ b/source/fuzz/fuzzer_pass_replace_params_with_struct.h @@ -29,8 +29,6 @@ class FuzzerPassReplaceParamsWithStruct : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassReplaceParamsWithStruct() override; - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_split_blocks.cpp b/source/fuzz/fuzzer_pass_split_blocks.cpp index 481cd960..7b493559 100644 --- a/source/fuzz/fuzzer_pass_split_blocks.cpp +++ b/source/fuzz/fuzzer_pass_split_blocks.cpp @@ -29,8 +29,6 @@ FuzzerPassSplitBlocks::FuzzerPassSplitBlocks( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassSplitBlocks::~FuzzerPassSplitBlocks() = default; - void FuzzerPassSplitBlocks::Apply() { // Gather up pointers to all the blocks in the module. We are then able to // iterate over these pointers and split the blocks to which they point; diff --git a/source/fuzz/fuzzer_pass_split_blocks.h b/source/fuzz/fuzzer_pass_split_blocks.h index 0ece48a0..58f10ddb 100644 --- a/source/fuzz/fuzzer_pass_split_blocks.h +++ b/source/fuzz/fuzzer_pass_split_blocks.h @@ -29,8 +29,6 @@ class FuzzerPassSplitBlocks : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassSplitBlocks() override; - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp b/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp index 321e8efd..27fadd17 100644 --- a/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp +++ b/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp @@ -28,8 +28,6 @@ FuzzerPassSwapCommutableOperands::FuzzerPassSwapCommutableOperands( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassSwapCommutableOperands::~FuzzerPassSwapCommutableOperands() = default; - void FuzzerPassSwapCommutableOperands::Apply() { auto context = GetIRContext(); // Iterates over the module's instructions and checks whether it is diff --git a/source/fuzz/fuzzer_pass_swap_commutable_operands.h b/source/fuzz/fuzzer_pass_swap_commutable_operands.h index 74d937d8..93de1728 100644 --- a/source/fuzz/fuzzer_pass_swap_commutable_operands.h +++ b/source/fuzz/fuzzer_pass_swap_commutable_operands.h @@ -30,8 +30,6 @@ class FuzzerPassSwapCommutableOperands : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassSwapCommutableOperands(); - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp b/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp index 9433a61f..b145b3bc 100644 --- a/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp +++ b/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp @@ -31,9 +31,6 @@ FuzzerPassSwapBranchConditionalOperands:: : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassSwapBranchConditionalOperands:: - ~FuzzerPassSwapBranchConditionalOperands() = default; - void FuzzerPassSwapBranchConditionalOperands::Apply() { ForEachInstructionWithInstructionDescriptor( [this](opt::Function* /*unused*/, opt::BasicBlock* /*unused*/, diff --git a/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h b/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h index f84f3ba4..0137f38b 100644 --- a/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h +++ b/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h @@ -29,8 +29,6 @@ class FuzzerPassSwapBranchConditionalOperands : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassSwapBranchConditionalOperands() override; - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_swap_functions.cpp b/source/fuzz/fuzzer_pass_swap_functions.cpp new file mode 100644 index 00000000..171f6cb8 --- /dev/null +++ b/source/fuzz/fuzzer_pass_swap_functions.cpp @@ -0,0 +1,52 @@ +// Copyright (c) 2021 Shiyu Liu +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_swap_functions.h" + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/transformation_swap_two_functions.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassSwapFunctions::FuzzerPassSwapFunctions( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +void FuzzerPassSwapFunctions::Apply() { + // Collect all function ids in a module. + std::vector<uint32_t> function_ids; + for (auto& function : *GetIRContext()->module()) { + function_ids.emplace_back(function.result_id()); + } + + // Iterate through every combination of id i & j where i!=j. + for (size_t i = 0; i < function_ids.size(); ++i) { + for (size_t j = i + 1; j < function_ids.size(); ++j) { + // Perform function swap randomly. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfSwappingFunctions())) { + continue; + } + TransformationSwapTwoFunctions transformation(function_ids[i], + function_ids[j]); + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/fuzzer_pass_swap_functions.h b/source/fuzz/fuzzer_pass_swap_functions.h new file mode 100644 index 00000000..ac551f69 --- /dev/null +++ b/source/fuzz/fuzzer_pass_swap_functions.h @@ -0,0 +1,37 @@ +// Copyright (c) 2021 Shiyu Liu +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_SWAP_FUNCTIONS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_SWAP_FUNCTIONS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Randomly swap functions within a module. +class FuzzerPassSwapFunctions : public FuzzerPass { + public: + FuzzerPassSwapFunctions(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_SWAP_FUNCTIONS_H_ diff --git a/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp b/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp index 4f26cba3..e5afd9ee 100644 --- a/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp +++ b/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp @@ -28,9 +28,6 @@ FuzzerPassToggleAccessChainInstruction::FuzzerPassToggleAccessChainInstruction( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassToggleAccessChainInstruction:: - ~FuzzerPassToggleAccessChainInstruction() = default; - void FuzzerPassToggleAccessChainInstruction::Apply() { auto context = GetIRContext(); // Iterates over the module's instructions and checks whether it is diff --git a/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h b/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h index d77c7cbe..ff2f5d45 100644 --- a/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h +++ b/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h @@ -29,8 +29,6 @@ class FuzzerPassToggleAccessChainInstruction : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassToggleAccessChainInstruction(); - void Apply() override; }; diff --git a/source/fuzz/fuzzer_pass_wrap_regions_in_selections.cpp b/source/fuzz/fuzzer_pass_wrap_regions_in_selections.cpp index e6cdca41..66bbcd81 100644 --- a/source/fuzz/fuzzer_pass_wrap_regions_in_selections.cpp +++ b/source/fuzz/fuzzer_pass_wrap_regions_in_selections.cpp @@ -29,9 +29,6 @@ FuzzerPassWrapRegionsInSelections::FuzzerPassWrapRegionsInSelections( : FuzzerPass(ir_context, transformation_context, fuzzer_context, transformations) {} -FuzzerPassWrapRegionsInSelections::~FuzzerPassWrapRegionsInSelections() = - default; - void FuzzerPassWrapRegionsInSelections::Apply() { for (auto& function : *GetIRContext()->module()) { if (!GetFuzzerContext()->ChoosePercentage( diff --git a/source/fuzz/fuzzer_pass_wrap_regions_in_selections.h b/source/fuzz/fuzzer_pass_wrap_regions_in_selections.h index eb28d208..822c308f 100644 --- a/source/fuzz/fuzzer_pass_wrap_regions_in_selections.h +++ b/source/fuzz/fuzzer_pass_wrap_regions_in_selections.h @@ -29,8 +29,6 @@ class FuzzerPassWrapRegionsInSelections : public FuzzerPass { FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations); - ~FuzzerPassWrapRegionsInSelections() override; - void Apply() override; private: diff --git a/source/fuzz/fuzzer_util.cpp b/source/fuzz/fuzzer_util.cpp index 8c14db4b..08b927e0 100644 --- a/source/fuzz/fuzzer_util.cpp +++ b/source/fuzz/fuzzer_util.cpp @@ -25,6 +25,25 @@ namespace fuzz { namespace fuzzerutil { namespace { +// A utility class that uses RAII to change and restore the terminator +// instruction of the |block|. +class ChangeTerminatorRAII { + public: + explicit ChangeTerminatorRAII(opt::BasicBlock* block, + opt::Instruction new_terminator) + : block_(block), old_terminator_(std::move(*block->terminator())) { + *block_->terminator() = std::move(new_terminator); + } + + ~ChangeTerminatorRAII() { + *block_->terminator() = std::move(old_terminator_); + } + + private: + opt::BasicBlock* block_; + opt::Instruction old_terminator_; +}; + uint32_t MaybeGetOpConstant(opt::IRContext* ir_context, const TransformationContext& transformation_context, const std::vector<uint32_t>& words, @@ -47,6 +66,34 @@ const spvtools::MessageConsumer kSilentMessageConsumer = [](spv_message_level_t, const char*, const spv_position_t&, const char*) -> void {}; +bool BuildIRContext(spv_target_env target_env, + const spvtools::MessageConsumer& message_consumer, + const std::vector<uint32_t>& binary_in, + spv_validator_options validator_options, + std::unique_ptr<spvtools::opt::IRContext>* ir_context) { + SpirvTools tools(target_env); + tools.SetMessageConsumer(message_consumer); + if (!tools.IsValid()) { + message_consumer(SPV_MSG_ERROR, nullptr, {}, + "Failed to create SPIRV-Tools interface; stopping."); + return false; + } + + // Initial binary should be valid. + if (!tools.Validate(binary_in.data(), binary_in.size(), validator_options)) { + message_consumer(SPV_MSG_ERROR, nullptr, {}, + "Initial binary is invalid; stopping."); + return false; + } + + // Build the module from the input binary. + auto result = BuildModule(target_env, message_consumer, binary_in.data(), + binary_in.size()); + assert(result && "IRContext must be valid"); + *ir_context = std::move(result); + return true; +} + bool IsFreshId(opt::IRContext* context, uint32_t id) { return !context->get_def_use_mgr()->GetDef(id); } @@ -135,35 +182,46 @@ bool PhiIdsOkForNewEdge( return true; } -void AddUnreachableEdgeAndUpdateOpPhis( - opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to, - uint32_t bool_id, - const google::protobuf::RepeatedField<google::protobuf::uint32>& phi_ids) { - assert(PhiIdsOkForNewEdge(context, bb_from, bb_to, phi_ids) && - "Precondition on phi_ids is not satisfied"); +opt::Instruction CreateUnreachableEdgeInstruction(opt::IRContext* ir_context, + uint32_t bb_from_id, + uint32_t bb_to_id, + uint32_t bool_id) { + const auto* bb_from = MaybeFindBlock(ir_context, bb_from_id); + assert(bb_from && "|bb_from_id| is invalid"); + assert(MaybeFindBlock(ir_context, bb_to_id) && "|bb_to_id| is invalid"); assert(bb_from->terminator()->opcode() == SpvOpBranch && "Precondition on terminator of bb_from is not satisfied"); // Get the id of the boolean constant to be used as the condition. - auto condition_inst = context->get_def_use_mgr()->GetDef(bool_id); + auto condition_inst = ir_context->get_def_use_mgr()->GetDef(bool_id); assert(condition_inst && (condition_inst->opcode() == SpvOpConstantTrue || condition_inst->opcode() == SpvOpConstantFalse) && "|bool_id| is invalid"); auto condition_value = condition_inst->opcode() == SpvOpConstantTrue; - - const bool from_to_edge_already_exists = bb_from->IsSuccessor(bb_to); - auto successor = bb_from->terminator()->GetSingleWordInOperand(0); + auto successor_id = bb_from->terminator()->GetSingleWordInOperand(0); // Add the dead branch, by turning OpBranch into OpBranchConditional, and // ordering the targets depending on whether the given boolean corresponds to // true or false. - bb_from->terminator()->SetOpcode(SpvOpBranchConditional); - bb_from->terminator()->SetInOperands( + return opt::Instruction( + ir_context, SpvOpBranchConditional, 0, 0, {{SPV_OPERAND_TYPE_ID, {bool_id}}, - {SPV_OPERAND_TYPE_ID, {condition_value ? successor : bb_to->id()}}, - {SPV_OPERAND_TYPE_ID, {condition_value ? bb_to->id() : successor}}}); + {SPV_OPERAND_TYPE_ID, {condition_value ? successor_id : bb_to_id}}, + {SPV_OPERAND_TYPE_ID, {condition_value ? bb_to_id : successor_id}}}); +} + +void AddUnreachableEdgeAndUpdateOpPhis( + opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to, + uint32_t bool_id, + const google::protobuf::RepeatedField<google::protobuf::uint32>& phi_ids) { + assert(PhiIdsOkForNewEdge(context, bb_from, bb_to, phi_ids) && + "Precondition on phi_ids is not satisfied"); + + const bool from_to_edge_already_exists = bb_from->IsSuccessor(bb_to); + *bb_from->terminator() = CreateUnreachableEdgeInstruction( + context, bb_from->id(), bb_to->id(), bool_id); // Update OpPhi instructions in the target block if this branch adds a // previously non-existent edge from source to target. @@ -410,7 +468,7 @@ bool IsValid(const opt::IRContext* context, std::vector<uint32_t> binary; context->module()->ToBinary(&binary, false); SpirvTools tools(context->grammar().target_env()); - tools.SetMessageConsumer(consumer); + tools.SetMessageConsumer(std::move(consumer)); return tools.Validate(binary.data(), binary.size(), validator_options); } @@ -747,14 +805,15 @@ bool IsNullConstantSupported(const opt::analysis::Type& type) { bool GlobalVariablesMustBeDeclaredInEntryPointInterfaces( const opt::IRContext* ir_context) { - // TODO(afd): We capture the universal environments for which this requirement - // holds. The check should be refined on demand for other target - // environments. + // TODO(afd): We capture the environments for which this requirement holds. + // The check should be refined on demand for other target environments. switch (ir_context->grammar().target_env()) { 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_VULKAN_1_0: + case SPV_ENV_VULKAN_1_1: return false; default: return true; @@ -778,9 +837,10 @@ void AddVariableIdToEntryPointInterfaces(opt::IRContext* context, uint32_t id) { } } -void AddGlobalVariable(opt::IRContext* context, uint32_t result_id, - uint32_t type_id, SpvStorageClass storage_class, - uint32_t initializer_id) { +opt::Instruction* AddGlobalVariable(opt::IRContext* context, uint32_t result_id, + uint32_t type_id, + SpvStorageClass storage_class, + uint32_t initializer_id) { // Check various preconditions. assert(result_id != 0 && "Result id can't be 0"); @@ -815,16 +875,20 @@ void AddGlobalVariable(opt::IRContext* context, uint32_t result_id, operands.push_back({SPV_OPERAND_TYPE_ID, {initializer_id}}); } - context->module()->AddGlobalValue(MakeUnique<opt::Instruction>( - context, SpvOpVariable, type_id, result_id, std::move(operands))); + auto new_instruction = MakeUnique<opt::Instruction>( + context, SpvOpVariable, type_id, result_id, std::move(operands)); + auto result = new_instruction.get(); + context->module()->AddGlobalValue(std::move(new_instruction)); AddVariableIdToEntryPointInterfaces(context, result_id); UpdateModuleIdBound(context, result_id); + + return result; } -void AddLocalVariable(opt::IRContext* context, uint32_t result_id, - uint32_t type_id, uint32_t function_id, - uint32_t initializer_id) { +opt::Instruction* AddLocalVariable(opt::IRContext* context, uint32_t result_id, + uint32_t type_id, uint32_t function_id, + uint32_t initializer_id) { // Check various preconditions. assert(result_id != 0 && "Result id can't be 0"); @@ -845,13 +909,17 @@ void AddLocalVariable(opt::IRContext* context, uint32_t result_id, auto* function = FindFunction(context, function_id); assert(function && "Function id is invalid"); - function->begin()->begin()->InsertBefore(MakeUnique<opt::Instruction>( + auto new_instruction = MakeUnique<opt::Instruction>( context, SpvOpVariable, type_id, result_id, opt::Instruction::OperandList{ {SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}, - {SPV_OPERAND_TYPE_ID, {initializer_id}}})); + {SPV_OPERAND_TYPE_ID, {initializer_id}}}); + auto result = new_instruction.get(); + function->begin()->begin()->InsertBefore(std::move(new_instruction)); UpdateModuleIdBound(context, result_id); + + return result; } bool HasDuplicates(const std::vector<uint32_t>& arr) { @@ -1355,74 +1423,6 @@ uint32_t MaybeGetBoolConstant( return 0; } -void AddIntegerType(opt::IRContext* ir_context, uint32_t result_id, - uint32_t width, bool is_signed) { - ir_context->module()->AddType(MakeUnique<opt::Instruction>( - ir_context, SpvOpTypeInt, 0, result_id, - opt::Instruction::OperandList{ - {SPV_OPERAND_TYPE_LITERAL_INTEGER, {width}}, - {SPV_OPERAND_TYPE_LITERAL_INTEGER, {is_signed ? 1u : 0u}}})); - - UpdateModuleIdBound(ir_context, result_id); -} - -void AddFloatType(opt::IRContext* ir_context, uint32_t result_id, - uint32_t width) { - ir_context->module()->AddType(MakeUnique<opt::Instruction>( - ir_context, SpvOpTypeFloat, 0, result_id, - opt::Instruction::OperandList{ - {SPV_OPERAND_TYPE_LITERAL_INTEGER, {width}}})); - - UpdateModuleIdBound(ir_context, result_id); -} - -void AddVectorType(opt::IRContext* ir_context, uint32_t result_id, - uint32_t component_type_id, uint32_t element_count) { - const auto* component_type = - ir_context->get_type_mgr()->GetType(component_type_id); - (void)component_type; // Make compiler happy in release mode. - assert(component_type && - (component_type->AsInteger() || component_type->AsFloat() || - component_type->AsBool()) && - "|component_type_id| is invalid"); - assert(element_count >= 2 && element_count <= 4 && - "Precondition: component count must be in range [2, 4]."); - ir_context->module()->AddType(MakeUnique<opt::Instruction>( - ir_context, SpvOpTypeVector, 0, result_id, - opt::Instruction::OperandList{ - {SPV_OPERAND_TYPE_ID, {component_type_id}}, - {SPV_OPERAND_TYPE_LITERAL_INTEGER, {element_count}}})); - - UpdateModuleIdBound(ir_context, result_id); -} - -void AddStructType(opt::IRContext* ir_context, uint32_t result_id, - const std::vector<uint32_t>& component_type_ids) { - opt::Instruction::OperandList operands; - operands.reserve(component_type_ids.size()); - - for (auto type_id : component_type_ids) { - const auto* type = ir_context->get_type_mgr()->GetType(type_id); - (void)type; // Make compiler happy in release mode. - assert(type && !type->AsFunction() && "Component's type id is invalid"); - - if (type->AsStruct()) { - // From the spec for the BuiltIn decoration: - // - When applied to a structure-type member, that structure type cannot - // be contained as a member of another structure type. - assert(!MembersHaveBuiltInDecoration(ir_context, type_id) && - "A member struct has BuiltIn members"); - } - - operands.push_back({SPV_OPERAND_TYPE_ID, {type_id}}); - } - - ir_context->AddType(MakeUnique<opt::Instruction>( - ir_context, SpvOpTypeStruct, 0, result_id, std::move(operands))); - - UpdateModuleIdBound(ir_context, result_id); -} - std::vector<uint32_t> IntToWords(uint64_t value, uint32_t width, bool is_signed) { assert(width <= 64 && "The bit width should not be more than 64 bits"); @@ -1827,6 +1827,113 @@ std::set<uint32_t> GetReachableReturnBlocks(opt::IRContext* ir_context, return result; } +bool NewTerminatorPreservesDominationRules(opt::IRContext* ir_context, + uint32_t block_id, + opt::Instruction new_terminator) { + auto* mutated_block = MaybeFindBlock(ir_context, block_id); + assert(mutated_block && "|block_id| is invalid"); + + ChangeTerminatorRAII change_terminator_raii(mutated_block, + std::move(new_terminator)); + opt::DominatorAnalysis dominator_analysis; + dominator_analysis.InitializeTree(*ir_context->cfg(), + mutated_block->GetParent()); + + // Check that each dominator appears before each dominated block. + std::unordered_map<uint32_t, size_t> positions; + for (const auto& block : *mutated_block->GetParent()) { + positions[block.id()] = positions.size(); + } + + std::queue<uint32_t> q({mutated_block->GetParent()->begin()->id()}); + std::unordered_set<uint32_t> visited; + while (!q.empty()) { + auto block = q.front(); + q.pop(); + visited.insert(block); + + auto success = ir_context->cfg()->block(block)->WhileEachSuccessorLabel( + [&positions, &visited, &dominator_analysis, block, &q](uint32_t id) { + if (id == block) { + // Handle the case when loop header and continue target are the same + // block. + return true; + } + + if (dominator_analysis.Dominates(block, id) && + positions[block] > positions[id]) { + // |block| dominates |id| but appears after |id| - violates + // domination rules. + return false; + } + + if (!visited.count(id)) { + q.push(id); + } + + return true; + }); + + if (!success) { + return false; + } + } + + // For each instruction in the |block->GetParent()| function check whether + // all its dependencies satisfy domination rules (i.e. all id operands + // dominate that instruction). + for (const auto& block : *mutated_block->GetParent()) { + if (!dominator_analysis.IsReachable(&block)) { + // If some block is not reachable then we don't need to worry about the + // preservation of domination rules for its instructions. + continue; + } + + for (const auto& inst : block) { + for (uint32_t i = 0; i < inst.NumInOperands(); + i += inst.opcode() == SpvOpPhi ? 2 : 1) { + const auto& operand = inst.GetInOperand(i); + if (!spvIsInIdType(operand.type)) { + continue; + } + + if (MaybeFindBlock(ir_context, operand.words[0])) { + // Ignore operands that refer to OpLabel instructions. + continue; + } + + const auto* dependency_block = + ir_context->get_instr_block(operand.words[0]); + if (!dependency_block) { + // A global instruction always dominates all instructions in any + // function. + continue; + } + + auto domination_target_id = inst.opcode() == SpvOpPhi + ? inst.GetSingleWordInOperand(i + 1) + : block.id(); + + if (!dominator_analysis.Dominates(dependency_block->id(), + domination_target_id)) { + return false; + } + } + } + } + + return true; +} + +opt::Module::iterator GetFunctionIterator(opt::IRContext* ir_context, + uint32_t function_id) { + return std::find_if(ir_context->module()->begin(), + ir_context->module()->end(), + [function_id](const opt::Function& f) { + return f.result_id() == function_id; + }); +} + } // namespace fuzzerutil } // namespace fuzz } // namespace spvtools diff --git a/source/fuzz/fuzzer_util.h b/source/fuzz/fuzzer_util.h index 4e6ec36e..dd7bd961 100644 --- a/source/fuzz/fuzzer_util.h +++ b/source/fuzz/fuzzer_util.h @@ -24,6 +24,7 @@ #include "source/opt/basic_block.h" #include "source/opt/instruction.h" #include "source/opt/ir_context.h" +#include "source/opt/module.h" #include "spirv-tools/libspirv.hpp" namespace spvtools { @@ -38,6 +39,15 @@ extern const spvtools::MessageConsumer kSilentMessageConsumer; // Function type that produces a SPIR-V module. using ModuleSupplier = std::function<std::unique_ptr<opt::IRContext>()>; +// Builds a new opt::IRContext object. Returns true if successful and changes +// the |ir_context| parameter. Otherwise (if any errors occur), returns false +// and |ir_context| remains unchanged. +bool BuildIRContext(spv_target_env target_env, + const spvtools::MessageConsumer& message_consumer, + const std::vector<uint32_t>& binary_in, + spv_validator_options validator_options, + std::unique_ptr<spvtools::opt::IRContext>* ir_context); + // Returns true if and only if the module does not define the given id. bool IsFreshId(opt::IRContext* context, uint32_t id); @@ -59,6 +69,16 @@ bool PhiIdsOkForNewEdge( opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to, const google::protobuf::RepeatedField<google::protobuf::uint32>& phi_ids); +// Returns an OpBranchConditional instruction that will create an unreachable +// branch from |bb_from_id| to |bb_to_id|. |bool_id| must be a result id of +// either OpConstantTrue or OpConstantFalse. Based on the opcode of |bool_id|, +// operands of the returned instruction will be positioned in a way that the +// branch from |bb_from_id| to |bb_to_id| is always unreachable. +opt::Instruction CreateUnreachableEdgeInstruction(opt::IRContext* ir_context, + uint32_t bb_from_id, + uint32_t bb_to_id, + uint32_t bool_id); + // Requires that |bool_id| is a valid result id of either OpConstantTrue or // OpConstantFalse, that PhiIdsOkForNewEdge(context, bb_from, bb_to, phi_ids) // holds, and that bb_from ends with "OpBranch %some_block". Turns OpBranch @@ -284,9 +304,12 @@ void AddVariableIdToEntryPointInterfaces(opt::IRContext* context, uint32_t id); // - |initializer_id| must be 0 if |storage_class| is Workgroup, and otherwise // may either be 0 or the id of a constant whose type is the pointee type of // |type_id|. -void AddGlobalVariable(opt::IRContext* context, uint32_t result_id, - uint32_t type_id, SpvStorageClass storage_class, - uint32_t initializer_id); +// +// Returns a pointer to the new global variable instruction. +opt::Instruction* AddGlobalVariable(opt::IRContext* context, uint32_t result_id, + uint32_t type_id, + SpvStorageClass storage_class, + uint32_t initializer_id); // Adds an instruction to the start of |function_id|, of the form: // |result_id| = OpVariable |type_id| Function |initializer_id|. @@ -296,9 +319,11 @@ void AddGlobalVariable(opt::IRContext* context, uint32_t result_id, // - |initializer_id| must be the id of a constant with the same type as the // pointer's pointee type. // - |function_id| must be the id of a function. -void AddLocalVariable(opt::IRContext* context, uint32_t result_id, - uint32_t type_id, uint32_t function_id, - uint32_t initializer_id); +// +// Returns a pointer to the new local variable instruction. +opt::Instruction* AddLocalVariable(opt::IRContext* context, uint32_t result_id, + uint32_t type_id, uint32_t function_id, + uint32_t initializer_id); // Returns true if the vector |arr| has duplicates. bool HasDuplicates(const std::vector<uint32_t>& arr); @@ -476,30 +501,6 @@ uint32_t MaybeGetBoolConstant( const TransformationContext& transformation_context, bool value, bool is_irrelevant); -// Creates a new OpTypeInt instruction in the module. Updates module's id bound -// to accommodate for |result_id|. -void AddIntegerType(opt::IRContext* ir_context, uint32_t result_id, - uint32_t width, bool is_signed); - -// Creates a new OpTypeFloat instruction in the module. Updates module's id -// bound to accommodate for |result_id|. -void AddFloatType(opt::IRContext* ir_context, uint32_t result_id, - uint32_t width); - -// Creates a new OpTypeVector instruction in the module. |component_type_id| -// must be a valid result id of an OpTypeInt, OpTypeFloat or OpTypeBool -// instruction in the module. |element_count| must be in the range [2, 4]. -// Updates module's id bound to accommodate for |result_id|. -void AddVectorType(opt::IRContext* ir_context, uint32_t result_id, - uint32_t component_type_id, uint32_t element_count); - -// Creates a new OpTypeStruct instruction in the module. Updates module's id -// bound to accommodate for |result_id|. |component_type_ids| may not contain -// a result id of an OpTypeFunction. if |component_type_ids| contains a result -// of an OpTypeStruct instruction, that struct may not have BuiltIn members. -void AddStructType(opt::IRContext* ir_context, uint32_t result_id, - const std::vector<uint32_t>& component_type_ids); - // Returns a vector of words representing the integer |value|, only considering // the last |width| bits. The last |width| bits are sign-extended if the value // is signed, zero-extended if it is unsigned. @@ -588,6 +589,20 @@ bool InstructionHasNoSideEffects(const opt::Instruction& instruction); std::set<uint32_t> GetReachableReturnBlocks(opt::IRContext* ir_context, uint32_t function_id); +// Returns true if changing terminator instruction to |new_terminator| in the +// basic block with id |block_id| preserves domination rules and valid block +// order (i.e. dominator must always appear before dominated in the CFG). +// Returns false otherwise. +bool NewTerminatorPreservesDominationRules(opt::IRContext* ir_context, + uint32_t block_id, + opt::Instruction new_terminator); + +// Return the iterator that points to the function with the corresponding +// function id. If the function is not found, return the pointer pointing to +// module()->end(). +opt::Module::iterator GetFunctionIterator(opt::IRContext* ir_context, + uint32_t function_id); + } // namespace fuzzerutil } // namespace fuzz } // namespace spvtools diff --git a/source/fuzz/instruction_descriptor.cpp b/source/fuzz/instruction_descriptor.cpp index c0cc5e52..fb1ff765 100644 --- a/source/fuzz/instruction_descriptor.cpp +++ b/source/fuzz/instruction_descriptor.cpp @@ -20,37 +20,32 @@ namespace fuzz { opt::Instruction* FindInstruction( const protobufs::InstructionDescriptor& instruction_descriptor, spvtools::opt::IRContext* context) { - for (auto& function : *context->module()) { - for (auto& block : function) { - bool found_base = - block.id() == instruction_descriptor.base_instruction_result_id(); - uint32_t num_ignored = 0; - for (auto& instruction : block) { - if (instruction.HasResultId() && - instruction.result_id() == - instruction_descriptor.base_instruction_result_id()) { - assert(!found_base && - "It should not be possible to find the base instruction " - "multiple times."); - found_base = true; - assert(num_ignored == 0 && - "The skipped instruction count should only be incremented " - "after the instruction base has been found."); - } - if (found_base && - instruction.opcode() == - instruction_descriptor.target_instruction_opcode()) { - if (num_ignored == instruction_descriptor.num_opcodes_to_ignore()) { - return &instruction; - } - num_ignored++; - } - } - if (found_base) { - // We found the base instruction, but did not find the target - // instruction in the same block. - return nullptr; + auto block = context->get_instr_block( + instruction_descriptor.base_instruction_result_id()); + if (block == nullptr) { + return nullptr; + } + bool found_base = + block->id() == instruction_descriptor.base_instruction_result_id(); + uint32_t num_ignored = 0; + for (auto& instruction : *block) { + if (instruction.HasResultId() && + instruction.result_id() == + instruction_descriptor.base_instruction_result_id()) { + assert(!found_base && + "It should not be possible to find the base instruction " + "multiple times."); + found_base = true; + assert(num_ignored == 0 && + "The skipped instruction count should only be incremented " + "after the instruction base has been found."); + } + if (found_base && instruction.opcode() == + instruction_descriptor.target_instruction_opcode()) { + if (num_ignored == instruction_descriptor.num_opcodes_to_ignore()) { + return &instruction; } + num_ignored++; } } return nullptr; diff --git a/source/fuzz/pass_management/repeated_pass_manager.cpp b/source/fuzz/pass_management/repeated_pass_manager.cpp index 032f2645..ec443733 100644 --- a/source/fuzz/pass_management/repeated_pass_manager.cpp +++ b/source/fuzz/pass_management/repeated_pass_manager.cpp @@ -14,6 +14,10 @@ #include "source/fuzz/pass_management/repeated_pass_manager.h" +#include "source/fuzz/pass_management/repeated_pass_manager_looped_with_recommendations.h" +#include "source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.h" +#include "source/fuzz/pass_management/repeated_pass_manager_simple.h" + namespace spvtools { namespace fuzz { @@ -23,5 +27,25 @@ RepeatedPassManager::RepeatedPassManager(FuzzerContext* fuzzer_context, RepeatedPassManager::~RepeatedPassManager() = default; +std::unique_ptr<RepeatedPassManager> RepeatedPassManager::Create( + RepeatedPassStrategy strategy, FuzzerContext* fuzzer_context, + RepeatedPassInstances* pass_instances, + RepeatedPassRecommender* pass_recommender) { + switch (strategy) { + case RepeatedPassStrategy::kSimple: + return MakeUnique<RepeatedPassManagerSimple>(fuzzer_context, + pass_instances); + case RepeatedPassStrategy::kLoopedWithRecommendations: + return MakeUnique<RepeatedPassManagerLoopedWithRecommendations>( + fuzzer_context, pass_instances, pass_recommender); + case RepeatedPassStrategy::kRandomWithRecommendations: + return MakeUnique<RepeatedPassManagerRandomWithRecommendations>( + fuzzer_context, pass_instances, pass_recommender); + } + + assert(false && "Unreachable"); + return nullptr; +} + } // namespace fuzz } // namespace spvtools diff --git a/source/fuzz/pass_management/repeated_pass_manager.h b/source/fuzz/pass_management/repeated_pass_manager.h index 1c231792..1e6ae3e4 100644 --- a/source/fuzz/pass_management/repeated_pass_manager.h +++ b/source/fuzz/pass_management/repeated_pass_manager.h @@ -18,11 +18,21 @@ #include "source/fuzz/fuzzer_context.h" #include "source/fuzz/fuzzer_pass.h" #include "source/fuzz/pass_management/repeated_pass_instances.h" +#include "source/fuzz/pass_management/repeated_pass_recommender.h" #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" namespace spvtools { namespace fuzz { +// Each field of this enum corresponds to an available repeated pass +// strategy, and is used to decide which kind of RepeatedPassManager object +// to create. +enum class RepeatedPassStrategy { + kSimple, + kRandomWithRecommendations, + kLoopedWithRecommendations +}; + // An interface to encapsulate the manner in which the sequence of repeated // passes that are applied during fuzzing is chosen. An implementation of this // interface could, for example, keep track of the history of passes that have @@ -40,6 +50,12 @@ class RepeatedPassManager { virtual FuzzerPass* ChoosePass( const protobufs::TransformationSequence& applied_transformations) = 0; + // Creates a corresponding RepeatedPassManager based on the |strategy|. + static std::unique_ptr<RepeatedPassManager> Create( + RepeatedPassStrategy strategy, FuzzerContext* fuzzer_context, + RepeatedPassInstances* pass_instances, + RepeatedPassRecommender* pass_recommender); + protected: FuzzerContext* GetFuzzerContext() { return fuzzer_context_; } diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto index 770a2dd3..657e8076 100644 --- a/source/fuzz/protobufs/spvtoolsfuzz.proto +++ b/source/fuzz/protobufs/spvtoolsfuzz.proto @@ -557,6 +557,8 @@ message Transformation { TransformationWrapEarlyTerminatorInFunction wrap_early_terminator_in_function = 83; TransformationMergeFunctionReturns merge_function_returns = 84; TransformationExpandVectorReduction expand_vector_reduction = 85; + TransformationSwapFunctionVariables swap_function_variables = 86; + TransformationSwapTwoFunctions swap_two_functions = 87; // Add additional option using the next available number. } } @@ -1134,6 +1136,14 @@ message TransformationAddSynonym { // New synonym is derived by applying OpLogicalAnd to |result_id| with the second // operand being 'true'. LOGICAL_AND = 5; + + // New synonym is derived by applying OpBitwiseOr to |result_id| with the second + // operand being 0 taken with the same bit length as |result_id| + BITWISE_OR = 6; + + // New synonym is derived by applying OpBitwiseXor to |result_id| with the second + // operand being 0 taken with the same bit length as |result_id| + BITWISE_XOR = 7; } // Type of the synonym to create. See SynonymType for more details. @@ -1632,6 +1642,9 @@ message TransformationMergeFunctionReturns { // A fresh id for the header of the new outer loop. uint32 outer_header_id = 2; + // A fresh id for an unreachable continue construct for the new outer loop. + uint32 unreachable_continue_id = 7; + // A fresh id for the new return block of the function, // i.e. the merge block of the new outer loop. uint32 outer_return_id = 3; @@ -2260,6 +2273,24 @@ message TransformationSwapConditionalBranchOperands { } +message TransformationSwapFunctionVariables { + // A transformation that swaps function variables + + // Result id of the first variable. + uint32 result_id1 = 1; + // Result id of the second variable. + uint32 result_id2 = 2; + +} + +message TransformationSwapTwoFunctions { + // A transformation that swaps the position of two functions within the same module. + + // the IDs for the two functions that are swapped. + uint32 function_id1 = 1; + uint32 function_id2 = 2; +} + message TransformationToggleAccessChainInstruction { // A transformation that toggles an access chain instruction. diff --git a/source/fuzz/transformation.cpp b/source/fuzz/transformation.cpp index ebbc3937..4ea0c773 100644 --- a/source/fuzz/transformation.cpp +++ b/source/fuzz/transformation.cpp @@ -98,6 +98,8 @@ #include "source/fuzz/transformation_store.h" #include "source/fuzz/transformation_swap_commutable_operands.h" #include "source/fuzz/transformation_swap_conditional_branch_operands.h" +#include "source/fuzz/transformation_swap_function_variables.h" +#include "source/fuzz/transformation_swap_two_functions.h" #include "source/fuzz/transformation_toggle_access_chain_instruction.h" #include "source/fuzz/transformation_vector_shuffle.h" #include "source/fuzz/transformation_wrap_early_terminator_in_function.h" @@ -361,6 +363,12 @@ std::unique_ptr<Transformation> Transformation::FromMessage( kSwapConditionalBranchOperands: return MakeUnique<TransformationSwapConditionalBranchOperands>( message.swap_conditional_branch_operands()); + case protobufs::Transformation::TransformationCase::kSwapFunctionVariables: + return MakeUnique<TransformationSwapFunctionVariables>( + message.swap_function_variables()); + case protobufs::Transformation::TransformationCase::kSwapTwoFunctions: + return MakeUnique<TransformationSwapTwoFunctions>( + message.swap_two_functions()); case protobufs::Transformation::TransformationCase:: kToggleAccessChainInstruction: return MakeUnique<TransformationToggleAccessChainInstruction>( diff --git a/source/fuzz/transformation_access_chain.cpp b/source/fuzz/transformation_access_chain.cpp index daf73d63..3fe9e656 100644 --- a/source/fuzz/transformation_access_chain.cpp +++ b/source/fuzz/transformation_access_chain.cpp @@ -23,8 +23,8 @@ namespace spvtools { namespace fuzz { TransformationAccessChain::TransformationAccessChain( - const spvtools::fuzz::protobufs::TransformationAccessChain& message) - : message_(message) {} + protobufs::TransformationAccessChain message) + : message_(std::move(message)) {} TransformationAccessChain::TransformationAccessChain( uint32_t fresh_id, uint32_t pointer_id, @@ -122,7 +122,7 @@ bool TransformationAccessChain::IsApplicable( bool successful; std::tie(successful, index_value) = - GetIndexValue(ir_context, index_id, subobject_type_id); + GetStructIndexValue(ir_context, index_id, subobject_type_id); if (!successful) { return false; @@ -228,6 +228,11 @@ void TransformationAccessChain::Apply( uint32_t id_pairs_used = 0; + opt::Instruction* instruction_to_insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + opt::BasicBlock* enclosing_block = + ir_context->get_instr_block(instruction_to_insert_before); + // Go through the index ids in turn. for (auto index_id : message_.index_id()) { uint32_t index_value; @@ -242,7 +247,7 @@ void TransformationAccessChain::Apply( // It is a struct: we need to retrieve the integer value. index_value = - GetIndexValue(ir_context, index_id, subobject_type_id).second; + GetStructIndexValue(ir_context, index_id, subobject_type_id).second; new_index_id = index_id; @@ -280,29 +285,37 @@ void TransformationAccessChain::Apply( // Clamp the integer and add the corresponding instructions in the module // if |add_clamping_instructions| is set. - auto instruction_to_insert_before = - FindInstruction(message_.instruction_to_insert_before(), ir_context); // Compare the index with the bound via an instruction of the form: // %fresh_ids.first = OpULessThanEqual %bool %int_id %bound_minus_one. fuzzerutil::UpdateModuleIdBound(ir_context, fresh_ids.first()); - instruction_to_insert_before->InsertBefore(MakeUnique<opt::Instruction>( + auto comparison_instruction = MakeUnique<opt::Instruction>( ir_context, SpvOpULessThanEqual, bool_type_id, fresh_ids.first(), opt::Instruction::OperandList( {{SPV_OPERAND_TYPE_ID, {index_instruction->result_id()}}, - {SPV_OPERAND_TYPE_ID, {bound_minus_one_id}}}))); + {SPV_OPERAND_TYPE_ID, {bound_minus_one_id}}})); + auto comparison_instruction_ptr = comparison_instruction.get(); + instruction_to_insert_before->InsertBefore( + std::move(comparison_instruction)); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse( + comparison_instruction_ptr); + ir_context->set_instr_block(comparison_instruction_ptr, enclosing_block); // Select the index if in-bounds, otherwise one less than the bound: // %fresh_ids.second = OpSelect %int_type %fresh_ids.first %int_id // %bound_minus_one fuzzerutil::UpdateModuleIdBound(ir_context, fresh_ids.second()); - instruction_to_insert_before->InsertBefore(MakeUnique<opt::Instruction>( + auto select_instruction = MakeUnique<opt::Instruction>( ir_context, SpvOpSelect, int_type_inst->result_id(), fresh_ids.second(), opt::Instruction::OperandList( {{SPV_OPERAND_TYPE_ID, {fresh_ids.first()}}, {SPV_OPERAND_TYPE_ID, {index_instruction->result_id()}}, - {SPV_OPERAND_TYPE_ID, {bound_minus_one_id}}}))); + {SPV_OPERAND_TYPE_ID, {bound_minus_one_id}}})); + auto select_instruction_ptr = select_instruction.get(); + instruction_to_insert_before->InsertBefore(std::move(select_instruction)); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(select_instruction_ptr); + ir_context->set_instr_block(select_instruction_ptr, enclosing_block); new_index_id = fresh_ids.second(); @@ -326,13 +339,14 @@ void TransformationAccessChain::Apply( // Add the access chain instruction to the module, and update the module's // id bound. fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); - FindInstruction(message_.instruction_to_insert_before(), ir_context) - ->InsertBefore(MakeUnique<opt::Instruction>( - ir_context, SpvOpAccessChain, result_type, message_.fresh_id(), - operands)); - - // Conservatively invalidate all analyses. - ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + auto access_chain_instruction = MakeUnique<opt::Instruction>( + ir_context, SpvOpAccessChain, result_type, message_.fresh_id(), operands); + auto access_chain_instruction_ptr = access_chain_instruction.get(); + instruction_to_insert_before->InsertBefore( + std::move(access_chain_instruction)); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse( + access_chain_instruction_ptr); + ir_context->set_instr_block(access_chain_instruction_ptr, enclosing_block); // If the base pointer's pointee value was irrelevant, the same is true of // the pointee value of the result of this access chain. @@ -349,9 +363,12 @@ protobufs::Transformation TransformationAccessChain::ToMessage() const { return result; } -std::pair<bool, uint32_t> TransformationAccessChain::GetIndexValue( +std::pair<bool, uint32_t> TransformationAccessChain::GetStructIndexValue( opt::IRContext* ir_context, uint32_t index_id, uint32_t object_type_id) const { + assert(ir_context->get_def_use_mgr()->GetDef(object_type_id)->opcode() == + SpvOpTypeStruct && + "Precondition: the type must be a struct type."); if (!ValidIndexToComposite(ir_context, index_id, object_type_id)) { return {false, 0}; } @@ -360,10 +377,9 @@ std::pair<bool, uint32_t> TransformationAccessChain::GetIndexValue( uint32_t bound = fuzzerutil::GetBoundForCompositeIndex( *ir_context->get_def_use_mgr()->GetDef(object_type_id), ir_context); - // The index must be a constant - if (!spvOpcodeIsConstant(index_instruction->opcode())) { - return {false, 0}; - } + // Ensure that the index given must represent a constant. + assert(spvOpcodeIsConstant(index_instruction->opcode()) && + "A non-constant index should already have been rejected."); // The index must be in bounds. uint32_t value = index_instruction->GetSingleWordInOperand(0); diff --git a/source/fuzz/transformation_access_chain.h b/source/fuzz/transformation_access_chain.h index 02cdc329..4e4fd2b6 100644 --- a/source/fuzz/transformation_access_chain.h +++ b/source/fuzz/transformation_access_chain.h @@ -28,7 +28,7 @@ namespace fuzz { class TransformationAccessChain : public Transformation { public: explicit TransformationAccessChain( - const protobufs::TransformationAccessChain& message); + protobufs::TransformationAccessChain message); TransformationAccessChain( uint32_t fresh_id, uint32_t pointer_id, @@ -83,13 +83,13 @@ class TransformationAccessChain : public Transformation { private: // Returns {false, 0} in each of the following cases: // - |index_id| does not correspond to a 32-bit integer constant - // - the object being indexed is not a composite type + // - |object_type_id| must be a struct type // - the constant at |index_id| is out of bounds. // Otherwise, returns {true, value}, where value is the value of the constant // at |index_id|. - std::pair<bool, uint32_t> GetIndexValue(opt::IRContext* ir_context, - uint32_t index_id, - uint32_t object_type_id) const; + std::pair<bool, uint32_t> GetStructIndexValue(opt::IRContext* ir_context, + uint32_t index_id, + uint32_t object_type_id) const; // Returns true if |index_id| corresponds, in the given context, to a 32-bit // integer which can be used to index an object of the type specified by diff --git a/source/fuzz/transformation_add_bit_instruction_synonym.cpp b/source/fuzz/transformation_add_bit_instruction_synonym.cpp index 6cdfdfb6..636c0a38 100644 --- a/source/fuzz/transformation_add_bit_instruction_synonym.cpp +++ b/source/fuzz/transformation_add_bit_instruction_synonym.cpp @@ -21,9 +21,8 @@ namespace spvtools { namespace fuzz { TransformationAddBitInstructionSynonym::TransformationAddBitInstructionSynonym( - const spvtools::fuzz::protobufs::TransformationAddBitInstructionSynonym& - message) - : message_(message) {} + protobufs::TransformationAddBitInstructionSynonym message) + : message_(std::move(message)) {} TransformationAddBitInstructionSynonym::TransformationAddBitInstructionSynonym( const uint32_t instruction_result_id, @@ -40,20 +39,9 @@ bool TransformationAddBitInstructionSynonym::IsApplicable( auto instruction = ir_context->get_def_use_mgr()->GetDef(message_.instruction_result_id()); - // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3557): - // Right now we only support certain operations. When this issue is addressed - // the following conditional can use the function |spvOpcodeIsBit|. - // |instruction| must be defined and must be a supported bit instruction. - if (!instruction || (instruction->opcode() != SpvOpBitwiseOr && - instruction->opcode() != SpvOpBitwiseXor && - instruction->opcode() != SpvOpBitwiseAnd && - instruction->opcode() != SpvOpNot)) { - return false; - } - - // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3792): - // Right now, only integer operands are supported. - if (ir_context->get_type_mgr()->GetType(instruction->type_id())->AsVector()) { + // Checks on: only integer operands are supported, instructions are bitwise + // operations only. Signedness of the operands must be the same. + if (!IsInstructionSupported(ir_context, instruction)) { return false; } @@ -112,6 +100,65 @@ void TransformationAddBitInstructionSynonym::Apply( } } +bool TransformationAddBitInstructionSynonym::IsInstructionSupported( + opt::IRContext* ir_context, opt::Instruction* instruction) { + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3557): + // Right now we only support certain operations. When this issue is addressed + // the following conditional can use the function |spvOpcodeIsBit|. + // |instruction| must be defined and must be a supported bit instruction. + if (!instruction || (instruction->opcode() != SpvOpBitwiseOr && + instruction->opcode() != SpvOpBitwiseXor && + instruction->opcode() != SpvOpBitwiseAnd && + instruction->opcode() != SpvOpNot)) { + return false; + } + + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3792): + // Right now, only integer operands are supported. + if (ir_context->get_type_mgr()->GetType(instruction->type_id())->AsVector()) { + return false; + } + + if (instruction->opcode() == SpvOpNot) { + auto operand = instruction->GetInOperand(0).words[0]; + auto operand_inst = ir_context->get_def_use_mgr()->GetDef(operand); + auto operand_type = + ir_context->get_type_mgr()->GetType(operand_inst->type_id()); + auto operand_sign = operand_type->AsInteger()->IsSigned(); + + auto type_id_sign = ir_context->get_type_mgr() + ->GetType(instruction->type_id()) + ->AsInteger() + ->IsSigned(); + + return operand_sign == type_id_sign; + + } else { + // Other BitWise operations that takes two operands. + auto first_operand = instruction->GetInOperand(0).words[0]; + auto first_operand_inst = + ir_context->get_def_use_mgr()->GetDef(first_operand); + auto first_operand_type = + ir_context->get_type_mgr()->GetType(first_operand_inst->type_id()); + auto first_operand_sign = first_operand_type->AsInteger()->IsSigned(); + + auto second_operand = instruction->GetInOperand(1).words[0]; + auto second_operand_inst = + ir_context->get_def_use_mgr()->GetDef(second_operand); + auto second_operand_type = + ir_context->get_type_mgr()->GetType(second_operand_inst->type_id()); + auto second_operand_sign = second_operand_type->AsInteger()->IsSigned(); + + auto type_id_sign = ir_context->get_type_mgr() + ->GetType(instruction->type_id()) + ->AsInteger() + ->IsSigned(); + + return first_operand_sign == second_operand_sign && + first_operand_sign == type_id_sign; + } +} + protobufs::Transformation TransformationAddBitInstructionSynonym::ToMessage() const { protobufs::Transformation result; diff --git a/source/fuzz/transformation_add_bit_instruction_synonym.h b/source/fuzz/transformation_add_bit_instruction_synonym.h index ed1a0af5..40b947a2 100644 --- a/source/fuzz/transformation_add_bit_instruction_synonym.h +++ b/source/fuzz/transformation_add_bit_instruction_synonym.h @@ -103,7 +103,7 @@ namespace fuzz { class TransformationAddBitInstructionSynonym : public Transformation { public: explicit TransformationAddBitInstructionSynonym( - const protobufs::TransformationAddBitInstructionSynonym& message); + protobufs::TransformationAddBitInstructionSynonym message); TransformationAddBitInstructionSynonym( const uint32_t instruction_result_id, @@ -128,6 +128,14 @@ class TransformationAddBitInstructionSynonym : public Transformation { static uint32_t GetRequiredFreshIdCount(opt::IRContext* ir_context, opt::Instruction* bit_instruction); + // Returns true if: + // - A |bit_instruction| is one of OpBitwiseOr, OpBitwiseAnd, OpBitwiseXor or + // OpNot. + // - |bit_instruction|'s operands are scalars. + // - The operands have the same signedness. + static bool IsInstructionSupported(opt::IRContext* ir_context, + opt::Instruction* instruction); + private: protobufs::TransformationAddBitInstructionSynonym message_; diff --git a/source/fuzz/transformation_add_constant_boolean.cpp b/source/fuzz/transformation_add_constant_boolean.cpp index 937fdbcc..39354325 100644 --- a/source/fuzz/transformation_add_constant_boolean.cpp +++ b/source/fuzz/transformation_add_constant_boolean.cpp @@ -21,8 +21,8 @@ namespace spvtools { namespace fuzz { TransformationAddConstantBoolean::TransformationAddConstantBoolean( - const protobufs::TransformationAddConstantBoolean& message) - : message_(message) {} + protobufs::TransformationAddConstantBoolean message) + : message_(std::move(message)) {} TransformationAddConstantBoolean::TransformationAddConstantBoolean( uint32_t fresh_id, bool is_true, bool is_irrelevant) { @@ -42,14 +42,18 @@ void TransformationAddConstantBoolean::Apply( TransformationContext* transformation_context) const { // Add the boolean constant to the module, ensuring the module's id bound is // high enough. + auto new_instruction = MakeUnique<opt::Instruction>( + ir_context, message_.is_true() ? SpvOpConstantTrue : SpvOpConstantFalse, + fuzzerutil::MaybeGetBoolType(ir_context), message_.fresh_id(), + opt::Instruction::OperandList()); + auto new_instruction_ptr = new_instruction.get(); + ir_context->module()->AddGlobalValue(std::move(new_instruction)); fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); - ir_context->module()->AddGlobalValue( - message_.is_true() ? SpvOpConstantTrue : SpvOpConstantFalse, - message_.fresh_id(), fuzzerutil::MaybeGetBoolType(ir_context)); - // We have added an instruction to the module, so need to be careful about the - // validity of existing analyses. - ir_context->InvalidateAnalysesExceptFor( - opt::IRContext::Analysis::kAnalysisNone); + + // Inform the def-use manager about the new instruction. Invalidate the + // constant manager as we have added a new constant. + ir_context->get_def_use_mgr()->AnalyzeInstDef(new_instruction_ptr); + ir_context->InvalidateAnalyses(opt::IRContext::kAnalysisConstants); if (message_.is_irrelevant()) { transformation_context->GetFactManager()->AddFactIdIsIrrelevant( diff --git a/source/fuzz/transformation_add_constant_boolean.h b/source/fuzz/transformation_add_constant_boolean.h index d1d04efd..7f31471c 100644 --- a/source/fuzz/transformation_add_constant_boolean.h +++ b/source/fuzz/transformation_add_constant_boolean.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationAddConstantBoolean : public Transformation { public: explicit TransformationAddConstantBoolean( - const protobufs::TransformationAddConstantBoolean& message); + protobufs::TransformationAddConstantBoolean message); TransformationAddConstantBoolean(uint32_t fresh_id, bool is_true, bool is_irrelevant); diff --git a/source/fuzz/transformation_add_constant_composite.cpp b/source/fuzz/transformation_add_constant_composite.cpp index 02603210..e6cd5a96 100644 --- a/source/fuzz/transformation_add_constant_composite.cpp +++ b/source/fuzz/transformation_add_constant_composite.cpp @@ -22,9 +22,8 @@ namespace spvtools { namespace fuzz { TransformationAddConstantComposite::TransformationAddConstantComposite( - const spvtools::fuzz::protobufs::TransformationAddConstantComposite& - message) - : message_(message) {} + spvtools::fuzz::protobufs::TransformationAddConstantComposite message) + : message_(std::move(message)) {} TransformationAddConstantComposite::TransformationAddConstantComposite( uint32_t fresh_id, uint32_t type_id, @@ -120,14 +119,17 @@ void TransformationAddConstantComposite::Apply( for (auto constituent_id : message_.constituent_id()) { in_operands.push_back({SPV_OPERAND_TYPE_ID, {constituent_id}}); } - ir_context->module()->AddGlobalValue(MakeUnique<opt::Instruction>( + auto new_instruction = MakeUnique<opt::Instruction>( ir_context, SpvOpConstantComposite, message_.type_id(), - message_.fresh_id(), in_operands)); + message_.fresh_id(), in_operands); + auto new_instruction_ptr = new_instruction.get(); + ir_context->module()->AddGlobalValue(std::move(new_instruction)); fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); - // We have added an instruction to the module, so need to be careful about the - // validity of existing analyses. - ir_context->InvalidateAnalysesExceptFor( - opt::IRContext::Analysis::kAnalysisNone); + + // Inform the def-use manager of the new instruction. Invalidate the constant + // manager as we have added a new constant. + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr); + ir_context->InvalidateAnalyses(opt::IRContext::kAnalysisConstants); if (message_.is_irrelevant()) { transformation_context->GetFactManager()->AddFactIdIsIrrelevant( diff --git a/source/fuzz/transformation_add_constant_composite.h b/source/fuzz/transformation_add_constant_composite.h index 9e9222dc..94e7a927 100644 --- a/source/fuzz/transformation_add_constant_composite.h +++ b/source/fuzz/transformation_add_constant_composite.h @@ -28,7 +28,7 @@ namespace fuzz { class TransformationAddConstantComposite : public Transformation { public: explicit TransformationAddConstantComposite( - const protobufs::TransformationAddConstantComposite& message); + protobufs::TransformationAddConstantComposite message); TransformationAddConstantComposite( uint32_t fresh_id, uint32_t type_id, diff --git a/source/fuzz/transformation_add_constant_null.cpp b/source/fuzz/transformation_add_constant_null.cpp index 3c66ab10..32544e6d 100644 --- a/source/fuzz/transformation_add_constant_null.cpp +++ b/source/fuzz/transformation_add_constant_null.cpp @@ -20,8 +20,8 @@ namespace spvtools { namespace fuzz { TransformationAddConstantNull::TransformationAddConstantNull( - const spvtools::fuzz::protobufs::TransformationAddConstantNull& message) - : message_(message) {} + spvtools::fuzz::protobufs::TransformationAddConstantNull message) + : message_(std::move(message)) {} TransformationAddConstantNull::TransformationAddConstantNull(uint32_t fresh_id, uint32_t type_id) { @@ -46,14 +46,18 @@ bool TransformationAddConstantNull::IsApplicable( } void TransformationAddConstantNull::Apply( - opt::IRContext* context, TransformationContext* /*unused*/) const { - context->module()->AddGlobalValue(MakeUnique<opt::Instruction>( - context, SpvOpConstantNull, message_.type_id(), message_.fresh_id(), - opt::Instruction::OperandList())); - fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id()); - // We have added an instruction to the module, so need to be careful about the - // validity of existing analyses. - context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone); + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + auto new_instruction = MakeUnique<opt::Instruction>( + ir_context, SpvOpConstantNull, message_.type_id(), message_.fresh_id(), + opt::Instruction::OperandList()); + auto new_instruction_ptr = new_instruction.get(); + ir_context->module()->AddGlobalValue(std::move(new_instruction)); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + // Inform the def-use manager about the new instruction. Invalidate the + // constant manager as we have added a new constant. + ir_context->get_def_use_mgr()->AnalyzeInstDef(new_instruction_ptr); + ir_context->InvalidateAnalyses(opt::IRContext::kAnalysisConstants); } protobufs::Transformation TransformationAddConstantNull::ToMessage() const { diff --git a/source/fuzz/transformation_add_constant_null.h b/source/fuzz/transformation_add_constant_null.h index bd08b1da..bb1d1b7c 100644 --- a/source/fuzz/transformation_add_constant_null.h +++ b/source/fuzz/transformation_add_constant_null.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationAddConstantNull : public Transformation { public: explicit TransformationAddConstantNull( - const protobufs::TransformationAddConstantNull& message); + protobufs::TransformationAddConstantNull message); TransformationAddConstantNull(uint32_t fresh_id, uint32_t type_id); @@ -34,12 +34,12 @@ class TransformationAddConstantNull : public Transformation { // - |message_.type_id| must be the id of a type for which it is acceptable // to create a null constant bool IsApplicable( - opt::IRContext* context, + opt::IRContext* ir_context, const TransformationContext& transformation_context) const override; // Adds an OpConstantNull instruction to the module, with |message_.type_id| // as its type. The instruction has result id |message_.fresh_id|. - void Apply(opt::IRContext* context, + void Apply(opt::IRContext* ir_context, TransformationContext* transformation_context) const override; std::unordered_set<uint32_t> GetFreshIds() const override; diff --git a/source/fuzz/transformation_add_constant_scalar.cpp b/source/fuzz/transformation_add_constant_scalar.cpp index 9a6642a9..a2d95fb6 100644 --- a/source/fuzz/transformation_add_constant_scalar.cpp +++ b/source/fuzz/transformation_add_constant_scalar.cpp @@ -20,8 +20,8 @@ namespace spvtools { namespace fuzz { TransformationAddConstantScalar::TransformationAddConstantScalar( - const spvtools::fuzz::protobufs::TransformationAddConstantScalar& message) - : message_(message) {} + spvtools::fuzz::protobufs::TransformationAddConstantScalar message) + : message_(std::move(message)) {} TransformationAddConstantScalar::TransformationAddConstantScalar( uint32_t fresh_id, uint32_t type_id, const std::vector<uint32_t>& words, @@ -64,19 +64,21 @@ bool TransformationAddConstantScalar::IsApplicable( void TransformationAddConstantScalar::Apply( opt::IRContext* ir_context, TransformationContext* transformation_context) const { - ir_context->module()->AddGlobalValue(MakeUnique<opt::Instruction>( + auto new_instruction = MakeUnique<opt::Instruction>( ir_context, SpvOpConstant, message_.type_id(), message_.fresh_id(), opt::Instruction::OperandList( {{SPV_OPERAND_TYPE_LITERAL_INTEGER, std::vector<uint32_t>(message_.word().begin(), - message_.word().end())}}))); + message_.word().end())}})); + auto new_instruction_ptr = new_instruction.get(); + ir_context->module()->AddGlobalValue(std::move(new_instruction)); fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); - // We have added an instruction to the module, so need to be careful about the - // validity of existing analyses. - ir_context->InvalidateAnalysesExceptFor( - opt::IRContext::Analysis::kAnalysisNone); + // Inform the def-use manager about the new instruction. Invalidate the + // constant manager as we have added a new constant. + ir_context->get_def_use_mgr()->AnalyzeInstDef(new_instruction_ptr); + ir_context->InvalidateAnalyses(opt::IRContext::kAnalysisConstants); if (message_.is_irrelevant()) { transformation_context->GetFactManager()->AddFactIdIsIrrelevant( diff --git a/source/fuzz/transformation_add_constant_scalar.h b/source/fuzz/transformation_add_constant_scalar.h index 3f23907c..adb07355 100644 --- a/source/fuzz/transformation_add_constant_scalar.h +++ b/source/fuzz/transformation_add_constant_scalar.h @@ -28,7 +28,7 @@ namespace fuzz { class TransformationAddConstantScalar : public Transformation { public: explicit TransformationAddConstantScalar( - const protobufs::TransformationAddConstantScalar& message); + protobufs::TransformationAddConstantScalar message); TransformationAddConstantScalar(uint32_t fresh_id, uint32_t type_id, const std::vector<uint32_t>& words, diff --git a/source/fuzz/transformation_add_copy_memory.cpp b/source/fuzz/transformation_add_copy_memory.cpp index 44bb9c57..5eb4bdc0 100644 --- a/source/fuzz/transformation_add_copy_memory.cpp +++ b/source/fuzz/transformation_add_copy_memory.cpp @@ -22,8 +22,8 @@ namespace spvtools { namespace fuzz { TransformationAddCopyMemory::TransformationAddCopyMemory( - const protobufs::TransformationAddCopyMemory& message) - : message_(message) {} + protobufs::TransformationAddCopyMemory message) + : message_(std::move(message)) {} TransformationAddCopyMemory::TransformationAddCopyMemory( const protobufs::InstructionDescriptor& instruction_descriptor, @@ -99,15 +99,8 @@ void TransformationAddCopyMemory::Apply( auto* insert_before_inst = FindInstruction(message_.instruction_descriptor(), ir_context); assert(insert_before_inst); - - auto insert_before_iter = fuzzerutil::GetIteratorForInstruction( - ir_context->get_instr_block(insert_before_inst), insert_before_inst); - - insert_before_iter.InsertBefore(MakeUnique<opt::Instruction>( - ir_context, SpvOpCopyMemory, 0, 0, - opt::Instruction::OperandList{ - {SPV_OPERAND_TYPE_ID, {message_.fresh_id()}}, - {SPV_OPERAND_TYPE_ID, {message_.source_id()}}})); + opt::BasicBlock* enclosing_block = + ir_context->get_instr_block(insert_before_inst); // Add global or local variable to copy memory into. auto storage_class = static_cast<SpvStorageClass>(message_.storage_class()); @@ -118,23 +111,35 @@ void TransformationAddCopyMemory::Apply( storage_class); if (storage_class == SpvStorageClassPrivate) { - fuzzerutil::AddGlobalVariable(ir_context, message_.fresh_id(), type_id, - storage_class, message_.initializer_id()); + opt::Instruction* new_global = + fuzzerutil::AddGlobalVariable(ir_context, message_.fresh_id(), type_id, + storage_class, message_.initializer_id()); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_global); } else { assert(storage_class == SpvStorageClassFunction && "Storage class can be either Private or Function"); - fuzzerutil::AddLocalVariable(ir_context, message_.fresh_id(), type_id, - ir_context->get_instr_block(insert_before_inst) - ->GetParent() - ->result_id(), - message_.initializer_id()); + opt::Function* enclosing_function = enclosing_block->GetParent(); + opt::Instruction* new_local = fuzzerutil::AddLocalVariable( + ir_context, message_.fresh_id(), type_id, + enclosing_function->result_id(), message_.initializer_id()); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_local); + ir_context->set_instr_block(new_local, &*enclosing_function->entry()); } - fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + auto insert_before_iter = fuzzerutil::GetIteratorForInstruction( + enclosing_block, insert_before_inst); + + auto new_instruction = MakeUnique<opt::Instruction>( + ir_context, SpvOpCopyMemory, 0, 0, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.fresh_id()}}, + {SPV_OPERAND_TYPE_ID, {message_.source_id()}}}); + auto new_instruction_ptr = new_instruction.get(); + insert_before_iter.InsertBefore(std::move(new_instruction)); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr); + ir_context->set_instr_block(new_instruction_ptr, enclosing_block); - // Make sure our changes are analyzed - ir_context->InvalidateAnalysesExceptFor( - opt::IRContext::Analysis::kAnalysisNone); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); // Even though the copy memory instruction will - at least temporarily - lead // to the destination and source pointers referring to identical values, this diff --git a/source/fuzz/transformation_add_copy_memory.h b/source/fuzz/transformation_add_copy_memory.h index cc42f1e2..b25652fd 100644 --- a/source/fuzz/transformation_add_copy_memory.h +++ b/source/fuzz/transformation_add_copy_memory.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationAddCopyMemory : public Transformation { public: explicit TransformationAddCopyMemory( - const protobufs::TransformationAddCopyMemory& message); + protobufs::TransformationAddCopyMemory message); TransformationAddCopyMemory( const protobufs::InstructionDescriptor& instruction_descriptor, diff --git a/source/fuzz/transformation_add_dead_block.cpp b/source/fuzz/transformation_add_dead_block.cpp index 5dce3569..82e8cd8f 100644 --- a/source/fuzz/transformation_add_dead_block.cpp +++ b/source/fuzz/transformation_add_dead_block.cpp @@ -20,8 +20,8 @@ namespace spvtools { namespace fuzz { TransformationAddDeadBlock::TransformationAddDeadBlock( - const spvtools::fuzz::protobufs::TransformationAddDeadBlock& message) - : message_(message) {} + protobufs::TransformationAddDeadBlock message) + : message_(std::move(message)) {} TransformationAddDeadBlock::TransformationAddDeadBlock(uint32_t fresh_id, uint32_t existing_block, diff --git a/source/fuzz/transformation_add_dead_block.h b/source/fuzz/transformation_add_dead_block.h index 50af6b0b..d8b3c2ae 100644 --- a/source/fuzz/transformation_add_dead_block.h +++ b/source/fuzz/transformation_add_dead_block.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationAddDeadBlock : public Transformation { public: explicit TransformationAddDeadBlock( - const protobufs::TransformationAddDeadBlock& message); + protobufs::TransformationAddDeadBlock message); TransformationAddDeadBlock(uint32_t fresh_id, uint32_t existing_block, bool condition_value); diff --git a/source/fuzz/transformation_add_dead_break.cpp b/source/fuzz/transformation_add_dead_break.cpp index bc938d40..ad46ce7f 100644 --- a/source/fuzz/transformation_add_dead_break.cpp +++ b/source/fuzz/transformation_add_dead_break.cpp @@ -24,8 +24,8 @@ namespace spvtools { namespace fuzz { TransformationAddDeadBreak::TransformationAddDeadBreak( - const spvtools::fuzz::protobufs::TransformationAddDeadBreak& message) - : message_(message) {} + protobufs::TransformationAddDeadBreak message) + : message_(std::move(message)) {} TransformationAddDeadBreak::TransformationAddDeadBreak( uint32_t from_block, uint32_t to_block, bool break_condition_value, @@ -112,9 +112,10 @@ bool TransformationAddDeadBreak::IsApplicable( const TransformationContext& transformation_context) const { // First, we check that a constant with the same value as // |message_.break_condition_value| is present. - if (!fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context, - message_.break_condition_value(), - false)) { + const auto bool_id = + fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context, + message_.break_condition_value(), false); + if (!bool_id) { // The required constant is not present, so the transformation cannot be // applied. return false; @@ -171,25 +172,23 @@ bool TransformationAddDeadBreak::IsApplicable( } // Adding the dead break is only valid if SPIR-V rules related to dominance - // hold. Rather than checking these rules explicitly, we defer to the - // validator. We make a clone of the module, apply the transformation to the - // clone, and check whether the transformed clone is valid. - // - // In principle some of the above checks could be removed, with more reliance - // being places on the validator. This should be revisited if we are sure - // the validator is complete with respect to checking structured control flow - // rules. - auto cloned_context = fuzzerutil::CloneIRContext(ir_context); - ApplyImpl(cloned_context.get(), transformation_context); - return fuzzerutil::IsValid(cloned_context.get(), - transformation_context.GetValidatorOptions(), - fuzzerutil::kSilentMessageConsumer); + // hold. + return fuzzerutil::NewTerminatorPreservesDominationRules( + ir_context, message_.from_block(), + fuzzerutil::CreateUnreachableEdgeInstruction( + ir_context, message_.from_block(), message_.to_block(), bool_id)); } void TransformationAddDeadBreak::Apply( opt::IRContext* ir_context, TransformationContext* transformation_context) const { - ApplyImpl(ir_context, *transformation_context); + fuzzerutil::AddUnreachableEdgeAndUpdateOpPhis( + ir_context, ir_context->cfg()->block(message_.from_block()), + ir_context->cfg()->block(message_.to_block()), + fuzzerutil::MaybeGetBoolConstant(ir_context, *transformation_context, + message_.break_condition_value(), false), + message_.phi_id()); + // Invalidate all analyses ir_context->InvalidateAnalysesExceptFor( opt::IRContext::Analysis::kAnalysisNone); @@ -201,17 +200,6 @@ protobufs::Transformation TransformationAddDeadBreak::ToMessage() const { return result; } -void TransformationAddDeadBreak::ApplyImpl( - spvtools::opt::IRContext* ir_context, - const TransformationContext& transformation_context) const { - fuzzerutil::AddUnreachableEdgeAndUpdateOpPhis( - ir_context, ir_context->cfg()->block(message_.from_block()), - ir_context->cfg()->block(message_.to_block()), - fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context, - message_.break_condition_value(), false), - message_.phi_id()); -} - std::unordered_set<uint32_t> TransformationAddDeadBreak::GetFreshIds() const { return std::unordered_set<uint32_t>(); } diff --git a/source/fuzz/transformation_add_dead_break.h b/source/fuzz/transformation_add_dead_break.h index afb8dc7b..8c1ab4a4 100644 --- a/source/fuzz/transformation_add_dead_break.h +++ b/source/fuzz/transformation_add_dead_break.h @@ -28,7 +28,7 @@ namespace fuzz { class TransformationAddDeadBreak : public Transformation { public: explicit TransformationAddDeadBreak( - const protobufs::TransformationAddDeadBreak& message); + protobufs::TransformationAddDeadBreak message); TransformationAddDeadBreak(uint32_t from_block, uint32_t to_block, bool break_condition_value, @@ -71,15 +71,6 @@ class TransformationAddDeadBreak : public Transformation { bool AddingBreakRespectsStructuredControlFlow(opt::IRContext* ir_context, opt::BasicBlock* bb_from) const; - // Used by 'Apply' to actually apply the transformation to the module of - // interest, and by 'IsApplicable' to do a dry-run of the transformation on a - // cloned module, in order to check that the transformation leads to a valid - // module. This is only invoked by 'IsApplicable' after certain basic - // applicability checks have been made, ensuring that the invocation of this - // method is legal. - void ApplyImpl(opt::IRContext* ir_context, - const TransformationContext& transformation_context) const; - protobufs::TransformationAddDeadBreak message_; }; diff --git a/source/fuzz/transformation_add_dead_continue.cpp b/source/fuzz/transformation_add_dead_continue.cpp index 18b3c39f..be6294e8 100644 --- a/source/fuzz/transformation_add_dead_continue.cpp +++ b/source/fuzz/transformation_add_dead_continue.cpp @@ -20,8 +20,8 @@ namespace spvtools { namespace fuzz { TransformationAddDeadContinue::TransformationAddDeadContinue( - const spvtools::fuzz::protobufs::TransformationAddDeadContinue& message) - : message_(message) {} + protobufs::TransformationAddDeadContinue message) + : message_(std::move(message)) {} TransformationAddDeadContinue::TransformationAddDeadContinue( uint32_t from_block, bool continue_condition_value, @@ -38,9 +38,10 @@ bool TransformationAddDeadContinue::IsApplicable( const TransformationContext& transformation_context) const { // First, we check that a constant with the same value as // |message_.continue_condition_value| is present. - if (!fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context, - message_.continue_condition_value(), - false)) { + const auto bool_id = fuzzerutil::MaybeGetBoolConstant( + ir_context, transformation_context, message_.continue_condition_value(), + false); + if (!bool_id) { // The required constant is not present, so the transformation cannot be // applied. return false; @@ -111,39 +112,16 @@ bool TransformationAddDeadContinue::IsApplicable( } // Adding the dead break is only valid if SPIR-V rules related to dominance - // hold. Rather than checking these rules explicitly, we defer to the - // validator. We make a clone of the module, apply the transformation to the - // clone, and check whether the transformed clone is valid. - // - // In principle some of the above checks could be removed, with more reliance - // being placed on the validator. This should be revisited if we are sure - // the validator is complete with respect to checking structured control flow - // rules. - auto cloned_context = fuzzerutil::CloneIRContext(ir_context); - ApplyImpl(cloned_context.get(), transformation_context); - return fuzzerutil::IsValid(cloned_context.get(), - transformation_context.GetValidatorOptions(), - fuzzerutil::kSilentMessageConsumer); + // hold. + return fuzzerutil::NewTerminatorPreservesDominationRules( + ir_context, message_.from_block(), + fuzzerutil::CreateUnreachableEdgeInstruction( + ir_context, message_.from_block(), continue_block, bool_id)); } void TransformationAddDeadContinue::Apply( opt::IRContext* ir_context, TransformationContext* transformation_context) const { - ApplyImpl(ir_context, *transformation_context); - // Invalidate all analyses - ir_context->InvalidateAnalysesExceptFor( - opt::IRContext::Analysis::kAnalysisNone); -} - -protobufs::Transformation TransformationAddDeadContinue::ToMessage() const { - protobufs::Transformation result; - *result.mutable_add_dead_continue() = message_; - return result; -} - -void TransformationAddDeadContinue::ApplyImpl( - spvtools::opt::IRContext* ir_context, - const TransformationContext& transformation_context) const { auto bb_from = ir_context->cfg()->block(message_.from_block()); auto continue_block = bb_from->IsLoopHeader() @@ -153,10 +131,20 @@ void TransformationAddDeadContinue::ApplyImpl( assert(continue_block && "message_.from_block must be in a loop."); fuzzerutil::AddUnreachableEdgeAndUpdateOpPhis( ir_context, bb_from, ir_context->cfg()->block(continue_block), - fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context, + fuzzerutil::MaybeGetBoolConstant(ir_context, *transformation_context, message_.continue_condition_value(), false), message_.phi_id()); + + // Invalidate all analyses + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +protobufs::Transformation TransformationAddDeadContinue::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_dead_continue() = message_; + return result; } std::unordered_set<uint32_t> TransformationAddDeadContinue::GetFreshIds() diff --git a/source/fuzz/transformation_add_dead_continue.h b/source/fuzz/transformation_add_dead_continue.h index 27527e78..9463aebc 100644 --- a/source/fuzz/transformation_add_dead_continue.h +++ b/source/fuzz/transformation_add_dead_continue.h @@ -28,7 +28,7 @@ namespace fuzz { class TransformationAddDeadContinue : public Transformation { public: explicit TransformationAddDeadContinue( - const protobufs::TransformationAddDeadContinue& message); + protobufs::TransformationAddDeadContinue message); TransformationAddDeadContinue(uint32_t from_block, bool continue_condition_value, @@ -68,15 +68,6 @@ class TransformationAddDeadContinue : public Transformation { protobufs::Transformation ToMessage() const override; private: - // Used by 'Apply' to actually apply the transformation to the module of - // interest, and by 'IsApplicable' to do a dry-run of the transformation on a - // cloned module, in order to check that the transformation leads to a valid - // module. This is only invoked by 'IsApplicable' after certain basic - // applicability checks have been made, ensuring that the invocation of this - // method is legal. - void ApplyImpl(opt::IRContext* ir_context, - const TransformationContext& transformation_context) const; - protobufs::TransformationAddDeadContinue message_; }; diff --git a/source/fuzz/transformation_add_early_terminator_wrapper.cpp b/source/fuzz/transformation_add_early_terminator_wrapper.cpp index 9f86070f..547398aa 100644 --- a/source/fuzz/transformation_add_early_terminator_wrapper.cpp +++ b/source/fuzz/transformation_add_early_terminator_wrapper.cpp @@ -22,9 +22,8 @@ namespace fuzz { TransformationAddEarlyTerminatorWrapper:: TransformationAddEarlyTerminatorWrapper( - const spvtools::fuzz::protobufs:: - TransformationAddEarlyTerminatorWrapper& message) - : message_(message) {} + protobufs::TransformationAddEarlyTerminatorWrapper message) + : message_(std::move(message)) {} TransformationAddEarlyTerminatorWrapper:: TransformationAddEarlyTerminatorWrapper(uint32_t function_fresh_id, diff --git a/source/fuzz/transformation_add_early_terminator_wrapper.h b/source/fuzz/transformation_add_early_terminator_wrapper.h index 273037e5..97cc527c 100644 --- a/source/fuzz/transformation_add_early_terminator_wrapper.h +++ b/source/fuzz/transformation_add_early_terminator_wrapper.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationAddEarlyTerminatorWrapper : public Transformation { public: explicit TransformationAddEarlyTerminatorWrapper( - const protobufs::TransformationAddEarlyTerminatorWrapper& message); + protobufs::TransformationAddEarlyTerminatorWrapper message); TransformationAddEarlyTerminatorWrapper(uint32_t function_fresh_id, uint32_t label_fresh_id, diff --git a/source/fuzz/transformation_add_function.cpp b/source/fuzz/transformation_add_function.cpp index 9799373d..06cd6572 100644 --- a/source/fuzz/transformation_add_function.cpp +++ b/source/fuzz/transformation_add_function.cpp @@ -21,8 +21,8 @@ namespace spvtools { namespace fuzz { TransformationAddFunction::TransformationAddFunction( - const spvtools::fuzz::protobufs::TransformationAddFunction& message) - : message_(message) {} + protobufs::TransformationAddFunction message) + : message_(std::move(message)) {} TransformationAddFunction::TransformationAddFunction( const std::vector<protobufs::Instruction>& instructions) { diff --git a/source/fuzz/transformation_add_function.h b/source/fuzz/transformation_add_function.h index e5381d14..c41eee33 100644 --- a/source/fuzz/transformation_add_function.h +++ b/source/fuzz/transformation_add_function.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationAddFunction : public Transformation { public: explicit TransformationAddFunction( - const protobufs::TransformationAddFunction& message); + protobufs::TransformationAddFunction message); // Creates a transformation to add a non live-safe function. explicit TransformationAddFunction( diff --git a/source/fuzz/transformation_add_global_undef.cpp b/source/fuzz/transformation_add_global_undef.cpp index 7a90b821..eb390ea0 100644 --- a/source/fuzz/transformation_add_global_undef.cpp +++ b/source/fuzz/transformation_add_global_undef.cpp @@ -20,8 +20,8 @@ namespace spvtools { namespace fuzz { TransformationAddGlobalUndef::TransformationAddGlobalUndef( - const spvtools::fuzz::protobufs::TransformationAddGlobalUndef& message) - : message_(message) {} + spvtools::fuzz::protobufs::TransformationAddGlobalUndef message) + : message_(std::move(message)) {} TransformationAddGlobalUndef::TransformationAddGlobalUndef(uint32_t fresh_id, uint32_t type_id) { @@ -42,14 +42,14 @@ bool TransformationAddGlobalUndef::IsApplicable( void TransformationAddGlobalUndef::Apply( opt::IRContext* ir_context, TransformationContext* /*unused*/) const { - ir_context->module()->AddGlobalValue(MakeUnique<opt::Instruction>( + auto new_instruction = MakeUnique<opt::Instruction>( ir_context, SpvOpUndef, message_.type_id(), message_.fresh_id(), - opt::Instruction::OperandList())); + opt::Instruction::OperandList()); + auto new_instruction_ptr = new_instruction.get(); + ir_context->module()->AddGlobalValue(std::move(new_instruction)); fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); - // We have added an instruction to the module, so need to be careful about the - // validity of existing analyses. - ir_context->InvalidateAnalysesExceptFor( - opt::IRContext::Analysis::kAnalysisNone); + // Inform the def-use manager about the new instruction. + ir_context->get_def_use_mgr()->AnalyzeInstDef(new_instruction_ptr); } protobufs::Transformation TransformationAddGlobalUndef::ToMessage() const { diff --git a/source/fuzz/transformation_add_global_undef.h b/source/fuzz/transformation_add_global_undef.h index 717dc9ad..37542c3f 100644 --- a/source/fuzz/transformation_add_global_undef.h +++ b/source/fuzz/transformation_add_global_undef.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationAddGlobalUndef : public Transformation { public: explicit TransformationAddGlobalUndef( - const protobufs::TransformationAddGlobalUndef& message); + protobufs::TransformationAddGlobalUndef message); TransformationAddGlobalUndef(uint32_t fresh_id, uint32_t type_id); diff --git a/source/fuzz/transformation_add_global_variable.cpp b/source/fuzz/transformation_add_global_variable.cpp index dd04e48a..814d01b3 100644 --- a/source/fuzz/transformation_add_global_variable.cpp +++ b/source/fuzz/transformation_add_global_variable.cpp @@ -20,8 +20,8 @@ namespace spvtools { namespace fuzz { TransformationAddGlobalVariable::TransformationAddGlobalVariable( - const spvtools::fuzz::protobufs::TransformationAddGlobalVariable& message) - : message_(message) {} + spvtools::fuzz::protobufs::TransformationAddGlobalVariable message) + : message_(std::move(message)) {} TransformationAddGlobalVariable::TransformationAddGlobalVariable( uint32_t fresh_id, uint32_t type_id, SpvStorageClass storage_class, @@ -93,15 +93,13 @@ bool TransformationAddGlobalVariable::IsApplicable( void TransformationAddGlobalVariable::Apply( opt::IRContext* ir_context, TransformationContext* transformation_context) const { - fuzzerutil::AddGlobalVariable( + opt::Instruction* new_instruction = fuzzerutil::AddGlobalVariable( ir_context, message_.fresh_id(), message_.type_id(), static_cast<SpvStorageClass>(message_.storage_class()), message_.initializer_id()); - // We have added an instruction to the module, so need to be careful about the - // validity of existing analyses. - ir_context->InvalidateAnalysesExceptFor( - opt::IRContext::Analysis::kAnalysisNone); + // Inform the def-use manager about the new instruction. + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction); if (message_.value_is_irrelevant()) { transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant( diff --git a/source/fuzz/transformation_add_global_variable.h b/source/fuzz/transformation_add_global_variable.h index 8d46edb7..d74d48a2 100644 --- a/source/fuzz/transformation_add_global_variable.h +++ b/source/fuzz/transformation_add_global_variable.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationAddGlobalVariable : public Transformation { public: explicit TransformationAddGlobalVariable( - const protobufs::TransformationAddGlobalVariable& message); + protobufs::TransformationAddGlobalVariable message); TransformationAddGlobalVariable(uint32_t fresh_id, uint32_t type_id, SpvStorageClass storage_class, diff --git a/source/fuzz/transformation_add_image_sample_unused_components.cpp b/source/fuzz/transformation_add_image_sample_unused_components.cpp index ab48f0b6..1ead82bc 100644 --- a/source/fuzz/transformation_add_image_sample_unused_components.cpp +++ b/source/fuzz/transformation_add_image_sample_unused_components.cpp @@ -22,9 +22,8 @@ namespace fuzz { TransformationAddImageSampleUnusedComponents:: TransformationAddImageSampleUnusedComponents( - const spvtools::fuzz::protobufs:: - TransformationAddImageSampleUnusedComponents& message) - : message_(message) {} + protobufs::TransformationAddImageSampleUnusedComponents message) + : message_(std::move(message)) {} TransformationAddImageSampleUnusedComponents:: TransformationAddImageSampleUnusedComponents( diff --git a/source/fuzz/transformation_add_image_sample_unused_components.h b/source/fuzz/transformation_add_image_sample_unused_components.h index 7486c760..7b13f9f6 100644 --- a/source/fuzz/transformation_add_image_sample_unused_components.h +++ b/source/fuzz/transformation_add_image_sample_unused_components.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationAddImageSampleUnusedComponents : public Transformation { public: explicit TransformationAddImageSampleUnusedComponents( - const protobufs::TransformationAddImageSampleUnusedComponents& message); + protobufs::TransformationAddImageSampleUnusedComponents message); TransformationAddImageSampleUnusedComponents( uint32_t coordinate_with_unused_components_id, diff --git a/source/fuzz/transformation_add_local_variable.cpp b/source/fuzz/transformation_add_local_variable.cpp index 0a7a3dac..21768d22 100644 --- a/source/fuzz/transformation_add_local_variable.cpp +++ b/source/fuzz/transformation_add_local_variable.cpp @@ -20,8 +20,8 @@ namespace spvtools { namespace fuzz { TransformationAddLocalVariable::TransformationAddLocalVariable( - const spvtools::fuzz::protobufs::TransformationAddLocalVariable& message) - : message_(message) {} + spvtools::fuzz::protobufs::TransformationAddLocalVariable message) + : message_(std::move(message)) {} TransformationAddLocalVariable::TransformationAddLocalVariable( uint32_t fresh_id, uint32_t type_id, uint32_t function_id, @@ -70,11 +70,17 @@ bool TransformationAddLocalVariable::IsApplicable( void TransformationAddLocalVariable::Apply( opt::IRContext* ir_context, TransformationContext* transformation_context) const { - fuzzerutil::AddLocalVariable(ir_context, message_.fresh_id(), - message_.type_id(), message_.function_id(), - message_.initializer_id()); + opt::Instruction* new_instruction = fuzzerutil::AddLocalVariable( + ir_context, message_.fresh_id(), message_.type_id(), + message_.function_id(), message_.initializer_id()); - ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + // Inform the def-use manager about the new instruction. + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction); + ir_context->set_instr_block( + new_instruction, + fuzzerutil::FindFunction(ir_context, message_.function_id()) + ->entry() + .get()); if (message_.value_is_irrelevant()) { transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant( diff --git a/source/fuzz/transformation_add_local_variable.h b/source/fuzz/transformation_add_local_variable.h index 963079f5..b008a1c0 100644 --- a/source/fuzz/transformation_add_local_variable.h +++ b/source/fuzz/transformation_add_local_variable.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationAddLocalVariable : public Transformation { public: explicit TransformationAddLocalVariable( - const protobufs::TransformationAddLocalVariable& message); + protobufs::TransformationAddLocalVariable message); TransformationAddLocalVariable(uint32_t fresh_id, uint32_t type_id, uint32_t function_id, uint32_t initializer_id, diff --git a/source/fuzz/transformation_add_loop_preheader.cpp b/source/fuzz/transformation_add_loop_preheader.cpp index 3d50fa92..71ab18da 100644 --- a/source/fuzz/transformation_add_loop_preheader.cpp +++ b/source/fuzz/transformation_add_loop_preheader.cpp @@ -20,8 +20,8 @@ namespace spvtools { namespace fuzz { TransformationAddLoopPreheader::TransformationAddLoopPreheader( - const protobufs::TransformationAddLoopPreheader& message) - : message_(message) {} + protobufs::TransformationAddLoopPreheader message) + : message_(std::move(message)) {} TransformationAddLoopPreheader::TransformationAddLoopPreheader( uint32_t loop_header_block, uint32_t fresh_id, diff --git a/source/fuzz/transformation_add_loop_preheader.h b/source/fuzz/transformation_add_loop_preheader.h index 05448f3f..9d2c565f 100644 --- a/source/fuzz/transformation_add_loop_preheader.h +++ b/source/fuzz/transformation_add_loop_preheader.h @@ -23,7 +23,7 @@ namespace fuzz { class TransformationAddLoopPreheader : public Transformation { public: explicit TransformationAddLoopPreheader( - const protobufs::TransformationAddLoopPreheader& message); + protobufs::TransformationAddLoopPreheader message); TransformationAddLoopPreheader(uint32_t loop_header_block, uint32_t fresh_id, std::vector<uint32_t> phi_id); diff --git a/source/fuzz/transformation_add_loop_to_create_int_constant_synonym.cpp b/source/fuzz/transformation_add_loop_to_create_int_constant_synonym.cpp index 45d3fc85..657fafa4 100644 --- a/source/fuzz/transformation_add_loop_to_create_int_constant_synonym.cpp +++ b/source/fuzz/transformation_add_loop_to_create_int_constant_synonym.cpp @@ -23,9 +23,8 @@ uint32_t kMaxNumOfIterations = 32; TransformationAddLoopToCreateIntConstantSynonym:: TransformationAddLoopToCreateIntConstantSynonym( - const protobufs::TransformationAddLoopToCreateIntConstantSynonym& - message) - : message_(message) {} + protobufs::TransformationAddLoopToCreateIntConstantSynonym message) + : message_(std::move(message)) {} TransformationAddLoopToCreateIntConstantSynonym:: TransformationAddLoopToCreateIntConstantSynonym( diff --git a/source/fuzz/transformation_add_loop_to_create_int_constant_synonym.h b/source/fuzz/transformation_add_loop_to_create_int_constant_synonym.h index 67c3bcd2..a6dfe63a 100644 --- a/source/fuzz/transformation_add_loop_to_create_int_constant_synonym.h +++ b/source/fuzz/transformation_add_loop_to_create_int_constant_synonym.h @@ -22,8 +22,7 @@ namespace fuzz { class TransformationAddLoopToCreateIntConstantSynonym : public Transformation { public: explicit TransformationAddLoopToCreateIntConstantSynonym( - const protobufs::TransformationAddLoopToCreateIntConstantSynonym& - message); + protobufs::TransformationAddLoopToCreateIntConstantSynonym message); TransformationAddLoopToCreateIntConstantSynonym( uint32_t constant_id, uint32_t initial_val_id, uint32_t step_val_id, diff --git a/source/fuzz/transformation_add_no_contraction_decoration.cpp b/source/fuzz/transformation_add_no_contraction_decoration.cpp index 29a871df..992a216b 100644 --- a/source/fuzz/transformation_add_no_contraction_decoration.cpp +++ b/source/fuzz/transformation_add_no_contraction_decoration.cpp @@ -21,9 +21,8 @@ namespace fuzz { TransformationAddNoContractionDecoration:: TransformationAddNoContractionDecoration( - const spvtools::fuzz::protobufs:: - TransformationAddNoContractionDecoration& message) - : message_(message) {} + protobufs::TransformationAddNoContractionDecoration message) + : message_(std::move(message)) {} TransformationAddNoContractionDecoration:: TransformationAddNoContractionDecoration(uint32_t result_id) { diff --git a/source/fuzz/transformation_add_no_contraction_decoration.h b/source/fuzz/transformation_add_no_contraction_decoration.h index f5a34e81..2f78d429 100644 --- a/source/fuzz/transformation_add_no_contraction_decoration.h +++ b/source/fuzz/transformation_add_no_contraction_decoration.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationAddNoContractionDecoration : public Transformation { public: explicit TransformationAddNoContractionDecoration( - const protobufs::TransformationAddNoContractionDecoration& message); + protobufs::TransformationAddNoContractionDecoration message); explicit TransformationAddNoContractionDecoration(uint32_t fresh_id); diff --git a/source/fuzz/transformation_add_opphi_synonym.cpp b/source/fuzz/transformation_add_opphi_synonym.cpp index 227c4338..3c4698a7 100644 --- a/source/fuzz/transformation_add_opphi_synonym.cpp +++ b/source/fuzz/transformation_add_opphi_synonym.cpp @@ -19,8 +19,8 @@ namespace spvtools { namespace fuzz { TransformationAddOpPhiSynonym::TransformationAddOpPhiSynonym( - const protobufs::TransformationAddOpPhiSynonym& message) - : message_(message) {} + protobufs::TransformationAddOpPhiSynonym message) + : message_(std::move(message)) {} TransformationAddOpPhiSynonym::TransformationAddOpPhiSynonym( uint32_t block_id, const std::map<uint32_t, uint32_t>& preds_to_ids, diff --git a/source/fuzz/transformation_add_opphi_synonym.h b/source/fuzz/transformation_add_opphi_synonym.h index 3b68abee..39ebea89 100644 --- a/source/fuzz/transformation_add_opphi_synonym.h +++ b/source/fuzz/transformation_add_opphi_synonym.h @@ -22,7 +22,7 @@ namespace fuzz { class TransformationAddOpPhiSynonym : public Transformation { public: explicit TransformationAddOpPhiSynonym( - const protobufs::TransformationAddOpPhiSynonym& message); + protobufs::TransformationAddOpPhiSynonym message); TransformationAddOpPhiSynonym( uint32_t block_id, const std::map<uint32_t, uint32_t>& preds_to_ids, diff --git a/source/fuzz/transformation_add_parameter.cpp b/source/fuzz/transformation_add_parameter.cpp index 9ed0bfb4..48de3e83 100644 --- a/source/fuzz/transformation_add_parameter.cpp +++ b/source/fuzz/transformation_add_parameter.cpp @@ -20,8 +20,8 @@ namespace spvtools { namespace fuzz { TransformationAddParameter::TransformationAddParameter( - const protobufs::TransformationAddParameter& message) - : message_(message) {} + protobufs::TransformationAddParameter message) + : message_(std::move(message)) {} TransformationAddParameter::TransformationAddParameter( uint32_t function_id, uint32_t parameter_fresh_id, diff --git a/source/fuzz/transformation_add_parameter.h b/source/fuzz/transformation_add_parameter.h index a33521da..0bc096e7 100644 --- a/source/fuzz/transformation_add_parameter.h +++ b/source/fuzz/transformation_add_parameter.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationAddParameter : public Transformation { public: explicit TransformationAddParameter( - const protobufs::TransformationAddParameter& message); + protobufs::TransformationAddParameter message); TransformationAddParameter(uint32_t function_id, uint32_t parameter_fresh_id, uint32_t parameter_type_id, diff --git a/source/fuzz/transformation_add_relaxed_decoration.cpp b/source/fuzz/transformation_add_relaxed_decoration.cpp index 7b513053..b66a1a83 100644 --- a/source/fuzz/transformation_add_relaxed_decoration.cpp +++ b/source/fuzz/transformation_add_relaxed_decoration.cpp @@ -20,9 +20,8 @@ namespace spvtools { namespace fuzz { TransformationAddRelaxedDecoration::TransformationAddRelaxedDecoration( - const spvtools::fuzz::protobufs::TransformationAddRelaxedDecoration& - message) - : message_(message) {} + protobufs::TransformationAddRelaxedDecoration message) + : message_(std::move(message)) {} TransformationAddRelaxedDecoration::TransformationAddRelaxedDecoration( uint32_t result_id) { diff --git a/source/fuzz/transformation_add_relaxed_decoration.h b/source/fuzz/transformation_add_relaxed_decoration.h index 3f8bf3ed..c0163497 100644 --- a/source/fuzz/transformation_add_relaxed_decoration.h +++ b/source/fuzz/transformation_add_relaxed_decoration.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationAddRelaxedDecoration : public Transformation { public: explicit TransformationAddRelaxedDecoration( - const protobufs::TransformationAddRelaxedDecoration& message); + protobufs::TransformationAddRelaxedDecoration message); explicit TransformationAddRelaxedDecoration(uint32_t fresh_id); diff --git a/source/fuzz/transformation_add_synonym.cpp b/source/fuzz/transformation_add_synonym.cpp index a516916b..a1949fbc 100644 --- a/source/fuzz/transformation_add_synonym.cpp +++ b/source/fuzz/transformation_add_synonym.cpp @@ -102,14 +102,19 @@ void TransformationAddSynonym::Apply( opt::IRContext* ir_context, TransformationContext* transformation_context) const { // Add a synonymous instruction. - FindInstruction(message_.insert_before(), ir_context) - ->InsertBefore( - MakeSynonymousInstruction(ir_context, *transformation_context)); + auto new_instruction = + MakeSynonymousInstruction(ir_context, *transformation_context); + auto new_instruction_ptr = new_instruction.get(); + auto insert_before = FindInstruction(message_.insert_before(), ir_context); + insert_before->InsertBefore(std::move(new_instruction)); fuzzerutil::UpdateModuleIdBound(ir_context, message_.synonym_fresh_id()); - ir_context->InvalidateAnalysesExceptFor( - opt::IRContext::Analysis::kAnalysisNone); + // Inform the def-use manager about the new instruction and record its basic + // block. + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr); + ir_context->set_instr_block(new_instruction_ptr, + ir_context->get_instr_block(insert_before)); // Propagate PointeeValueIsIrrelevant fact. const auto* new_synonym_type = ir_context->get_type_mgr()->GetType( @@ -165,6 +170,18 @@ bool TransformationAddSynonym::IsInstructionValid( return type->AsInteger() || type->AsFloat(); } + case protobufs::TransformationAddSynonym::BITWISE_OR: + case protobufs::TransformationAddSynonym::BITWISE_XOR: { + // The instruction must be either an integer or a vector of integers. + const auto* type = ir_context->get_type_mgr()->GetType(inst->type_id()); + assert(type && "Instruction's result id is invalid"); + + if (const auto* vector = type->AsVector()) { + return vector->element_type()->AsInteger(); + } + + return type->AsInteger(); + } case protobufs::TransformationAddSynonym::COPY_OBJECT: // All checks for OpCopyObject are handled by // fuzzerutil::CanMakeSynonymOf. @@ -190,68 +207,56 @@ TransformationAddSynonym::MakeSynonymousInstruction( auto synonym_type_id = fuzzerutil::GetTypeId(ir_context, message_.result_id()); assert(synonym_type_id && "Synonym has invalid type id"); + auto opcode = SpvOpNop; + const auto* synonym_type = + ir_context->get_type_mgr()->GetType(synonym_type_id); + assert(synonym_type && "Synonym has invalid type"); + + auto is_integral = (synonym_type->AsVector() && + synonym_type->AsVector()->element_type()->AsInteger()) || + synonym_type->AsInteger(); switch (message_.synonym_type()) { case protobufs::TransformationAddSynonym::SUB_ZERO: + opcode = is_integral ? SpvOpISub : SpvOpFSub; + break; case protobufs::TransformationAddSynonym::MUL_ONE: - case protobufs::TransformationAddSynonym::ADD_ZERO: { - const auto* synonym_type = - ir_context->get_type_mgr()->GetType(synonym_type_id); - assert(synonym_type && "Synonym has invalid type"); - - // Compute instruction's opcode based on the type of the operand. - // We have already checked that the operand is either a scalar or a vector - // of either integers or floats. - auto is_integral = - (synonym_type->AsVector() && - synonym_type->AsVector()->element_type()->AsInteger()) || - synonym_type->AsInteger(); - auto opcode = SpvOpNop; - switch (message_.synonym_type()) { - case protobufs::TransformationAddSynonym::SUB_ZERO: - opcode = is_integral ? SpvOpISub : SpvOpFSub; - break; - case protobufs::TransformationAddSynonym::MUL_ONE: - opcode = is_integral ? SpvOpIMul : SpvOpFMul; - break; - case protobufs::TransformationAddSynonym::ADD_ZERO: - opcode = is_integral ? SpvOpIAdd : SpvOpFAdd; - break; - default: - assert(false && "Unreachable"); - break; - } + opcode = is_integral ? SpvOpIMul : SpvOpFMul; + break; + case protobufs::TransformationAddSynonym::ADD_ZERO: + opcode = is_integral ? SpvOpIAdd : SpvOpFAdd; + break; + case protobufs::TransformationAddSynonym::LOGICAL_OR: + opcode = SpvOpLogicalOr; + break; + case protobufs::TransformationAddSynonym::LOGICAL_AND: + opcode = SpvOpLogicalAnd; + break; + case protobufs::TransformationAddSynonym::BITWISE_OR: + opcode = SpvOpBitwiseOr; + break; + case protobufs::TransformationAddSynonym::BITWISE_XOR: + opcode = SpvOpBitwiseXor; + break; - return MakeUnique<opt::Instruction>( - ir_context, opcode, synonym_type_id, message_.synonym_fresh_id(), - opt::Instruction::OperandList{ - {SPV_OPERAND_TYPE_ID, {message_.result_id()}}, - {SPV_OPERAND_TYPE_ID, - {MaybeGetConstantId(ir_context, transformation_context)}}}); - } case protobufs::TransformationAddSynonym::COPY_OBJECT: return MakeUnique<opt::Instruction>( ir_context, SpvOpCopyObject, synonym_type_id, message_.synonym_fresh_id(), opt::Instruction::OperandList{ {SPV_OPERAND_TYPE_ID, {message_.result_id()}}}); - case protobufs::TransformationAddSynonym::LOGICAL_OR: - case protobufs::TransformationAddSynonym::LOGICAL_AND: { - auto opcode = message_.synonym_type() == - protobufs::TransformationAddSynonym::LOGICAL_OR - ? SpvOpLogicalOr - : SpvOpLogicalAnd; - return MakeUnique<opt::Instruction>( - ir_context, opcode, synonym_type_id, message_.synonym_fresh_id(), - opt::Instruction::OperandList{ - {SPV_OPERAND_TYPE_ID, {message_.result_id()}}, - {SPV_OPERAND_TYPE_ID, - {MaybeGetConstantId(ir_context, transformation_context)}}}); - } + default: assert(false && "Unhandled synonym type"); return nullptr; } + + return MakeUnique<opt::Instruction>( + ir_context, opcode, synonym_type_id, message_.synonym_fresh_id(), + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.result_id()}}, + {SPV_OPERAND_TYPE_ID, + {MaybeGetConstantId(ir_context, transformation_context)}}}); } uint32_t TransformationAddSynonym::MaybeGetConstantId( @@ -268,6 +273,8 @@ uint32_t TransformationAddSynonym::MaybeGetConstantId( case protobufs::TransformationAddSynonym::ADD_ZERO: case protobufs::TransformationAddSynonym::SUB_ZERO: case protobufs::TransformationAddSynonym::LOGICAL_OR: + case protobufs::TransformationAddSynonym::BITWISE_OR: + case protobufs::TransformationAddSynonym::BITWISE_XOR: return fuzzerutil::MaybeGetZeroConstant( ir_context, transformation_context, synonym_type_id, false); case protobufs::TransformationAddSynonym::MUL_ONE: @@ -314,6 +321,8 @@ bool TransformationAddSynonym::IsAdditionalConstantRequired( case protobufs::TransformationAddSynonym::LOGICAL_OR: case protobufs::TransformationAddSynonym::MUL_ONE: case protobufs::TransformationAddSynonym::LOGICAL_AND: + case protobufs::TransformationAddSynonym::BITWISE_OR: + case protobufs::TransformationAddSynonym::BITWISE_XOR: return true; default: return false; diff --git a/source/fuzz/transformation_add_type_array.cpp b/source/fuzz/transformation_add_type_array.cpp index c9f6a87a..45bc8dfe 100644 --- a/source/fuzz/transformation_add_type_array.cpp +++ b/source/fuzz/transformation_add_type_array.cpp @@ -20,8 +20,8 @@ namespace spvtools { namespace fuzz { TransformationAddTypeArray::TransformationAddTypeArray( - const spvtools::fuzz::protobufs::TransformationAddTypeArray& message) - : message_(message) {} + protobufs::TransformationAddTypeArray message) + : message_(std::move(message)) {} TransformationAddTypeArray::TransformationAddTypeArray(uint32_t fresh_id, uint32_t element_type_id, @@ -39,9 +39,11 @@ bool TransformationAddTypeArray::IsApplicable( } auto element_type = ir_context->get_type_mgr()->GetType(message_.element_type_id()); - if (!element_type || element_type->AsFunction()) { - // The element type id either does not refer to a type, or refers to a - // function type; both are illegal. + if (!element_type || element_type->AsFunction() || + fuzzerutil::HasBlockOrBufferBlockDecoration(ir_context, + message_.element_type_id())) { + // The element type id either does not refer to a type, refers to a function + // type, or refers to a block-decorated struct. These cases are all illegal. return false; } auto constant = @@ -69,13 +71,17 @@ void TransformationAddTypeArray::Apply( opt::Instruction::OperandList in_operands; in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.element_type_id()}}); in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.size_id()}}); - ir_context->module()->AddType(MakeUnique<opt::Instruction>( - ir_context, SpvOpTypeArray, 0, message_.fresh_id(), in_operands)); + auto type_instruction = MakeUnique<opt::Instruction>( + ir_context, SpvOpTypeArray, 0, message_.fresh_id(), in_operands); + auto type_instruction_ptr = type_instruction.get(); + ir_context->module()->AddType(std::move(type_instruction)); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); - // We have added an instruction to the module, so need to be careful about the - // validity of existing analyses. - ir_context->InvalidateAnalysesExceptFor( - opt::IRContext::Analysis::kAnalysisNone); + + // Inform the def use manager that there is a new definition. Invalidate the + // type manager since we have added a new type. + ir_context->get_def_use_mgr()->AnalyzeInstDef(type_instruction_ptr); + ir_context->InvalidateAnalyses(opt::IRContext::kAnalysisTypes); } protobufs::Transformation TransformationAddTypeArray::ToMessage() const { diff --git a/source/fuzz/transformation_add_type_array.h b/source/fuzz/transformation_add_type_array.h index ebefc237..3162497a 100644 --- a/source/fuzz/transformation_add_type_array.h +++ b/source/fuzz/transformation_add_type_array.h @@ -26,15 +26,17 @@ namespace fuzz { class TransformationAddTypeArray : public Transformation { public: explicit TransformationAddTypeArray( - const protobufs::TransformationAddTypeArray& message); + protobufs::TransformationAddTypeArray message); TransformationAddTypeArray(uint32_t fresh_id, uint32_t element_type_id, uint32_t size_id); // - |message_.fresh_id| must be fresh // - |message_.element_type_id| must be the id of a non-function type + // - |message_.member_type_id| must not be the result id of an OpTypeStruct + // instruction that has the Block or BufferBlock decoration // - |message_.size_id| must be the id of a 32-bit integer constant that is - // positive when interpreted as signed. + // positive when interpreted as signed bool IsApplicable( opt::IRContext* ir_context, const TransformationContext& transformation_context) const override; diff --git a/source/fuzz/transformation_add_type_boolean.cpp b/source/fuzz/transformation_add_type_boolean.cpp index ebbfabc8..30ff43e2 100644 --- a/source/fuzz/transformation_add_type_boolean.cpp +++ b/source/fuzz/transformation_add_type_boolean.cpp @@ -20,8 +20,8 @@ namespace spvtools { namespace fuzz { TransformationAddTypeBoolean::TransformationAddTypeBoolean( - const spvtools::fuzz::protobufs::TransformationAddTypeBoolean& message) - : message_(message) {} + protobufs::TransformationAddTypeBoolean message) + : message_(std::move(message)) {} TransformationAddTypeBoolean::TransformationAddTypeBoolean(uint32_t fresh_id) { message_.set_fresh_id(fresh_id); @@ -42,13 +42,17 @@ bool TransformationAddTypeBoolean::IsApplicable( void TransformationAddTypeBoolean::Apply( opt::IRContext* ir_context, TransformationContext* /*unused*/) const { opt::Instruction::OperandList empty_operands; - ir_context->module()->AddType(MakeUnique<opt::Instruction>( - ir_context, SpvOpTypeBool, 0, message_.fresh_id(), empty_operands)); + auto type_instruction = MakeUnique<opt::Instruction>( + ir_context, SpvOpTypeBool, 0, message_.fresh_id(), empty_operands); + auto type_instruction_ptr = type_instruction.get(); + ir_context->module()->AddType(std::move(type_instruction)); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); - // We have added an instruction to the module, so need to be careful about the - // validity of existing analyses. - ir_context->InvalidateAnalysesExceptFor( - opt::IRContext::Analysis::kAnalysisNone); + + // Inform the def use manager that there is a new definition. Invalidate the + // type manager since we have added a new type. + ir_context->get_def_use_mgr()->AnalyzeInstDef(type_instruction_ptr); + ir_context->InvalidateAnalyses(opt::IRContext::kAnalysisTypes); } protobufs::Transformation TransformationAddTypeBoolean::ToMessage() const { diff --git a/source/fuzz/transformation_add_type_boolean.h b/source/fuzz/transformation_add_type_boolean.h index 25c92727..ee640153 100644 --- a/source/fuzz/transformation_add_type_boolean.h +++ b/source/fuzz/transformation_add_type_boolean.h @@ -25,7 +25,7 @@ namespace fuzz { class TransformationAddTypeBoolean : public Transformation { public: explicit TransformationAddTypeBoolean( - const protobufs::TransformationAddTypeBoolean& message); + protobufs::TransformationAddTypeBoolean message); explicit TransformationAddTypeBoolean(uint32_t fresh_id); diff --git a/source/fuzz/transformation_add_type_float.cpp b/source/fuzz/transformation_add_type_float.cpp index da3f3e4e..1b88b25c 100644 --- a/source/fuzz/transformation_add_type_float.cpp +++ b/source/fuzz/transformation_add_type_float.cpp @@ -26,8 +26,8 @@ TransformationAddTypeFloat::TransformationAddTypeFloat(uint32_t fresh_id, } TransformationAddTypeFloat::TransformationAddTypeFloat( - const spvtools::fuzz::protobufs::TransformationAddTypeFloat& message) - : message_(message) {} + protobufs::TransformationAddTypeFloat message) + : message_(std::move(message)) {} bool TransformationAddTypeFloat::IsApplicable( opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { @@ -65,11 +65,18 @@ bool TransformationAddTypeFloat::IsApplicable( void TransformationAddTypeFloat::Apply( opt::IRContext* ir_context, TransformationContext* /*unused*/) const { - fuzzerutil::AddFloatType(ir_context, message_.fresh_id(), message_.width()); - // We have added an instruction to the module, so need to be careful about the - // validity of existing analyses. - ir_context->InvalidateAnalysesExceptFor( - opt::IRContext::Analysis::kAnalysisNone); + auto type_instruction = MakeUnique<opt::Instruction>( + ir_context, SpvOpTypeFloat, 0, message_.fresh_id(), + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.width()}}}); + auto type_instruction_ptr = type_instruction.get(); + ir_context->module()->AddType(std::move(type_instruction)); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + // Inform the def use manager that there is a new definition, and invalidate + // the type manager since we have added a new type. + ir_context->get_def_use_mgr()->AnalyzeInstDef(type_instruction_ptr); + ir_context->InvalidateAnalyses(opt::IRContext::kAnalysisTypes); } protobufs::Transformation TransformationAddTypeFloat::ToMessage() const { diff --git a/source/fuzz/transformation_add_type_float.h b/source/fuzz/transformation_add_type_float.h index 30cd0fc6..e049d9a0 100644 --- a/source/fuzz/transformation_add_type_float.h +++ b/source/fuzz/transformation_add_type_float.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationAddTypeFloat : public Transformation { public: explicit TransformationAddTypeFloat( - const protobufs::TransformationAddTypeFloat& message); + protobufs::TransformationAddTypeFloat message); TransformationAddTypeFloat(uint32_t fresh_id, uint32_t width); diff --git a/source/fuzz/transformation_add_type_function.cpp b/source/fuzz/transformation_add_type_function.cpp index b6cddfc0..e96067f2 100644 --- a/source/fuzz/transformation_add_type_function.cpp +++ b/source/fuzz/transformation_add_type_function.cpp @@ -22,8 +22,8 @@ namespace spvtools { namespace fuzz { TransformationAddTypeFunction::TransformationAddTypeFunction( - const spvtools::fuzz::protobufs::TransformationAddTypeFunction& message) - : message_(message) {} + protobufs::TransformationAddTypeFunction message) + : message_(std::move(message)) {} TransformationAddTypeFunction::TransformationAddTypeFunction( uint32_t fresh_id, uint32_t return_type_id, diff --git a/source/fuzz/transformation_add_type_function.h b/source/fuzz/transformation_add_type_function.h index 59ded2b3..71044579 100644 --- a/source/fuzz/transformation_add_type_function.h +++ b/source/fuzz/transformation_add_type_function.h @@ -28,7 +28,7 @@ namespace fuzz { class TransformationAddTypeFunction : public Transformation { public: explicit TransformationAddTypeFunction( - const protobufs::TransformationAddTypeFunction& message); + protobufs::TransformationAddTypeFunction message); TransformationAddTypeFunction(uint32_t fresh_id, uint32_t return_type_id, const std::vector<uint32_t>& argument_type_ids); diff --git a/source/fuzz/transformation_add_type_int.cpp b/source/fuzz/transformation_add_type_int.cpp index 253ea15b..d4ef9819 100644 --- a/source/fuzz/transformation_add_type_int.cpp +++ b/source/fuzz/transformation_add_type_int.cpp @@ -20,8 +20,8 @@ namespace spvtools { namespace fuzz { TransformationAddTypeInt::TransformationAddTypeInt( - const spvtools::fuzz::protobufs::TransformationAddTypeInt& message) - : message_(message) {} + protobufs::TransformationAddTypeInt message) + : message_(std::move(message)) {} TransformationAddTypeInt::TransformationAddTypeInt(uint32_t fresh_id, uint32_t width, @@ -74,12 +74,21 @@ bool TransformationAddTypeInt::IsApplicable( void TransformationAddTypeInt::Apply(opt::IRContext* ir_context, TransformationContext* /*unused*/) const { - fuzzerutil::AddIntegerType(ir_context, message_.fresh_id(), message_.width(), - message_.is_signed()); - // We have added an instruction to the module, so need to be careful about the - // validity of existing analyses. - ir_context->InvalidateAnalysesExceptFor( - opt::IRContext::Analysis::kAnalysisNone); + auto type_instruction = MakeUnique<opt::Instruction>( + ir_context, SpvOpTypeInt, 0, message_.fresh_id(), + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.width()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, + {message_.is_signed() ? 1u : 0u}}}); + auto type_instruction_ptr = type_instruction.get(); + ir_context->module()->AddType(std::move(type_instruction)); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + // Inform the def use manager that there is a new definition. Invalidate the + // type manager since we have added a new type. + ir_context->get_def_use_mgr()->AnalyzeInstDef(type_instruction_ptr); + ir_context->InvalidateAnalyses(opt::IRContext::kAnalysisTypes); } protobufs::Transformation TransformationAddTypeInt::ToMessage() const { diff --git a/source/fuzz/transformation_add_type_int.h b/source/fuzz/transformation_add_type_int.h index 20c90ca4..dc67b7dc 100644 --- a/source/fuzz/transformation_add_type_int.h +++ b/source/fuzz/transformation_add_type_int.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationAddTypeInt : public Transformation { public: explicit TransformationAddTypeInt( - const protobufs::TransformationAddTypeInt& message); + protobufs::TransformationAddTypeInt message); TransformationAddTypeInt(uint32_t fresh_id, uint32_t width, bool is_signed); diff --git a/source/fuzz/transformation_add_type_matrix.cpp b/source/fuzz/transformation_add_type_matrix.cpp index cecebb41..b574b01b 100644 --- a/source/fuzz/transformation_add_type_matrix.cpp +++ b/source/fuzz/transformation_add_type_matrix.cpp @@ -20,8 +20,8 @@ namespace spvtools { namespace fuzz { TransformationAddTypeMatrix::TransformationAddTypeMatrix( - const spvtools::fuzz::protobufs::TransformationAddTypeMatrix& message) - : message_(message) {} + protobufs::TransformationAddTypeMatrix message) + : message_(std::move(message)) {} TransformationAddTypeMatrix::TransformationAddTypeMatrix( uint32_t fresh_id, uint32_t column_type_id, uint32_t column_count) { @@ -52,13 +52,17 @@ void TransformationAddTypeMatrix::Apply( in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.column_type_id()}}); in_operands.push_back( {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.column_count()}}); - ir_context->module()->AddType(MakeUnique<opt::Instruction>( - ir_context, SpvOpTypeMatrix, 0, message_.fresh_id(), in_operands)); + auto type_instruction = MakeUnique<opt::Instruction>( + ir_context, SpvOpTypeMatrix, 0, message_.fresh_id(), in_operands); + auto type_instruction_ptr = type_instruction.get(); + ir_context->module()->AddType(std::move(type_instruction)); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); - // We have added an instruction to the module, so need to be careful about the - // validity of existing analyses. - ir_context->InvalidateAnalysesExceptFor( - opt::IRContext::Analysis::kAnalysisNone); + + // Inform the def use manager that there is a new definition. Invalidate the + // type manager since we have added a new type. + ir_context->get_def_use_mgr()->AnalyzeInstDef(type_instruction_ptr); + ir_context->InvalidateAnalyses(opt::IRContext::kAnalysisTypes); } protobufs::Transformation TransformationAddTypeMatrix::ToMessage() const { diff --git a/source/fuzz/transformation_add_type_matrix.h b/source/fuzz/transformation_add_type_matrix.h index f1d41656..b4788b1b 100644 --- a/source/fuzz/transformation_add_type_matrix.h +++ b/source/fuzz/transformation_add_type_matrix.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationAddTypeMatrix : public Transformation { public: explicit TransformationAddTypeMatrix( - const protobufs::TransformationAddTypeMatrix& message); + protobufs::TransformationAddTypeMatrix message); TransformationAddTypeMatrix(uint32_t fresh_id, uint32_t column_type_id, uint32_t column_count); diff --git a/source/fuzz/transformation_add_type_pointer.cpp b/source/fuzz/transformation_add_type_pointer.cpp index f74768d6..c6c3945b 100644 --- a/source/fuzz/transformation_add_type_pointer.cpp +++ b/source/fuzz/transformation_add_type_pointer.cpp @@ -20,8 +20,8 @@ namespace spvtools { namespace fuzz { TransformationAddTypePointer::TransformationAddTypePointer( - const spvtools::fuzz::protobufs::TransformationAddTypePointer& message) - : message_(message) {} + protobufs::TransformationAddTypePointer message) + : message_(std::move(message)) {} TransformationAddTypePointer::TransformationAddTypePointer( uint32_t fresh_id, SpvStorageClass storage_class, uint32_t base_type_id) { @@ -47,13 +47,17 @@ void TransformationAddTypePointer::Apply( opt::Instruction::OperandList in_operands = { {SPV_OPERAND_TYPE_STORAGE_CLASS, {message_.storage_class()}}, {SPV_OPERAND_TYPE_ID, {message_.base_type_id()}}}; - ir_context->module()->AddType(MakeUnique<opt::Instruction>( - ir_context, SpvOpTypePointer, 0, message_.fresh_id(), in_operands)); + auto type_instruction = MakeUnique<opt::Instruction>( + ir_context, SpvOpTypePointer, 0, message_.fresh_id(), in_operands); + auto type_instruction_ptr = type_instruction.get(); + ir_context->module()->AddType(std::move(type_instruction)); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); - // We have added an instruction to the module, so need to be careful about the - // validity of existing analyses. - ir_context->InvalidateAnalysesExceptFor( - opt::IRContext::Analysis::kAnalysisNone); + + // Inform the def use manager that there is a new definition. Invalidate the + // type manager since we have added a new type. + ir_context->get_def_use_mgr()->AnalyzeInstDef(type_instruction_ptr); + ir_context->InvalidateAnalyses(opt::IRContext::kAnalysisTypes); } protobufs::Transformation TransformationAddTypePointer::ToMessage() const { diff --git a/source/fuzz/transformation_add_type_pointer.h b/source/fuzz/transformation_add_type_pointer.h index 3f686e97..8468c14d 100644 --- a/source/fuzz/transformation_add_type_pointer.h +++ b/source/fuzz/transformation_add_type_pointer.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationAddTypePointer : public Transformation { public: explicit TransformationAddTypePointer( - const protobufs::TransformationAddTypePointer& message); + protobufs::TransformationAddTypePointer message); TransformationAddTypePointer(uint32_t fresh_id, SpvStorageClass storage_class, uint32_t base_type_id); diff --git a/source/fuzz/transformation_add_type_struct.cpp b/source/fuzz/transformation_add_type_struct.cpp index b20ffb00..d7f0711e 100644 --- a/source/fuzz/transformation_add_type_struct.cpp +++ b/source/fuzz/transformation_add_type_struct.cpp @@ -20,8 +20,8 @@ namespace spvtools { namespace fuzz { TransformationAddTypeStruct::TransformationAddTypeStruct( - const spvtools::fuzz::protobufs::TransformationAddTypeStruct& message) - : message_(message) {} + protobufs::TransformationAddTypeStruct message) + : message_(std::move(message)) {} TransformationAddTypeStruct::TransformationAddTypeStruct( uint32_t fresh_id, const std::vector<uint32_t>& member_type_ids) { @@ -39,9 +39,11 @@ bool TransformationAddTypeStruct::IsApplicable( } for (auto member_type : message_.member_type_id()) { auto type = ir_context->get_type_mgr()->GetType(member_type); - if (!type || type->AsFunction()) { - // The member type id either does not refer to a type, or refers to a - // function type; both are illegal. + if (!type || type->AsFunction() || + fuzzerutil::HasBlockOrBufferBlockDecoration(ir_context, member_type)) { + // The member type id either does not refer to a type, refers to a + // function type, or refers to a block-decorated struct. These cases are + // all illegal. return false; } @@ -58,14 +60,36 @@ bool TransformationAddTypeStruct::IsApplicable( void TransformationAddTypeStruct::Apply( opt::IRContext* ir_context, TransformationContext* /*unused*/) const { - fuzzerutil::AddStructType( - ir_context, message_.fresh_id(), - std::vector<uint32_t>(message_.member_type_id().begin(), - message_.member_type_id().end())); - // We have added an instruction to the module, so need to be careful about the - // validity of existing analyses. - ir_context->InvalidateAnalysesExceptFor( - opt::IRContext::Analysis::kAnalysisNone); + opt::Instruction::OperandList operands; + operands.reserve(message_.member_type_id().size()); + + for (auto type_id : message_.member_type_id()) { + const auto* type = ir_context->get_type_mgr()->GetType(type_id); + (void)type; // Make compiler happy in release mode. + assert(type && !type->AsFunction() && "Component's type id is invalid"); + + if (type->AsStruct()) { + // From the spec for the BuiltIn decoration: + // - When applied to a structure-type member, that structure type cannot + // be contained as a member of another structure type. + assert(!fuzzerutil::MembersHaveBuiltInDecoration(ir_context, type_id) && + "A member struct has BuiltIn members"); + } + + operands.push_back({SPV_OPERAND_TYPE_ID, {type_id}}); + } + + auto type_instruction = MakeUnique<opt::Instruction>( + ir_context, SpvOpTypeStruct, 0, message_.fresh_id(), std::move(operands)); + auto type_instruction_ptr = type_instruction.get(); + ir_context->AddType(std::move(type_instruction)); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + // Inform the def use manager that there is a new definition. Invalidate the + // type manager since we have added a new type. + ir_context->get_def_use_mgr()->AnalyzeInstDef(type_instruction_ptr); + ir_context->InvalidateAnalyses(opt::IRContext::kAnalysisTypes); } protobufs::Transformation TransformationAddTypeStruct::ToMessage() const { diff --git a/source/fuzz/transformation_add_type_struct.h b/source/fuzz/transformation_add_type_struct.h index 94be42ad..6a8dce7c 100644 --- a/source/fuzz/transformation_add_type_struct.h +++ b/source/fuzz/transformation_add_type_struct.h @@ -28,7 +28,7 @@ namespace fuzz { class TransformationAddTypeStruct : public Transformation { public: explicit TransformationAddTypeStruct( - const protobufs::TransformationAddTypeStruct& message); + protobufs::TransformationAddTypeStruct message); TransformationAddTypeStruct(uint32_t fresh_id, const std::vector<uint32_t>& component_type_ids); @@ -37,7 +37,9 @@ class TransformationAddTypeStruct : public Transformation { // - |message_.member_type_id| must be a sequence of non-function type ids // - |message_.member_type_id| may not contain a result id of an OpTypeStruct // instruction with BuiltIn members (i.e. members of the struct are - // decorated via OpMemberDecorate with BuiltIn decoration). + // decorated via OpMemberDecorate with BuiltIn decoration) + // - |message_.member_type_id| may not contain a result id of an OpTypeStruct + // instruction that has the Block or BufferBlock decoration bool IsApplicable( opt::IRContext* ir_context, const TransformationContext& transformation_context) const override; diff --git a/source/fuzz/transformation_add_type_vector.cpp b/source/fuzz/transformation_add_type_vector.cpp index a3b0010e..4da0ff01 100644 --- a/source/fuzz/transformation_add_type_vector.cpp +++ b/source/fuzz/transformation_add_type_vector.cpp @@ -20,8 +20,8 @@ namespace spvtools { namespace fuzz { TransformationAddTypeVector::TransformationAddTypeVector( - const spvtools::fuzz::protobufs::TransformationAddTypeVector& message) - : message_(message) {} + protobufs::TransformationAddTypeVector message) + : message_(std::move(message)) {} TransformationAddTypeVector::TransformationAddTypeVector( uint32_t fresh_id, uint32_t component_type_id, uint32_t component_count) { @@ -46,13 +46,30 @@ bool TransformationAddTypeVector::IsApplicable( void TransformationAddTypeVector::Apply( opt::IRContext* ir_context, TransformationContext* /*unused*/) const { - fuzzerutil::AddVectorType(ir_context, message_.fresh_id(), - message_.component_type_id(), - message_.component_count()); - // We have added an instruction to the module, so need to be careful about the - // validity of existing analyses. - ir_context->InvalidateAnalysesExceptFor( - opt::IRContext::Analysis::kAnalysisNone); + const auto* component_type = + ir_context->get_type_mgr()->GetType(message_.component_type_id()); + (void)component_type; // Make compiler happy in release mode. + assert(component_type && + (component_type->AsInteger() || component_type->AsFloat() || + component_type->AsBool()) && + "|component_type_id| is invalid"); + assert(message_.component_count() >= 2 && message_.component_count() <= 4 && + "Precondition: component count must be in range [2, 4]."); + + auto type_instruction = MakeUnique<opt::Instruction>( + ir_context, SpvOpTypeVector, 0, message_.fresh_id(), + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.component_type_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.component_count()}}}); + auto type_instruction_ptr = type_instruction.get(); + ir_context->module()->AddType(std::move(type_instruction)); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + // Inform the def use manager that there is a new definition. Invalidate the + // type manager since we have added a new type. + ir_context->get_def_use_mgr()->AnalyzeInstDef(type_instruction_ptr); + ir_context->InvalidateAnalyses(opt::IRContext::kAnalysisTypes); } protobufs::Transformation TransformationAddTypeVector::ToMessage() const { diff --git a/source/fuzz/transformation_add_type_vector.h b/source/fuzz/transformation_add_type_vector.h index c25d565d..43460ce4 100644 --- a/source/fuzz/transformation_add_type_vector.h +++ b/source/fuzz/transformation_add_type_vector.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationAddTypeVector : public Transformation { public: explicit TransformationAddTypeVector( - const protobufs::TransformationAddTypeVector& message); + protobufs::TransformationAddTypeVector message); TransformationAddTypeVector(uint32_t fresh_id, uint32_t component_type_id, uint32_t component_count); diff --git a/source/fuzz/transformation_adjust_branch_weights.cpp b/source/fuzz/transformation_adjust_branch_weights.cpp index 8b74ed37..21fef258 100644 --- a/source/fuzz/transformation_adjust_branch_weights.cpp +++ b/source/fuzz/transformation_adjust_branch_weights.cpp @@ -28,8 +28,8 @@ const uint32_t kBranchWeightForFalseLabelIndex = 4; } // namespace TransformationAdjustBranchWeights::TransformationAdjustBranchWeights( - const spvtools::fuzz::protobufs::TransformationAdjustBranchWeights& message) - : message_(message) {} + protobufs::TransformationAdjustBranchWeights message) + : message_(std::move(message)) {} TransformationAdjustBranchWeights::TransformationAdjustBranchWeights( const protobufs::InstructionDescriptor& instruction_descriptor, diff --git a/source/fuzz/transformation_adjust_branch_weights.h b/source/fuzz/transformation_adjust_branch_weights.h index 4d451a5a..41eceeb5 100644 --- a/source/fuzz/transformation_adjust_branch_weights.h +++ b/source/fuzz/transformation_adjust_branch_weights.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationAdjustBranchWeights : public Transformation { public: explicit TransformationAdjustBranchWeights( - const protobufs::TransformationAdjustBranchWeights& message); + protobufs::TransformationAdjustBranchWeights message); TransformationAdjustBranchWeights( const protobufs::InstructionDescriptor& instruction_descriptor, diff --git a/source/fuzz/transformation_composite_construct.cpp b/source/fuzz/transformation_composite_construct.cpp index f0de1aeb..0cd2308b 100644 --- a/source/fuzz/transformation_composite_construct.cpp +++ b/source/fuzz/transformation_composite_construct.cpp @@ -23,8 +23,8 @@ namespace spvtools { namespace fuzz { TransformationCompositeConstruct::TransformationCompositeConstruct( - const protobufs::TransformationCompositeConstruct& message) - : message_(message) {} + protobufs::TransformationCompositeConstruct message) + : message_(std::move(message)) {} TransformationCompositeConstruct::TransformationCompositeConstruct( uint32_t composite_type_id, std::vector<uint32_t> component, @@ -120,12 +120,18 @@ void TransformationCompositeConstruct::Apply( } // Insert an OpCompositeConstruct instruction. - insert_before.InsertBefore(MakeUnique<opt::Instruction>( + auto new_instruction = MakeUnique<opt::Instruction>( ir_context, SpvOpCompositeConstruct, message_.composite_type_id(), - message_.fresh_id(), in_operands)); + message_.fresh_id(), in_operands); + auto new_instruction_ptr = new_instruction.get(); + insert_before.InsertBefore(std::move(new_instruction)); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr); + ir_context->set_instr_block(new_instruction_ptr, destination_block); fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); - ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + + // No analyses need to be invalidated since the transformation is local to a + // block and the def-use and instruction-to-block mappings have been updated. AddDataSynonymFacts(ir_context, transformation_context); } diff --git a/source/fuzz/transformation_composite_construct.h b/source/fuzz/transformation_composite_construct.h index 3a3e43c4..cc44a612 100644 --- a/source/fuzz/transformation_composite_construct.h +++ b/source/fuzz/transformation_composite_construct.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationCompositeConstruct : public Transformation { public: explicit TransformationCompositeConstruct( - const protobufs::TransformationCompositeConstruct& message); + protobufs::TransformationCompositeConstruct message); TransformationCompositeConstruct( uint32_t composite_type_id, std::vector<uint32_t> component, diff --git a/source/fuzz/transformation_composite_extract.cpp b/source/fuzz/transformation_composite_extract.cpp index 2aff02fb..647cd74e 100644 --- a/source/fuzz/transformation_composite_extract.cpp +++ b/source/fuzz/transformation_composite_extract.cpp @@ -24,8 +24,8 @@ namespace spvtools { namespace fuzz { TransformationCompositeExtract::TransformationCompositeExtract( - const spvtools::fuzz::protobufs::TransformationCompositeExtract& message) - : message_(message) {} + protobufs::TransformationCompositeExtract message) + : message_(std::move(message)) {} TransformationCompositeExtract::TransformationCompositeExtract( const protobufs::InstructionDescriptor& instruction_to_insert_before, @@ -89,15 +89,20 @@ void TransformationCompositeExtract::Apply( auto extracted_type = fuzzerutil::WalkCompositeTypeIndices( ir_context, composite_instruction->type_id(), message_.index()); - FindInstruction(message_.instruction_to_insert_before(), ir_context) - ->InsertBefore(MakeUnique<opt::Instruction>( + auto insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + opt::Instruction* new_instruction = + insert_before->InsertBefore(MakeUnique<opt::Instruction>( ir_context, SpvOpCompositeExtract, extracted_type, message_.fresh_id(), extract_operands)); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction); + ir_context->set_instr_block(new_instruction, + ir_context->get_instr_block(insert_before)); fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); - ir_context->InvalidateAnalysesExceptFor( - opt::IRContext::Analysis::kAnalysisNone); + // No analyses need to be invalidated since the transformation is local to a + // block and the def-use and instruction-to-block mappings have been updated. AddDataSynonymFacts(ir_context, transformation_context); } diff --git a/source/fuzz/transformation_composite_extract.h b/source/fuzz/transformation_composite_extract.h index 0f5348a8..0682a61c 100644 --- a/source/fuzz/transformation_composite_extract.h +++ b/source/fuzz/transformation_composite_extract.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationCompositeExtract : public Transformation { public: explicit TransformationCompositeExtract( - const protobufs::TransformationCompositeExtract& message); + protobufs::TransformationCompositeExtract message); TransformationCompositeExtract( const protobufs::InstructionDescriptor& instruction_to_insert_before, diff --git a/source/fuzz/transformation_composite_insert.cpp b/source/fuzz/transformation_composite_insert.cpp index cc681415..05162bfc 100644 --- a/source/fuzz/transformation_composite_insert.cpp +++ b/source/fuzz/transformation_composite_insert.cpp @@ -14,7 +14,6 @@ #include "transformation_composite_insert.h" -#include "source/fuzz/fuzzer_pass_add_composite_inserts.h" #include "source/fuzz/fuzzer_util.h" #include "source/fuzz/instruction_descriptor.h" @@ -22,8 +21,8 @@ namespace spvtools { namespace fuzz { TransformationCompositeInsert::TransformationCompositeInsert( - const spvtools::fuzz::protobufs::TransformationCompositeInsert& message) - : message_(message) {} + protobufs::TransformationCompositeInsert message) + : message_(std::move(message)) {} TransformationCompositeInsert::TransformationCompositeInsert( const protobufs::InstructionDescriptor& instruction_to_insert_before, @@ -124,15 +123,21 @@ void TransformationCompositeInsert::Apply( auto composite_type_id = fuzzerutil::GetTypeId(ir_context, message_.composite_id()); - FindInstruction(message_.instruction_to_insert_before(), ir_context) - ->InsertBefore(MakeUnique<opt::Instruction>( - ir_context, SpvOpCompositeInsert, composite_type_id, - message_.fresh_id(), std::move(in_operands))); + auto insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + auto new_instruction = MakeUnique<opt::Instruction>( + ir_context, SpvOpCompositeInsert, composite_type_id, message_.fresh_id(), + std::move(in_operands)); + auto new_instruction_ptr = new_instruction.get(); + insert_before->InsertBefore(std::move(new_instruction)); fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); - // We have modified the module so most analyzes are now invalid. - ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + // Inform the def-use manager about the new instruction and record its basic + // block. + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr); + ir_context->set_instr_block(new_instruction_ptr, + ir_context->get_instr_block(insert_before)); // Add data synonym facts that arise from the insertion. AddDataSynonymFacts(ir_context, transformation_context); diff --git a/source/fuzz/transformation_composite_insert.h b/source/fuzz/transformation_composite_insert.h index f2290142..413d41f1 100644 --- a/source/fuzz/transformation_composite_insert.h +++ b/source/fuzz/transformation_composite_insert.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationCompositeInsert : public Transformation { public: explicit TransformationCompositeInsert( - const protobufs::TransformationCompositeInsert& message); + protobufs::TransformationCompositeInsert message); TransformationCompositeInsert( const protobufs::InstructionDescriptor& instruction_to_insert_before, diff --git a/source/fuzz/transformation_compute_data_synonym_fact_closure.cpp b/source/fuzz/transformation_compute_data_synonym_fact_closure.cpp index f7270526..7ae9df82 100644 --- a/source/fuzz/transformation_compute_data_synonym_fact_closure.cpp +++ b/source/fuzz/transformation_compute_data_synonym_fact_closure.cpp @@ -19,9 +19,8 @@ namespace fuzz { TransformationComputeDataSynonymFactClosure:: TransformationComputeDataSynonymFactClosure( - const spvtools::fuzz::protobufs:: - TransformationComputeDataSynonymFactClosure& message) - : message_(message) {} + protobufs::TransformationComputeDataSynonymFactClosure message) + : message_(std::move(message)) {} TransformationComputeDataSynonymFactClosure:: TransformationComputeDataSynonymFactClosure( diff --git a/source/fuzz/transformation_compute_data_synonym_fact_closure.h b/source/fuzz/transformation_compute_data_synonym_fact_closure.h index dedabe23..c61b26ec 100644 --- a/source/fuzz/transformation_compute_data_synonym_fact_closure.h +++ b/source/fuzz/transformation_compute_data_synonym_fact_closure.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationComputeDataSynonymFactClosure : public Transformation { public: explicit TransformationComputeDataSynonymFactClosure( - const protobufs::TransformationComputeDataSynonymFactClosure& message); + protobufs::TransformationComputeDataSynonymFactClosure message); explicit TransformationComputeDataSynonymFactClosure( uint32_t maximum_equivalence_class_size); diff --git a/source/fuzz/transformation_duplicate_region_with_selection.cpp b/source/fuzz/transformation_duplicate_region_with_selection.cpp index 2ac6259d..dee1207f 100644 --- a/source/fuzz/transformation_duplicate_region_with_selection.cpp +++ b/source/fuzz/transformation_duplicate_region_with_selection.cpp @@ -21,9 +21,8 @@ namespace fuzz { TransformationDuplicateRegionWithSelection:: TransformationDuplicateRegionWithSelection( - const spvtools::fuzz::protobufs:: - TransformationDuplicateRegionWithSelection& message) - : message_(message) {} + protobufs::TransformationDuplicateRegionWithSelection message) + : message_(std::move(message)) {} TransformationDuplicateRegionWithSelection:: TransformationDuplicateRegionWithSelection( diff --git a/source/fuzz/transformation_duplicate_region_with_selection.h b/source/fuzz/transformation_duplicate_region_with_selection.h index a2b9a433..30e3c373 100644 --- a/source/fuzz/transformation_duplicate_region_with_selection.h +++ b/source/fuzz/transformation_duplicate_region_with_selection.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationDuplicateRegionWithSelection : public Transformation { public: explicit TransformationDuplicateRegionWithSelection( - const protobufs::TransformationDuplicateRegionWithSelection& message); + protobufs::TransformationDuplicateRegionWithSelection message); explicit TransformationDuplicateRegionWithSelection( uint32_t new_entry_fresh_id, uint32_t condition_id, diff --git a/source/fuzz/transformation_equation_instruction.cpp b/source/fuzz/transformation_equation_instruction.cpp index 32e83518..1e5dae97 100644 --- a/source/fuzz/transformation_equation_instruction.cpp +++ b/source/fuzz/transformation_equation_instruction.cpp @@ -21,8 +21,8 @@ namespace spvtools { namespace fuzz { TransformationEquationInstruction::TransformationEquationInstruction( - const spvtools::fuzz::protobufs::TransformationEquationInstruction& message) - : message_(message) {} + protobufs::TransformationEquationInstruction message) + : message_(std::move(message)) {} TransformationEquationInstruction::TransformationEquationInstruction( uint32_t fresh_id, SpvOp opcode, const std::vector<uint32_t>& in_operand_id, @@ -84,13 +84,17 @@ void TransformationEquationInstruction::Apply( rhs_id.push_back(id); } - FindInstruction(message_.instruction_to_insert_before(), ir_context) - ->InsertBefore(MakeUnique<opt::Instruction>( + auto insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + opt::Instruction* new_instruction = + insert_before->InsertBefore(MakeUnique<opt::Instruction>( ir_context, static_cast<SpvOp>(message_.opcode()), MaybeGetResultTypeId(ir_context), message_.fresh_id(), std::move(in_operands))); - ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction); + ir_context->set_instr_block(new_instruction, + ir_context->get_instr_block(insert_before)); // Add an equation fact as long as the result id is not irrelevant (it could // be if we are inserting into a dead block). diff --git a/source/fuzz/transformation_equation_instruction.h b/source/fuzz/transformation_equation_instruction.h index 4afc85fb..ae32a1a2 100644 --- a/source/fuzz/transformation_equation_instruction.h +++ b/source/fuzz/transformation_equation_instruction.h @@ -28,7 +28,7 @@ namespace fuzz { class TransformationEquationInstruction : public Transformation { public: explicit TransformationEquationInstruction( - const protobufs::TransformationEquationInstruction& message); + protobufs::TransformationEquationInstruction message); TransformationEquationInstruction( uint32_t fresh_id, SpvOp opcode, diff --git a/source/fuzz/transformation_expand_vector_reduction.cpp b/source/fuzz/transformation_expand_vector_reduction.cpp index 640aea25..99387066 100644 --- a/source/fuzz/transformation_expand_vector_reduction.cpp +++ b/source/fuzz/transformation_expand_vector_reduction.cpp @@ -21,9 +21,8 @@ namespace spvtools { namespace fuzz { TransformationExpandVectorReduction::TransformationExpandVectorReduction( - const spvtools::fuzz::protobufs::TransformationExpandVectorReduction& - message) - : message_(message) {} + protobufs::TransformationExpandVectorReduction message) + : message_(std::move(message)) {} TransformationExpandVectorReduction::TransformationExpandVectorReduction( const uint32_t instruction_result_id, diff --git a/source/fuzz/transformation_expand_vector_reduction.h b/source/fuzz/transformation_expand_vector_reduction.h index e4cc9539..6ee2cefa 100644 --- a/source/fuzz/transformation_expand_vector_reduction.h +++ b/source/fuzz/transformation_expand_vector_reduction.h @@ -70,7 +70,7 @@ namespace fuzz { class TransformationExpandVectorReduction : public Transformation { public: explicit TransformationExpandVectorReduction( - const protobufs::TransformationExpandVectorReduction& message); + protobufs::TransformationExpandVectorReduction message); TransformationExpandVectorReduction(const uint32_t instruction_result_id, const std::vector<uint32_t>& fresh_ids); diff --git a/source/fuzz/transformation_flatten_conditional_branch.cpp b/source/fuzz/transformation_flatten_conditional_branch.cpp index fdee5130..b8c6de0c 100644 --- a/source/fuzz/transformation_flatten_conditional_branch.cpp +++ b/source/fuzz/transformation_flatten_conditional_branch.cpp @@ -21,8 +21,8 @@ namespace spvtools { namespace fuzz { TransformationFlattenConditionalBranch::TransformationFlattenConditionalBranch( - const protobufs::TransformationFlattenConditionalBranch& message) - : message_(message) {} + protobufs::TransformationFlattenConditionalBranch message) + : message_(std::move(message)) {} TransformationFlattenConditionalBranch::TransformationFlattenConditionalBranch( uint32_t header_block_id, bool true_branch_first, @@ -844,7 +844,9 @@ bool TransformationFlattenConditionalBranch::OpSelectArgumentsAreRestricted( 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_3: + case SPV_ENV_VULKAN_1_0: + case SPV_ENV_VULKAN_1_1: { return true; } default: diff --git a/source/fuzz/transformation_flatten_conditional_branch.h b/source/fuzz/transformation_flatten_conditional_branch.h index 9bdae937..4efff670 100644 --- a/source/fuzz/transformation_flatten_conditional_branch.h +++ b/source/fuzz/transformation_flatten_conditional_branch.h @@ -23,7 +23,7 @@ namespace fuzz { class TransformationFlattenConditionalBranch : public Transformation { public: explicit TransformationFlattenConditionalBranch( - const protobufs::TransformationFlattenConditionalBranch& message); + protobufs::TransformationFlattenConditionalBranch message); TransformationFlattenConditionalBranch( uint32_t header_block_id, bool true_branch_first, diff --git a/source/fuzz/transformation_function_call.cpp b/source/fuzz/transformation_function_call.cpp index ec95c320..0f88ce51 100644 --- a/source/fuzz/transformation_function_call.cpp +++ b/source/fuzz/transformation_function_call.cpp @@ -22,8 +22,8 @@ namespace spvtools { namespace fuzz { TransformationFunctionCall::TransformationFunctionCall( - const spvtools::fuzz::protobufs::TransformationFunctionCall& message) - : message_(message) {} + protobufs::TransformationFunctionCall message) + : message_(std::move(message)) {} TransformationFunctionCall::TransformationFunctionCall( uint32_t fresh_id, uint32_t callee_id, diff --git a/source/fuzz/transformation_function_call.h b/source/fuzz/transformation_function_call.h index e220d83d..a2aaf365 100644 --- a/source/fuzz/transformation_function_call.h +++ b/source/fuzz/transformation_function_call.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationFunctionCall : public Transformation { public: explicit TransformationFunctionCall( - const protobufs::TransformationFunctionCall& message); + protobufs::TransformationFunctionCall message); TransformationFunctionCall( uint32_t fresh_id, uint32_t callee_id, diff --git a/source/fuzz/transformation_inline_function.cpp b/source/fuzz/transformation_inline_function.cpp index f997491d..a48b8179 100644 --- a/source/fuzz/transformation_inline_function.cpp +++ b/source/fuzz/transformation_inline_function.cpp @@ -21,8 +21,8 @@ namespace spvtools { namespace fuzz { TransformationInlineFunction::TransformationInlineFunction( - const spvtools::fuzz::protobufs::TransformationInlineFunction& message) - : message_(message) {} + protobufs::TransformationInlineFunction message) + : message_(std::move(message)) {} TransformationInlineFunction::TransformationInlineFunction( uint32_t function_call_id, diff --git a/source/fuzz/transformation_inline_function.h b/source/fuzz/transformation_inline_function.h index 8105d92b..f4dc410a 100644 --- a/source/fuzz/transformation_inline_function.h +++ b/source/fuzz/transformation_inline_function.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationInlineFunction : public Transformation { public: explicit TransformationInlineFunction( - const protobufs::TransformationInlineFunction& message); + protobufs::TransformationInlineFunction message); TransformationInlineFunction( uint32_t function_call_id, diff --git a/source/fuzz/transformation_load.cpp b/source/fuzz/transformation_load.cpp index f8b35134..e22f8dd2 100644 --- a/source/fuzz/transformation_load.cpp +++ b/source/fuzz/transformation_load.cpp @@ -20,9 +20,8 @@ namespace spvtools { namespace fuzz { -TransformationLoad::TransformationLoad( - const spvtools::fuzz::protobufs::TransformationLoad& message) - : message_(message) {} +TransformationLoad::TransformationLoad(protobufs::TransformationLoad message) + : message_(std::move(message)) {} TransformationLoad::TransformationLoad( uint32_t fresh_id, uint32_t pointer_id, @@ -84,12 +83,19 @@ void TransformationLoad::Apply(opt::IRContext* ir_context, uint32_t result_type = fuzzerutil::GetPointeeTypeIdFromPointerType( ir_context, fuzzerutil::GetTypeId(ir_context, message_.pointer_id())); fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); - FindInstruction(message_.instruction_to_insert_before(), ir_context) - ->InsertBefore(MakeUnique<opt::Instruction>( - ir_context, SpvOpLoad, result_type, message_.fresh_id(), - opt::Instruction::OperandList( - {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}}))); - ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + auto insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + auto new_instruction = MakeUnique<opt::Instruction>( + ir_context, SpvOpLoad, result_type, message_.fresh_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}})); + auto new_instruction_ptr = new_instruction.get(); + insert_before->InsertBefore(std::move(new_instruction)); + // Inform the def-use manager about the new instruction and record its basic + // block. + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr); + ir_context->set_instr_block(new_instruction_ptr, + ir_context->get_instr_block(insert_before)); } protobufs::Transformation TransformationLoad::ToMessage() const { diff --git a/source/fuzz/transformation_load.h b/source/fuzz/transformation_load.h index 683bba52..d10b0073 100644 --- a/source/fuzz/transformation_load.h +++ b/source/fuzz/transformation_load.h @@ -25,7 +25,7 @@ namespace fuzz { class TransformationLoad : public Transformation { public: - explicit TransformationLoad(const protobufs::TransformationLoad& message); + explicit TransformationLoad(protobufs::TransformationLoad message); TransformationLoad( uint32_t fresh_id, uint32_t pointer_id, diff --git a/source/fuzz/transformation_make_vector_operation_dynamic.cpp b/source/fuzz/transformation_make_vector_operation_dynamic.cpp index d6d51404..bd0664c0 100644 --- a/source/fuzz/transformation_make_vector_operation_dynamic.cpp +++ b/source/fuzz/transformation_make_vector_operation_dynamic.cpp @@ -22,9 +22,8 @@ namespace fuzz { TransformationMakeVectorOperationDynamic:: TransformationMakeVectorOperationDynamic( - const spvtools::fuzz::protobufs:: - TransformationMakeVectorOperationDynamic& message) - : message_(message) {} + protobufs::TransformationMakeVectorOperationDynamic message) + : message_(std::move(message)) {} TransformationMakeVectorOperationDynamic:: TransformationMakeVectorOperationDynamic(uint32_t instruction_result_id, diff --git a/source/fuzz/transformation_make_vector_operation_dynamic.h b/source/fuzz/transformation_make_vector_operation_dynamic.h index d1765c52..e444f40c 100644 --- a/source/fuzz/transformation_make_vector_operation_dynamic.h +++ b/source/fuzz/transformation_make_vector_operation_dynamic.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationMakeVectorOperationDynamic : public Transformation { public: explicit TransformationMakeVectorOperationDynamic( - const protobufs::TransformationMakeVectorOperationDynamic& message); + protobufs::TransformationMakeVectorOperationDynamic message); TransformationMakeVectorOperationDynamic(uint32_t instruction_result_id, uint32_t constant_index_id); diff --git a/source/fuzz/transformation_merge_blocks.cpp b/source/fuzz/transformation_merge_blocks.cpp index 2a9e90cc..22236795 100644 --- a/source/fuzz/transformation_merge_blocks.cpp +++ b/source/fuzz/transformation_merge_blocks.cpp @@ -21,8 +21,8 @@ namespace spvtools { namespace fuzz { TransformationMergeBlocks::TransformationMergeBlocks( - const spvtools::fuzz::protobufs::TransformationMergeBlocks& message) - : message_(message) {} + protobufs::TransformationMergeBlocks message) + : message_(std::move(message)) {} TransformationMergeBlocks::TransformationMergeBlocks(uint32_t block_id) { message_.set_block_id(block_id); diff --git a/source/fuzz/transformation_merge_blocks.h b/source/fuzz/transformation_merge_blocks.h index d9a0ca05..f6306c5a 100644 --- a/source/fuzz/transformation_merge_blocks.h +++ b/source/fuzz/transformation_merge_blocks.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationMergeBlocks : public Transformation { public: explicit TransformationMergeBlocks( - const protobufs::TransformationMergeBlocks& message); + protobufs::TransformationMergeBlocks message); TransformationMergeBlocks(uint32_t block_id); diff --git a/source/fuzz/transformation_merge_function_returns.cpp b/source/fuzz/transformation_merge_function_returns.cpp index c7cb5572..022e1b6d 100644 --- a/source/fuzz/transformation_merge_function_returns.cpp +++ b/source/fuzz/transformation_merge_function_returns.cpp @@ -21,15 +21,17 @@ namespace spvtools { namespace fuzz { TransformationMergeFunctionReturns::TransformationMergeFunctionReturns( - const protobufs::TransformationMergeFunctionReturns& message) - : message_(message) {} + protobufs::TransformationMergeFunctionReturns message) + : message_(std::move(message)) {} TransformationMergeFunctionReturns::TransformationMergeFunctionReturns( - uint32_t function_id, uint32_t outer_header_id, uint32_t outer_return_id, + uint32_t function_id, uint32_t outer_header_id, + uint32_t unreachable_continue_id, uint32_t outer_return_id, uint32_t return_val_id, uint32_t any_returnable_val_id, const std::vector<protobufs::ReturnMergingInfo>& returns_merging_info) { message_.set_function_id(function_id); message_.set_outer_header_id(outer_header_id); + message_.set_unreachable_continue_id(unreachable_continue_id); message_.set_outer_return_id(outer_return_id); message_.set_return_val_id(return_val_id); message_.set_any_returnable_val_id(any_returnable_val_id); @@ -66,7 +68,9 @@ bool TransformationMergeFunctionReturns::IsApplicable( // Check that the fresh ids provided are fresh and distinct. std::set<uint32_t> used_fresh_ids; - for (uint32_t id : {message_.outer_header_id(), message_.outer_return_id()}) { + for (uint32_t id : + {message_.outer_header_id(), message_.unreachable_continue_id(), + message_.outer_return_id()}) { if (!id || !CheckIdIsFreshAndNotUsedByThisTransformation(id, ir_context, &used_fresh_ids)) { return false; @@ -499,25 +503,20 @@ void TransformationMergeFunctionReturns::Apply( fuzzerutil::UpdateModuleIdBound(ir_context, message_.outer_header_id()); - // Add the instruction: OpLoopMerge %outer_return_id %outer_header_id None - // The header is the continue block of the outer loop. + // Add the instruction: + // OpLoopMerge %outer_return_id %unreachable_continue_id None outer_loop_header->AddInstruction(MakeUnique<opt::Instruction>( ir_context, SpvOpLoopMerge, 0, 0, opt::Instruction::OperandList{ {SPV_OPERAND_TYPE_ID, {message_.outer_return_id()}}, - {SPV_OPERAND_TYPE_ID, {message_.outer_header_id()}}, + {SPV_OPERAND_TYPE_ID, {message_.unreachable_continue_id()}}, {SPV_OPERAND_TYPE_LOOP_CONTROL, {SpvLoopControlMaskNone}}})); - // Add conditional branch: - // OpBranchConditional %true %block_after_entry %outer_header_id - // This will always branch to %block_after_entry, but it also creates a back - // edge for the loop (which is never traversed). + // Add unconditional branch to %block_after_entry. outer_loop_header->AddInstruction(MakeUnique<opt::Instruction>( - ir_context, SpvOpBranchConditional, 0, 0, + ir_context, SpvOpBranch, 0, 0, opt::Instruction::OperandList{ - {SPV_OPERAND_TYPE_ID, {constant_true}}, - {SPV_OPERAND_TYPE_ID, {block_after_entry}}, - {SPV_OPERAND_TYPE_ID, {message_.outer_header_id()}}})); + {SPV_OPERAND_TYPE_ID, {block_after_entry}}})); // Insert the header right after the entry block. function->InsertBasicBlockAfter(std::move(outer_loop_header), @@ -581,6 +580,24 @@ void TransformationMergeFunctionReturns::Apply( // Insert the new return block at the end of the function. function->AddBasicBlock(std::move(outer_return_block)); + // Create the unreachable continue block associated with the enclosing loop. + auto unreachable_continue_block = + MakeUnique<opt::BasicBlock>(MakeUnique<opt::Instruction>( + ir_context, SpvOpLabel, 0, message_.unreachable_continue_id(), + opt::Instruction::OperandList())); + + fuzzerutil::UpdateModuleIdBound(ir_context, + message_.unreachable_continue_id()); + + // Insert an branch back to the loop header, to create a back edge. + unreachable_continue_block->AddInstruction(MakeUnique<opt::Instruction>( + ir_context, SpvOpBranch, 0, 0, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.outer_header_id()}}})); + + // Insert the unreachable continue block at the end of the function. + function->AddBasicBlock(std::move(unreachable_continue_block)); + // All analyses must be invalidated because the structure of the module was // changed. ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); @@ -590,13 +607,14 @@ std::unordered_set<uint32_t> TransformationMergeFunctionReturns::GetFreshIds() const { std::unordered_set<uint32_t> result; result.emplace(message_.outer_header_id()); + result.emplace(message_.unreachable_continue_id()); result.emplace(message_.outer_return_id()); // |message_.return_val_info| can be 0 if the function is void. if (message_.return_val_id()) { result.emplace(message_.return_val_id()); } - for (auto merging_info : message_.return_merging_info()) { + for (const auto& merging_info : message_.return_merging_info()) { result.emplace(merging_info.is_returning_id()); // |maybe_return_val_id| can be 0 if the function is void. if (merging_info.maybe_return_val_id()) { diff --git a/source/fuzz/transformation_merge_function_returns.h b/source/fuzz/transformation_merge_function_returns.h index 4b299367..b3208acf 100644 --- a/source/fuzz/transformation_merge_function_returns.h +++ b/source/fuzz/transformation_merge_function_returns.h @@ -22,10 +22,11 @@ namespace fuzz { class TransformationMergeFunctionReturns : public Transformation { public: explicit TransformationMergeFunctionReturns( - const protobufs::TransformationMergeFunctionReturns& message); + protobufs::TransformationMergeFunctionReturns message); TransformationMergeFunctionReturns( - uint32_t function_id, uint32_t outer_header_id, uint32_t outer_return_id, + uint32_t function_id, uint32_t outer_header_id, + uint32_t unreachable_continue_id, uint32_t outer_return_id, uint32_t return_val_id, uint32_t any_returnable_val_id, const std::vector<protobufs::ReturnMergingInfo>& returns_merging_info); @@ -40,7 +41,7 @@ class TransformationMergeFunctionReturns : public Transformation { // statements, this id will be ignored. // - Merge blocks of reachable loops that contain return statements only // consist of OpLabel, OpPhi or OpBranch instructions. - // - The model contains OpConstantTrue and OpConstantFalse instructions. + // - The module contains OpConstantTrue and OpConstantFalse instructions. // - For all merge blocks of reachable loops that contain return statements, // either: // - a mapping is provided in |message_.return_merging_info|, all of the diff --git a/source/fuzz/transformation_move_block_down.cpp b/source/fuzz/transformation_move_block_down.cpp index c5ed4333..dc1b2430 100644 --- a/source/fuzz/transformation_move_block_down.cpp +++ b/source/fuzz/transformation_move_block_down.cpp @@ -20,8 +20,8 @@ namespace spvtools { namespace fuzz { TransformationMoveBlockDown::TransformationMoveBlockDown( - const spvtools::fuzz::protobufs::TransformationMoveBlockDown& message) - : message_(message) {} + protobufs::TransformationMoveBlockDown message) + : message_(std::move(message)) {} TransformationMoveBlockDown::TransformationMoveBlockDown(uint32_t id) { message_.set_block_id(id); diff --git a/source/fuzz/transformation_move_block_down.h b/source/fuzz/transformation_move_block_down.h index 82f2599a..cbad9451 100644 --- a/source/fuzz/transformation_move_block_down.h +++ b/source/fuzz/transformation_move_block_down.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationMoveBlockDown : public Transformation { public: explicit TransformationMoveBlockDown( - const protobufs::TransformationMoveBlockDown& message); + protobufs::TransformationMoveBlockDown message); explicit TransformationMoveBlockDown(uint32_t id); diff --git a/source/fuzz/transformation_move_instruction_down.cpp b/source/fuzz/transformation_move_instruction_down.cpp index dec05789..c8139e72 100644 --- a/source/fuzz/transformation_move_instruction_down.cpp +++ b/source/fuzz/transformation_move_instruction_down.cpp @@ -38,8 +38,8 @@ std::string GetExtensionSet(opt::IRContext* ir_context, } // namespace TransformationMoveInstructionDown::TransformationMoveInstructionDown( - const protobufs::TransformationMoveInstructionDown& message) - : message_(message) {} + protobufs::TransformationMoveInstructionDown message) + : message_(std::move(message)) {} TransformationMoveInstructionDown::TransformationMoveInstructionDown( const protobufs::InstructionDescriptor& instruction) { diff --git a/source/fuzz/transformation_move_instruction_down.h b/source/fuzz/transformation_move_instruction_down.h index 85852253..2a5a8f1f 100644 --- a/source/fuzz/transformation_move_instruction_down.h +++ b/source/fuzz/transformation_move_instruction_down.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationMoveInstructionDown : public Transformation { public: explicit TransformationMoveInstructionDown( - const protobufs::TransformationMoveInstructionDown& message); + protobufs::TransformationMoveInstructionDown message); explicit TransformationMoveInstructionDown( const protobufs::InstructionDescriptor& instruction); diff --git a/source/fuzz/transformation_mutate_pointer.cpp b/source/fuzz/transformation_mutate_pointer.cpp index fefedbd1..516a0d61 100644 --- a/source/fuzz/transformation_mutate_pointer.cpp +++ b/source/fuzz/transformation_mutate_pointer.cpp @@ -21,8 +21,8 @@ namespace spvtools { namespace fuzz { TransformationMutatePointer::TransformationMutatePointer( - const protobufs::TransformationMutatePointer& message) - : message_(message) {} + protobufs::TransformationMutatePointer message) + : message_(std::move(message)) {} TransformationMutatePointer::TransformationMutatePointer( uint32_t pointer_id, uint32_t fresh_id, @@ -92,36 +92,47 @@ void TransformationMutatePointer::Apply( auto* insert_before_inst = FindInstruction(message_.insert_before(), ir_context); assert(insert_before_inst && "|insert_before| descriptor is invalid"); + opt::BasicBlock* enclosing_block = + ir_context->get_instr_block(insert_before_inst); auto pointee_type_id = fuzzerutil::GetPointeeTypeIdFromPointerType( ir_context, fuzzerutil::GetTypeId(ir_context, message_.pointer_id())); // Back up the original value. - insert_before_inst->InsertBefore(MakeUnique<opt::Instruction>( + auto backup_instruction = MakeUnique<opt::Instruction>( ir_context, SpvOpLoad, pointee_type_id, message_.fresh_id(), opt::Instruction::OperandList{ - {SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}})); + {SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}}); + auto backup_instruction_ptr = backup_instruction.get(); + insert_before_inst->InsertBefore(std::move(backup_instruction)); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(backup_instruction_ptr); + ir_context->set_instr_block(backup_instruction_ptr, enclosing_block); // Insert a new value. - insert_before_inst->InsertBefore(MakeUnique<opt::Instruction>( + auto new_value_instruction = MakeUnique<opt::Instruction>( ir_context, SpvOpStore, 0, 0, opt::Instruction::OperandList{ {SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}, {SPV_OPERAND_TYPE_ID, {fuzzerutil::MaybeGetZeroConstant( - ir_context, *transformation_context, pointee_type_id, true)}}})); + ir_context, *transformation_context, pointee_type_id, true)}}}); + auto new_value_instruction_ptr = new_value_instruction.get(); + insert_before_inst->InsertBefore(std::move(new_value_instruction)); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_value_instruction_ptr); + ir_context->set_instr_block(new_value_instruction_ptr, enclosing_block); // Restore the original value. - insert_before_inst->InsertBefore(MakeUnique<opt::Instruction>( + auto restore_instruction = MakeUnique<opt::Instruction>( ir_context, SpvOpStore, 0, 0, opt::Instruction::OperandList{ {SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}, - {SPV_OPERAND_TYPE_ID, {message_.fresh_id()}}})); + {SPV_OPERAND_TYPE_ID, {message_.fresh_id()}}}); + auto restore_instruction_ptr = restore_instruction.get(); + insert_before_inst->InsertBefore(std::move(restore_instruction)); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(restore_instruction_ptr); + ir_context->set_instr_block(restore_instruction_ptr, enclosing_block); fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); - - // Make sure analyses represent the correct state of the module. - ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); } protobufs::Transformation TransformationMutatePointer::ToMessage() const { diff --git a/source/fuzz/transformation_mutate_pointer.h b/source/fuzz/transformation_mutate_pointer.h index b9f09656..2c712909 100644 --- a/source/fuzz/transformation_mutate_pointer.h +++ b/source/fuzz/transformation_mutate_pointer.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationMutatePointer : public Transformation { public: explicit TransformationMutatePointer( - const protobufs::TransformationMutatePointer& message); + protobufs::TransformationMutatePointer message); explicit TransformationMutatePointer( uint32_t pointer_id, uint32_t fresh_id, diff --git a/source/fuzz/transformation_outline_function.cpp b/source/fuzz/transformation_outline_function.cpp index 643fd699..84e8ac2c 100644 --- a/source/fuzz/transformation_outline_function.cpp +++ b/source/fuzz/transformation_outline_function.cpp @@ -22,8 +22,8 @@ namespace spvtools { namespace fuzz { TransformationOutlineFunction::TransformationOutlineFunction( - const spvtools::fuzz::protobufs::TransformationOutlineFunction& message) - : message_(message) {} + protobufs::TransformationOutlineFunction message) + : message_(std::move(message)) {} TransformationOutlineFunction::TransformationOutlineFunction( uint32_t entry_block, uint32_t exit_block, @@ -175,6 +175,19 @@ bool TransformationOutlineFunction::IsApplicable( // This is achieved by going through every block in the function that contains // the region. for (auto& block : *entry_block->GetParent()) { + if (region_set.count(&block) != 0) { + // The block is in the region. Check that it does not have any unreachable + // predecessors. If it does, then we do not regard the region as single- + // entry-single-exit and hence do not outline it. + for (auto pred : ir_context->cfg()->preds(block.id())) { + if (!fuzzerutil::BlockIsReachableInItsFunction( + ir_context, ir_context->cfg()->block(pred))) { + // The predecessor is unreachable. + return false; + } + } + } + if (&block == exit_block) { // It is OK (and typically expected) for the exit block of the region to // have successors outside the region. diff --git a/source/fuzz/transformation_outline_function.h b/source/fuzz/transformation_outline_function.h index 36c0daf5..94ce556d 100644 --- a/source/fuzz/transformation_outline_function.h +++ b/source/fuzz/transformation_outline_function.h @@ -30,7 +30,7 @@ namespace fuzz { class TransformationOutlineFunction : public Transformation { public: explicit TransformationOutlineFunction( - const protobufs::TransformationOutlineFunction& message); + protobufs::TransformationOutlineFunction message); TransformationOutlineFunction( uint32_t entry_block, uint32_t exit_block, diff --git a/source/fuzz/transformation_permute_function_parameters.cpp b/source/fuzz/transformation_permute_function_parameters.cpp index a954cc18..5663d72f 100644 --- a/source/fuzz/transformation_permute_function_parameters.cpp +++ b/source/fuzz/transformation_permute_function_parameters.cpp @@ -23,9 +23,8 @@ namespace fuzz { TransformationPermuteFunctionParameters:: TransformationPermuteFunctionParameters( - const spvtools::fuzz::protobufs:: - TransformationPermuteFunctionParameters& message) - : message_(message) {} + protobufs::TransformationPermuteFunctionParameters message) + : message_(std::move(message)) {} TransformationPermuteFunctionParameters:: TransformationPermuteFunctionParameters( diff --git a/source/fuzz/transformation_permute_function_parameters.h b/source/fuzz/transformation_permute_function_parameters.h index 38de8b18..abb5675c 100644 --- a/source/fuzz/transformation_permute_function_parameters.h +++ b/source/fuzz/transformation_permute_function_parameters.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationPermuteFunctionParameters : public Transformation { public: explicit TransformationPermuteFunctionParameters( - const protobufs::TransformationPermuteFunctionParameters& message); + protobufs::TransformationPermuteFunctionParameters message); TransformationPermuteFunctionParameters( uint32_t function_id, uint32_t function_type_fresh_id, diff --git a/source/fuzz/transformation_permute_phi_operands.cpp b/source/fuzz/transformation_permute_phi_operands.cpp index ebd3c862..7ee7a82f 100644 --- a/source/fuzz/transformation_permute_phi_operands.cpp +++ b/source/fuzz/transformation_permute_phi_operands.cpp @@ -22,8 +22,8 @@ namespace spvtools { namespace fuzz { TransformationPermutePhiOperands::TransformationPermutePhiOperands( - const spvtools::fuzz::protobufs::TransformationPermutePhiOperands& message) - : message_(message) {} + protobufs::TransformationPermutePhiOperands message) + : message_(std::move(message)) {} TransformationPermutePhiOperands::TransformationPermutePhiOperands( uint32_t result_id, const std::vector<uint32_t>& permutation) { @@ -80,9 +80,8 @@ void TransformationPermutePhiOperands::Apply( inst->SetInOperands(std::move(permuted_operands)); - // Make sure our changes are analyzed - ir_context->InvalidateAnalysesExceptFor( - opt::IRContext::Analysis::kAnalysisNone); + // Update the def-use manager. + ir_context->UpdateDefUse(inst); } protobufs::Transformation TransformationPermutePhiOperands::ToMessage() const { diff --git a/source/fuzz/transformation_permute_phi_operands.h b/source/fuzz/transformation_permute_phi_operands.h index 8198b706..16427113 100644 --- a/source/fuzz/transformation_permute_phi_operands.h +++ b/source/fuzz/transformation_permute_phi_operands.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationPermutePhiOperands : public Transformation { public: explicit TransformationPermutePhiOperands( - const protobufs::TransformationPermutePhiOperands& message); + protobufs::TransformationPermutePhiOperands message); TransformationPermutePhiOperands(uint32_t result_id, const std::vector<uint32_t>& permutation); diff --git a/source/fuzz/transformation_propagate_instruction_down.cpp b/source/fuzz/transformation_propagate_instruction_down.cpp index ba22e398..7713562e 100644 --- a/source/fuzz/transformation_propagate_instruction_down.cpp +++ b/source/fuzz/transformation_propagate_instruction_down.cpp @@ -21,8 +21,8 @@ namespace spvtools { namespace fuzz { TransformationPropagateInstructionDown::TransformationPropagateInstructionDown( - const protobufs::TransformationPropagateInstructionDown& message) - : message_(message) {} + protobufs::TransformationPropagateInstructionDown message) + : message_(std::move(message)) {} TransformationPropagateInstructionDown::TransformationPropagateInstructionDown( uint32_t block_id, uint32_t phi_fresh_id, diff --git a/source/fuzz/transformation_propagate_instruction_down.h b/source/fuzz/transformation_propagate_instruction_down.h index 7eca1ad1..560d7dc5 100644 --- a/source/fuzz/transformation_propagate_instruction_down.h +++ b/source/fuzz/transformation_propagate_instruction_down.h @@ -28,7 +28,7 @@ namespace fuzz { class TransformationPropagateInstructionDown : public Transformation { public: explicit TransformationPropagateInstructionDown( - const protobufs::TransformationPropagateInstructionDown& message); + protobufs::TransformationPropagateInstructionDown message); TransformationPropagateInstructionDown( uint32_t block_id, uint32_t phi_fresh_id, diff --git a/source/fuzz/transformation_propagate_instruction_up.cpp b/source/fuzz/transformation_propagate_instruction_up.cpp index a2cacf40..bf0e6630 100644 --- a/source/fuzz/transformation_propagate_instruction_up.cpp +++ b/source/fuzz/transformation_propagate_instruction_up.cpp @@ -79,8 +79,8 @@ bool HasValidDependencies(opt::IRContext* ir_context, opt::Instruction* inst) { } // namespace TransformationPropagateInstructionUp::TransformationPropagateInstructionUp( - const protobufs::TransformationPropagateInstructionUp& message) - : message_(message) {} + protobufs::TransformationPropagateInstructionUp message) + : message_(std::move(message)) {} TransformationPropagateInstructionUp::TransformationPropagateInstructionUp( uint32_t block_id, diff --git a/source/fuzz/transformation_propagate_instruction_up.h b/source/fuzz/transformation_propagate_instruction_up.h index 63540947..0ca051bf 100644 --- a/source/fuzz/transformation_propagate_instruction_up.h +++ b/source/fuzz/transformation_propagate_instruction_up.h @@ -28,7 +28,7 @@ namespace fuzz { class TransformationPropagateInstructionUp : public Transformation { public: explicit TransformationPropagateInstructionUp( - const protobufs::TransformationPropagateInstructionUp& message); + protobufs::TransformationPropagateInstructionUp message); TransformationPropagateInstructionUp( uint32_t block_id, diff --git a/source/fuzz/transformation_push_id_through_variable.cpp b/source/fuzz/transformation_push_id_through_variable.cpp index cdc40aab..0df1da6b 100644 --- a/source/fuzz/transformation_push_id_through_variable.cpp +++ b/source/fuzz/transformation_push_id_through_variable.cpp @@ -21,9 +21,8 @@ namespace spvtools { namespace fuzz { TransformationPushIdThroughVariable::TransformationPushIdThroughVariable( - const spvtools::fuzz::protobufs::TransformationPushIdThroughVariable& - message) - : message_(message) {} + protobufs::TransformationPushIdThroughVariable message) + : message_(std::move(message)) {} TransformationPushIdThroughVariable::TransformationPushIdThroughVariable( uint32_t value_id, uint32_t value_synonym_id, uint32_t variable_id, @@ -105,6 +104,10 @@ void TransformationPushIdThroughVariable::Apply( auto value_instruction = ir_context->get_def_use_mgr()->GetDef(message_.value_id()); + opt::Instruction* insert_before = + FindInstruction(message_.instruction_descriptor(), ir_context); + opt::BasicBlock* enclosing_block = ir_context->get_instr_block(insert_before); + // A pointer type instruction pointing to the value type must be defined. auto pointer_type_id = fuzzerutil::MaybeGetPointerType( ir_context, value_instruction->type_id(), @@ -113,36 +116,42 @@ void TransformationPushIdThroughVariable::Apply( // Adds whether a global or local variable. if (message_.variable_storage_class() == SpvStorageClassPrivate) { - fuzzerutil::AddGlobalVariable(ir_context, message_.variable_id(), - pointer_type_id, SpvStorageClassPrivate, - message_.initializer_id()); + opt::Instruction* global_variable = fuzzerutil::AddGlobalVariable( + ir_context, message_.variable_id(), pointer_type_id, + SpvStorageClassPrivate, message_.initializer_id()); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(global_variable); } else { - auto function_id = ir_context - ->get_instr_block(FindInstruction( - message_.instruction_descriptor(), ir_context)) - ->GetParent() - ->result_id(); - fuzzerutil::AddLocalVariable(ir_context, message_.variable_id(), - pointer_type_id, function_id, - message_.initializer_id()); + opt::Function* function = + ir_context + ->get_instr_block( + FindInstruction(message_.instruction_descriptor(), ir_context)) + ->GetParent(); + opt::Instruction* local_variable = fuzzerutil::AddLocalVariable( + ir_context, message_.variable_id(), pointer_type_id, + function->result_id(), message_.initializer_id()); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(local_variable); + ir_context->set_instr_block(local_variable, &*function->entry()); } // First, insert the OpLoad instruction before |instruction_descriptor| and // then insert the OpStore instruction before the OpLoad instruction. fuzzerutil::UpdateModuleIdBound(ir_context, message_.value_synonym_id()); - FindInstruction(message_.instruction_descriptor(), ir_context) - ->InsertBefore(MakeUnique<opt::Instruction>( + opt::Instruction* load_instruction = + insert_before->InsertBefore(MakeUnique<opt::Instruction>( ir_context, SpvOpLoad, value_instruction->type_id(), message_.value_synonym_id(), opt::Instruction::OperandList( - {{SPV_OPERAND_TYPE_ID, {message_.variable_id()}}}))) - ->InsertBefore(MakeUnique<opt::Instruction>( + {{SPV_OPERAND_TYPE_ID, {message_.variable_id()}}}))); + opt::Instruction* store_instruction = + load_instruction->InsertBefore(MakeUnique<opt::Instruction>( ir_context, SpvOpStore, 0, 0, opt::Instruction::OperandList( {{SPV_OPERAND_TYPE_ID, {message_.variable_id()}}, {SPV_OPERAND_TYPE_ID, {message_.value_id()}}}))); - - ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(store_instruction); + ir_context->set_instr_block(store_instruction, enclosing_block); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(load_instruction); + ir_context->set_instr_block(load_instruction, enclosing_block); // We should be able to create a synonym of |value_id| if it's not irrelevant. if (fuzzerutil::CanMakeSynonymOf(ir_context, *transformation_context, diff --git a/source/fuzz/transformation_push_id_through_variable.h b/source/fuzz/transformation_push_id_through_variable.h index d0558259..ec6943ca 100644 --- a/source/fuzz/transformation_push_id_through_variable.h +++ b/source/fuzz/transformation_push_id_through_variable.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationPushIdThroughVariable : public Transformation { public: explicit TransformationPushIdThroughVariable( - const protobufs::TransformationPushIdThroughVariable& message); + protobufs::TransformationPushIdThroughVariable message); TransformationPushIdThroughVariable( uint32_t value_id, uint32_t value_synonym_fresh_id, diff --git a/source/fuzz/transformation_record_synonymous_constants.cpp b/source/fuzz/transformation_record_synonymous_constants.cpp index 30ea94bb..3278d7d8 100644 --- a/source/fuzz/transformation_record_synonymous_constants.cpp +++ b/source/fuzz/transformation_record_synonymous_constants.cpp @@ -22,8 +22,8 @@ namespace fuzz { TransformationRecordSynonymousConstants:: TransformationRecordSynonymousConstants( - const protobufs::TransformationRecordSynonymousConstants& message) - : message_(message) {} + protobufs::TransformationRecordSynonymousConstants message) + : message_(std::move(message)) {} TransformationRecordSynonymousConstants:: TransformationRecordSynonymousConstants(uint32_t constant1_id, diff --git a/source/fuzz/transformation_record_synonymous_constants.h b/source/fuzz/transformation_record_synonymous_constants.h index 4376c878..d99b0e2a 100644 --- a/source/fuzz/transformation_record_synonymous_constants.h +++ b/source/fuzz/transformation_record_synonymous_constants.h @@ -24,7 +24,7 @@ namespace fuzz { class TransformationRecordSynonymousConstants : public Transformation { public: explicit TransformationRecordSynonymousConstants( - const protobufs::TransformationRecordSynonymousConstants& message); + protobufs::TransformationRecordSynonymousConstants message); TransformationRecordSynonymousConstants(uint32_t constant1_id, uint32_t constant2_id); diff --git a/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.cpp b/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.cpp index a2575153..e1977a64 100644 --- a/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.cpp +++ b/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.cpp @@ -27,9 +27,8 @@ const uint32_t kArithmeticInstructionIndexRightInOperand = 1; TransformationReplaceAddSubMulWithCarryingExtended:: TransformationReplaceAddSubMulWithCarryingExtended( - const spvtools::fuzz::protobufs:: - TransformationReplaceAddSubMulWithCarryingExtended& message) - : message_(message) {} + protobufs::TransformationReplaceAddSubMulWithCarryingExtended message) + : message_(std::move(message)) {} TransformationReplaceAddSubMulWithCarryingExtended:: TransformationReplaceAddSubMulWithCarryingExtended(uint32_t struct_fresh_id, diff --git a/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h b/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h index 243542c2..9deb2803 100644 --- a/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h +++ b/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h @@ -27,8 +27,7 @@ class TransformationReplaceAddSubMulWithCarryingExtended : public Transformation { public: explicit TransformationReplaceAddSubMulWithCarryingExtended( - const protobufs::TransformationReplaceAddSubMulWithCarryingExtended& - message); + protobufs::TransformationReplaceAddSubMulWithCarryingExtended message); explicit TransformationReplaceAddSubMulWithCarryingExtended( uint32_t struct_fresh_id, uint32_t result_id); diff --git a/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp b/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp index b458b569..24293516 100644 --- a/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp +++ b/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp @@ -111,9 +111,9 @@ bool unsigned_int_binop_evaluates_to(T lhs, T rhs, SpvOp binop, TransformationReplaceBooleanConstantWithConstantBinary:: TransformationReplaceBooleanConstantWithConstantBinary( - const spvtools::fuzz::protobufs:: - TransformationReplaceBooleanConstantWithConstantBinary& message) - : message_(message) {} + protobufs::TransformationReplaceBooleanConstantWithConstantBinary + message) + : message_(std::move(message)) {} TransformationReplaceBooleanConstantWithConstantBinary:: TransformationReplaceBooleanConstantWithConstantBinary( diff --git a/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h b/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h index a0ece7f3..97c66bf9 100644 --- a/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h +++ b/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h @@ -27,7 +27,7 @@ class TransformationReplaceBooleanConstantWithConstantBinary : public Transformation { public: explicit TransformationReplaceBooleanConstantWithConstantBinary( - const protobufs::TransformationReplaceBooleanConstantWithConstantBinary& + protobufs::TransformationReplaceBooleanConstantWithConstantBinary message); TransformationReplaceBooleanConstantWithConstantBinary( diff --git a/source/fuzz/transformation_replace_branch_from_dead_block_with_exit.cpp b/source/fuzz/transformation_replace_branch_from_dead_block_with_exit.cpp index e8090121..9ea7cb6a 100644 --- a/source/fuzz/transformation_replace_branch_from_dead_block_with_exit.cpp +++ b/source/fuzz/transformation_replace_branch_from_dead_block_with_exit.cpp @@ -21,9 +21,8 @@ namespace fuzz { TransformationReplaceBranchFromDeadBlockWithExit:: TransformationReplaceBranchFromDeadBlockWithExit( - const spvtools::fuzz::protobufs:: - TransformationReplaceBranchFromDeadBlockWithExit& message) - : message_(message) {} + protobufs::TransformationReplaceBranchFromDeadBlockWithExit message) + : message_(std::move(message)) {} TransformationReplaceBranchFromDeadBlockWithExit:: TransformationReplaceBranchFromDeadBlockWithExit(uint32_t block_id, @@ -162,7 +161,10 @@ bool TransformationReplaceBranchFromDeadBlockWithExit::BlockIsSuitable( if (ir_context->cfg()->preds(successor->id()).size() < 2) { return false; } - return true; + // Make sure that domination rules are satisfied when we remove the branch + // from the |block| to its |successor|. + return fuzzerutil::NewTerminatorPreservesDominationRules( + ir_context, block.id(), {ir_context, SpvOpUnreachable}); } } // namespace fuzz diff --git a/source/fuzz/transformation_replace_branch_from_dead_block_with_exit.h b/source/fuzz/transformation_replace_branch_from_dead_block_with_exit.h index e1418c9d..89667fcd 100644 --- a/source/fuzz/transformation_replace_branch_from_dead_block_with_exit.h +++ b/source/fuzz/transformation_replace_branch_from_dead_block_with_exit.h @@ -27,8 +27,7 @@ namespace fuzz { class TransformationReplaceBranchFromDeadBlockWithExit : public Transformation { public: explicit TransformationReplaceBranchFromDeadBlockWithExit( - const protobufs::TransformationReplaceBranchFromDeadBlockWithExit& - message); + protobufs::TransformationReplaceBranchFromDeadBlockWithExit message); TransformationReplaceBranchFromDeadBlockWithExit(uint32_t block_id, SpvOp opcode, @@ -41,13 +40,17 @@ class TransformationReplaceBranchFromDeadBlockWithExit : public Transformation { // predecessor // - |message_.opcode()| must be one of OpKill, OpReturn, OpReturnValue and // OpUnreachable - // - |message_.opcode()| can only be OpKill the module's entry points all + // - |message_.opcode()| can only be OpKill if the module's entry points all // have Fragment execution mode // - |message_.opcode()| can only be OpReturn if the return type of the // function containing the block is void // - If |message_.opcode()| is OpReturnValue then |message_.return_value_id| // must be an id that is available at the block terminator and that matches // the return type of the enclosing function + // - Domination rules should be preserved when we apply this transformation. + // In particular, if some block appears after the |block_id|'s successor in + // the CFG, then that block cannot dominate |block_id|'s successor when this + // transformation is applied. bool IsApplicable( opt::IRContext* ir_context, const TransformationContext& transformation_context) const override; diff --git a/source/fuzz/transformation_replace_constant_with_uniform.cpp b/source/fuzz/transformation_replace_constant_with_uniform.cpp index 95932bf6..c6698c03 100644 --- a/source/fuzz/transformation_replace_constant_with_uniform.cpp +++ b/source/fuzz/transformation_replace_constant_with_uniform.cpp @@ -22,9 +22,8 @@ namespace fuzz { TransformationReplaceConstantWithUniform:: TransformationReplaceConstantWithUniform( - const spvtools::fuzz::protobufs:: - TransformationReplaceConstantWithUniform& message) - : message_(message) {} + protobufs::TransformationReplaceConstantWithUniform message) + : message_(std::move(message)) {} TransformationReplaceConstantWithUniform:: TransformationReplaceConstantWithUniform( @@ -254,28 +253,39 @@ void TransformationReplaceConstantWithUniform::Apply( auto* insert_before_inst = GetInsertBeforeInstruction(ir_context); assert(insert_before_inst && "There must exist an insertion point for OpAccessChain and OpLoad"); + opt::BasicBlock* enclosing_block = + ir_context->get_instr_block(insert_before_inst); // Add an access chain instruction to target the uniform element. - insert_before_inst->InsertBefore( - MakeAccessChainInstruction(ir_context, constant_type_id)); + auto access_chain_instruction = + MakeAccessChainInstruction(ir_context, constant_type_id); + auto access_chain_instruction_ptr = access_chain_instruction.get(); + insert_before_inst->InsertBefore(std::move(access_chain_instruction)); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse( + access_chain_instruction_ptr); + ir_context->set_instr_block(access_chain_instruction_ptr, enclosing_block); // Add a load from this access chain. - insert_before_inst->InsertBefore( - MakeLoadInstruction(ir_context, constant_type_id)); + auto load_instruction = MakeLoadInstruction(ir_context, constant_type_id); + auto load_instruction_ptr = load_instruction.get(); + insert_before_inst->InsertBefore(std::move(load_instruction)); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(load_instruction_ptr); + ir_context->set_instr_block(load_instruction_ptr, enclosing_block); // Adjust the instruction containing the usage of the constant so that this // usage refers instead to the result of the load. instruction_containing_constant_use->SetInOperand( message_.id_use_descriptor().in_operand_index(), {message_.fresh_id_for_load()}); + ir_context->get_def_use_mgr()->EraseUseRecordsOfOperandIds( + instruction_containing_constant_use); + ir_context->get_def_use_mgr()->AnalyzeInstUse( + instruction_containing_constant_use); // Update the module id bound to reflect the new instructions. fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id_for_load()); fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id_for_access_chain()); - - ir_context->InvalidateAnalysesExceptFor( - opt::IRContext::Analysis::kAnalysisNone); } protobufs::Transformation TransformationReplaceConstantWithUniform::ToMessage() diff --git a/source/fuzz/transformation_replace_constant_with_uniform.h b/source/fuzz/transformation_replace_constant_with_uniform.h index 9e097484..21210927 100644 --- a/source/fuzz/transformation_replace_constant_with_uniform.h +++ b/source/fuzz/transformation_replace_constant_with_uniform.h @@ -28,7 +28,7 @@ namespace fuzz { class TransformationReplaceConstantWithUniform : public Transformation { public: explicit TransformationReplaceConstantWithUniform( - const protobufs::TransformationReplaceConstantWithUniform& message); + protobufs::TransformationReplaceConstantWithUniform message); TransformationReplaceConstantWithUniform( protobufs::IdUseDescriptor id_use, diff --git a/source/fuzz/transformation_replace_copy_memory_with_load_store.cpp b/source/fuzz/transformation_replace_copy_memory_with_load_store.cpp index 936b0542..de9d1fd3 100644 --- a/source/fuzz/transformation_replace_copy_memory_with_load_store.cpp +++ b/source/fuzz/transformation_replace_copy_memory_with_load_store.cpp @@ -22,9 +22,8 @@ namespace fuzz { TransformationReplaceCopyMemoryWithLoadStore:: TransformationReplaceCopyMemoryWithLoadStore( - const spvtools::fuzz::protobufs:: - TransformationReplaceCopyMemoryWithLoadStore& message) - : message_(message) {} + protobufs::TransformationReplaceCopyMemoryWithLoadStore message) + : message_(std::move(message)) {} TransformationReplaceCopyMemoryWithLoadStore:: TransformationReplaceCopyMemoryWithLoadStore( diff --git a/source/fuzz/transformation_replace_copy_memory_with_load_store.h b/source/fuzz/transformation_replace_copy_memory_with_load_store.h index 67d349ff..55d1e064 100644 --- a/source/fuzz/transformation_replace_copy_memory_with_load_store.h +++ b/source/fuzz/transformation_replace_copy_memory_with_load_store.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationReplaceCopyMemoryWithLoadStore : public Transformation { public: explicit TransformationReplaceCopyMemoryWithLoadStore( - const protobufs::TransformationReplaceCopyMemoryWithLoadStore& message); + protobufs::TransformationReplaceCopyMemoryWithLoadStore message); TransformationReplaceCopyMemoryWithLoadStore( uint32_t fresh_id, const protobufs::InstructionDescriptor& diff --git a/source/fuzz/transformation_replace_copy_object_with_store_load.cpp b/source/fuzz/transformation_replace_copy_object_with_store_load.cpp index 54c99d51..e0643bf3 100644 --- a/source/fuzz/transformation_replace_copy_object_with_store_load.cpp +++ b/source/fuzz/transformation_replace_copy_object_with_store_load.cpp @@ -22,9 +22,8 @@ namespace fuzz { TransformationReplaceCopyObjectWithStoreLoad:: TransformationReplaceCopyObjectWithStoreLoad( - const spvtools::fuzz::protobufs:: - TransformationReplaceCopyObjectWithStoreLoad& message) - : message_(message) {} + protobufs::TransformationReplaceCopyObjectWithStoreLoad message) + : message_(std::move(message)) {} TransformationReplaceCopyObjectWithStoreLoad:: TransformationReplaceCopyObjectWithStoreLoad( @@ -88,6 +87,10 @@ void TransformationReplaceCopyObjectWithStoreLoad::Apply( assert(copy_object_instruction && copy_object_instruction->opcode() == SpvOpCopyObject && "The required OpCopyObject instruction must be defined."); + + opt::BasicBlock* enclosing_block = + ir_context->get_instr_block(copy_object_instruction); + // Get id used as a source by the OpCopyObject instruction. uint32_t src_operand = copy_object_instruction->GetSingleWordInOperand(0); // A pointer type instruction pointing to the value type must be defined. @@ -98,37 +101,46 @@ void TransformationReplaceCopyObjectWithStoreLoad::Apply( // Adds a global or local variable (according to the storage class). if (message_.variable_storage_class() == SpvStorageClassPrivate) { - fuzzerutil::AddGlobalVariable(ir_context, message_.fresh_variable_id(), - pointer_type_id, SpvStorageClassPrivate, - message_.variable_initializer_id()); + opt::Instruction* new_global = fuzzerutil::AddGlobalVariable( + ir_context, message_.fresh_variable_id(), pointer_type_id, + SpvStorageClassPrivate, message_.variable_initializer_id()); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_global); } else { - auto function_id = ir_context->get_instr_block(copy_object_instruction) - ->GetParent() - ->result_id(); - fuzzerutil::AddLocalVariable(ir_context, message_.fresh_variable_id(), - pointer_type_id, function_id, - message_.variable_initializer_id()); + opt::Function* function = + ir_context->get_instr_block(copy_object_instruction)->GetParent(); + opt::Instruction* new_local = fuzzerutil::AddLocalVariable( + ir_context, message_.fresh_variable_id(), pointer_type_id, + function->result_id(), message_.variable_initializer_id()); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_local); + ir_context->set_instr_block(new_local, &*function->begin()); } // First, insert the OpLoad instruction before the OpCopyObject instruction // and then insert the OpStore instruction before the OpLoad instruction. fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_variable_id()); - copy_object_instruction - ->InsertBefore(MakeUnique<opt::Instruction>( + opt::Instruction* load_instruction = + copy_object_instruction->InsertBefore(MakeUnique<opt::Instruction>( ir_context, SpvOpLoad, copy_object_instruction->type_id(), message_.copy_object_result_id(), opt::Instruction::OperandList( - {{SPV_OPERAND_TYPE_ID, {message_.fresh_variable_id()}}}))) - ->InsertBefore(MakeUnique<opt::Instruction>( + {{SPV_OPERAND_TYPE_ID, {message_.fresh_variable_id()}}}))); + opt::Instruction* store_instruction = + load_instruction->InsertBefore(MakeUnique<opt::Instruction>( ir_context, SpvOpStore, 0, 0, opt::Instruction::OperandList( {{SPV_OPERAND_TYPE_ID, {message_.fresh_variable_id()}}, {SPV_OPERAND_TYPE_ID, {src_operand}}}))); + + // Register the new instructions with the def-use manager, and record their + // enclosing block. + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(store_instruction); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(load_instruction); + ir_context->set_instr_block(store_instruction, enclosing_block); + ir_context->set_instr_block(load_instruction, enclosing_block); + // Remove the CopyObject instruction. ir_context->KillInst(copy_object_instruction); - ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); - if (!transformation_context->GetFactManager()->IdIsIrrelevant( message_.copy_object_result_id()) && !transformation_context->GetFactManager()->IdIsIrrelevant(src_operand)) { diff --git a/source/fuzz/transformation_replace_copy_object_with_store_load.h b/source/fuzz/transformation_replace_copy_object_with_store_load.h index a90905c5..8c5ce9e2 100644 --- a/source/fuzz/transformation_replace_copy_object_with_store_load.h +++ b/source/fuzz/transformation_replace_copy_object_with_store_load.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationReplaceCopyObjectWithStoreLoad : public Transformation { public: explicit TransformationReplaceCopyObjectWithStoreLoad( - const protobufs::TransformationReplaceCopyObjectWithStoreLoad& message); + protobufs::TransformationReplaceCopyObjectWithStoreLoad message); TransformationReplaceCopyObjectWithStoreLoad( uint32_t copy_object_result_id, uint32_t fresh_variable_id, diff --git a/source/fuzz/transformation_replace_id_with_synonym.cpp b/source/fuzz/transformation_replace_id_with_synonym.cpp index 24e079ff..92ce751d 100644 --- a/source/fuzz/transformation_replace_id_with_synonym.cpp +++ b/source/fuzz/transformation_replace_id_with_synonym.cpp @@ -26,9 +26,8 @@ namespace spvtools { namespace fuzz { TransformationReplaceIdWithSynonym::TransformationReplaceIdWithSynonym( - const spvtools::fuzz::protobufs::TransformationReplaceIdWithSynonym& - message) - : message_(message) {} + protobufs::TransformationReplaceIdWithSynonym message) + : message_(std::move(message)) {} TransformationReplaceIdWithSynonym::TransformationReplaceIdWithSynonym( protobufs::IdUseDescriptor id_use_descriptor, uint32_t synonymous_id) { @@ -95,8 +94,12 @@ void TransformationReplaceIdWithSynonym::Apply( instruction_to_change->SetInOperand( message_.id_use_descriptor().in_operand_index(), {message_.synonymous_id()}); - ir_context->InvalidateAnalysesExceptFor( - opt::IRContext::Analysis::kAnalysisNone); + ir_context->get_def_use_mgr()->EraseUseRecordsOfOperandIds( + instruction_to_change); + ir_context->get_def_use_mgr()->AnalyzeInstUse(instruction_to_change); + + // No analyses need to be invalidated, since the transformation is local to a + // block, and the def-use analysis has been updated. } protobufs::Transformation TransformationReplaceIdWithSynonym::ToMessage() diff --git a/source/fuzz/transformation_replace_id_with_synonym.h b/source/fuzz/transformation_replace_id_with_synonym.h index 3101710a..1ac636b4 100644 --- a/source/fuzz/transformation_replace_id_with_synonym.h +++ b/source/fuzz/transformation_replace_id_with_synonym.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationReplaceIdWithSynonym : public Transformation { public: explicit TransformationReplaceIdWithSynonym( - const protobufs::TransformationReplaceIdWithSynonym& message); + protobufs::TransformationReplaceIdWithSynonym message); TransformationReplaceIdWithSynonym( protobufs::IdUseDescriptor id_use_descriptor, uint32_t synonymous_id); diff --git a/source/fuzz/transformation_replace_irrelevant_id.cpp b/source/fuzz/transformation_replace_irrelevant_id.cpp index 27f56eba..a71f96a8 100644 --- a/source/fuzz/transformation_replace_irrelevant_id.cpp +++ b/source/fuzz/transformation_replace_irrelevant_id.cpp @@ -21,8 +21,8 @@ namespace spvtools { namespace fuzz { TransformationReplaceIrrelevantId::TransformationReplaceIrrelevantId( - const protobufs::TransformationReplaceIrrelevantId& message) - : message_(message) {} + protobufs::TransformationReplaceIrrelevantId message) + : message_(std::move(message)) {} TransformationReplaceIrrelevantId::TransformationReplaceIrrelevantId( const protobufs::IdUseDescriptor& id_use_descriptor, @@ -107,9 +107,12 @@ void TransformationReplaceIrrelevantId::Apply( message_.id_use_descriptor().in_operand_index(), {message_.replacement_id()}); - // Invalidate the analyses, since the usage of ids has been changed. - ir_context->InvalidateAnalysesExceptFor( - opt::IRContext::Analysis::kAnalysisNone); + ir_context->get_def_use_mgr()->EraseUseRecordsOfOperandIds( + instruction_to_change); + ir_context->get_def_use_mgr()->AnalyzeInstUse(instruction_to_change); + + // No analyses need to be invalidated, since the transformation is local to a + // block, and the def-use analysis has been updated. } protobufs::Transformation TransformationReplaceIrrelevantId::ToMessage() const { diff --git a/source/fuzz/transformation_replace_irrelevant_id.h b/source/fuzz/transformation_replace_irrelevant_id.h index 35b1987d..e6210b4d 100644 --- a/source/fuzz/transformation_replace_irrelevant_id.h +++ b/source/fuzz/transformation_replace_irrelevant_id.h @@ -23,7 +23,7 @@ namespace fuzz { class TransformationReplaceIrrelevantId : public Transformation { public: explicit TransformationReplaceIrrelevantId( - const protobufs::TransformationReplaceIrrelevantId& message); + protobufs::TransformationReplaceIrrelevantId message); TransformationReplaceIrrelevantId( const protobufs::IdUseDescriptor& id_use_descriptor, diff --git a/source/fuzz/transformation_replace_linear_algebra_instruction.cpp b/source/fuzz/transformation_replace_linear_algebra_instruction.cpp index fc73a262..2430ccab 100644 --- a/source/fuzz/transformation_replace_linear_algebra_instruction.cpp +++ b/source/fuzz/transformation_replace_linear_algebra_instruction.cpp @@ -22,9 +22,8 @@ namespace fuzz { TransformationReplaceLinearAlgebraInstruction:: TransformationReplaceLinearAlgebraInstruction( - const spvtools::fuzz::protobufs:: - TransformationReplaceLinearAlgebraInstruction& message) - : message_(message) {} + protobufs::TransformationReplaceLinearAlgebraInstruction message) + : message_(std::move(message)) {} TransformationReplaceLinearAlgebraInstruction:: TransformationReplaceLinearAlgebraInstruction( diff --git a/source/fuzz/transformation_replace_linear_algebra_instruction.h b/source/fuzz/transformation_replace_linear_algebra_instruction.h index 45f4aa64..0f0c18bf 100644 --- a/source/fuzz/transformation_replace_linear_algebra_instruction.h +++ b/source/fuzz/transformation_replace_linear_algebra_instruction.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationReplaceLinearAlgebraInstruction : public Transformation { public: explicit TransformationReplaceLinearAlgebraInstruction( - const protobufs::TransformationReplaceLinearAlgebraInstruction& message); + protobufs::TransformationReplaceLinearAlgebraInstruction message); TransformationReplaceLinearAlgebraInstruction( const std::vector<uint32_t>& fresh_ids, diff --git a/source/fuzz/transformation_replace_load_store_with_copy_memory.cpp b/source/fuzz/transformation_replace_load_store_with_copy_memory.cpp index 6067fca8..e75337f6 100644 --- a/source/fuzz/transformation_replace_load_store_with_copy_memory.cpp +++ b/source/fuzz/transformation_replace_load_store_with_copy_memory.cpp @@ -29,9 +29,8 @@ const uint32_t kOpLoadOperandIndexSourceVariable = 2; TransformationReplaceLoadStoreWithCopyMemory:: TransformationReplaceLoadStoreWithCopyMemory( - const spvtools::fuzz::protobufs:: - TransformationReplaceLoadStoreWithCopyMemory& message) - : message_(message) {} + protobufs::TransformationReplaceLoadStoreWithCopyMemory message) + : message_(std::move(message)) {} TransformationReplaceLoadStoreWithCopyMemory:: TransformationReplaceLoadStoreWithCopyMemory( diff --git a/source/fuzz/transformation_replace_load_store_with_copy_memory.h b/source/fuzz/transformation_replace_load_store_with_copy_memory.h index 4dd728e7..bb4d27ef 100644 --- a/source/fuzz/transformation_replace_load_store_with_copy_memory.h +++ b/source/fuzz/transformation_replace_load_store_with_copy_memory.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationReplaceLoadStoreWithCopyMemory : public Transformation { public: explicit TransformationReplaceLoadStoreWithCopyMemory( - const protobufs::TransformationReplaceLoadStoreWithCopyMemory& message); + protobufs::TransformationReplaceLoadStoreWithCopyMemory message); TransformationReplaceLoadStoreWithCopyMemory( const protobufs::InstructionDescriptor& load_instruction_descriptor, diff --git a/source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.cpp b/source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.cpp index f13af7e7..84ca1aba 100644 --- a/source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.cpp +++ b/source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.cpp @@ -21,9 +21,8 @@ namespace fuzz { TransformationReplaceOpPhiIdFromDeadPredecessor:: TransformationReplaceOpPhiIdFromDeadPredecessor( - const protobufs::TransformationReplaceOpPhiIdFromDeadPredecessor& - message) - : message_(message) {} + protobufs::TransformationReplaceOpPhiIdFromDeadPredecessor message) + : message_(std::move(message)) {} TransformationReplaceOpPhiIdFromDeadPredecessor:: TransformationReplaceOpPhiIdFromDeadPredecessor(uint32_t opphi_id, diff --git a/source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.h b/source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.h index d26b6b0a..87ce8ad1 100644 --- a/source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.h +++ b/source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.h @@ -23,8 +23,7 @@ namespace fuzz { class TransformationReplaceOpPhiIdFromDeadPredecessor : public Transformation { public: explicit TransformationReplaceOpPhiIdFromDeadPredecessor( - const protobufs::TransformationReplaceOpPhiIdFromDeadPredecessor& - message); + protobufs::TransformationReplaceOpPhiIdFromDeadPredecessor message); TransformationReplaceOpPhiIdFromDeadPredecessor(uint32_t opphi_id, uint32_t pred_label_id, diff --git a/source/fuzz/transformation_replace_opselect_with_conditional_branch.cpp b/source/fuzz/transformation_replace_opselect_with_conditional_branch.cpp index 7160d4d5..c0e6e449 100644 --- a/source/fuzz/transformation_replace_opselect_with_conditional_branch.cpp +++ b/source/fuzz/transformation_replace_opselect_with_conditional_branch.cpp @@ -20,9 +20,8 @@ namespace spvtools { namespace fuzz { TransformationReplaceOpSelectWithConditionalBranch:: TransformationReplaceOpSelectWithConditionalBranch( - const spvtools::fuzz::protobufs:: - TransformationReplaceOpSelectWithConditionalBranch& message) - : message_(message) {} + protobufs::TransformationReplaceOpSelectWithConditionalBranch message) + : message_(std::move(message)) {} TransformationReplaceOpSelectWithConditionalBranch:: TransformationReplaceOpSelectWithConditionalBranch( diff --git a/source/fuzz/transformation_replace_opselect_with_conditional_branch.h b/source/fuzz/transformation_replace_opselect_with_conditional_branch.h index 8ee5c7f1..ec926c65 100644 --- a/source/fuzz/transformation_replace_opselect_with_conditional_branch.h +++ b/source/fuzz/transformation_replace_opselect_with_conditional_branch.h @@ -24,8 +24,7 @@ class TransformationReplaceOpSelectWithConditionalBranch : public Transformation { public: explicit TransformationReplaceOpSelectWithConditionalBranch( - const protobufs::TransformationReplaceOpSelectWithConditionalBranch& - message); + protobufs::TransformationReplaceOpSelectWithConditionalBranch message); TransformationReplaceOpSelectWithConditionalBranch(uint32_t select_id, uint32_t true_block_id, diff --git a/source/fuzz/transformation_replace_parameter_with_global.cpp b/source/fuzz/transformation_replace_parameter_with_global.cpp index cdf7645f..caf67168 100644 --- a/source/fuzz/transformation_replace_parameter_with_global.cpp +++ b/source/fuzz/transformation_replace_parameter_with_global.cpp @@ -23,8 +23,8 @@ namespace fuzz { TransformationReplaceParameterWithGlobal:: TransformationReplaceParameterWithGlobal( - const protobufs::TransformationReplaceParameterWithGlobal& message) - : message_(message) {} + protobufs::TransformationReplaceParameterWithGlobal message) + : message_(std::move(message)) {} TransformationReplaceParameterWithGlobal:: TransformationReplaceParameterWithGlobal( diff --git a/source/fuzz/transformation_replace_parameter_with_global.h b/source/fuzz/transformation_replace_parameter_with_global.h index c2d5f8ff..38a9c17f 100644 --- a/source/fuzz/transformation_replace_parameter_with_global.h +++ b/source/fuzz/transformation_replace_parameter_with_global.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationReplaceParameterWithGlobal : public Transformation { public: explicit TransformationReplaceParameterWithGlobal( - const protobufs::TransformationReplaceParameterWithGlobal& message); + protobufs::TransformationReplaceParameterWithGlobal message); TransformationReplaceParameterWithGlobal(uint32_t function_type_fresh_id, uint32_t parameter_id, diff --git a/source/fuzz/transformation_replace_params_with_struct.cpp b/source/fuzz/transformation_replace_params_with_struct.cpp index 0a135e56..13eeccb4 100644 --- a/source/fuzz/transformation_replace_params_with_struct.cpp +++ b/source/fuzz/transformation_replace_params_with_struct.cpp @@ -22,8 +22,8 @@ namespace spvtools { namespace fuzz { TransformationReplaceParamsWithStruct::TransformationReplaceParamsWithStruct( - const protobufs::TransformationReplaceParamsWithStruct& message) - : message_(message) {} + protobufs::TransformationReplaceParamsWithStruct message) + : message_(std::move(message)) {} TransformationReplaceParamsWithStruct::TransformationReplaceParamsWithStruct( const std::vector<uint32_t>& parameter_id, uint32_t fresh_function_type_id, diff --git a/source/fuzz/transformation_replace_params_with_struct.h b/source/fuzz/transformation_replace_params_with_struct.h index afa6b14f..705f3405 100644 --- a/source/fuzz/transformation_replace_params_with_struct.h +++ b/source/fuzz/transformation_replace_params_with_struct.h @@ -28,7 +28,7 @@ namespace fuzz { class TransformationReplaceParamsWithStruct : public Transformation { public: explicit TransformationReplaceParamsWithStruct( - const protobufs::TransformationReplaceParamsWithStruct& message); + protobufs::TransformationReplaceParamsWithStruct message); TransformationReplaceParamsWithStruct( const std::vector<uint32_t>& parameter_id, diff --git a/source/fuzz/transformation_set_function_control.cpp b/source/fuzz/transformation_set_function_control.cpp index 8ab9b8cb..02a8c9fe 100644 --- a/source/fuzz/transformation_set_function_control.cpp +++ b/source/fuzz/transformation_set_function_control.cpp @@ -18,8 +18,8 @@ namespace spvtools { namespace fuzz { TransformationSetFunctionControl::TransformationSetFunctionControl( - const spvtools::fuzz::protobufs::TransformationSetFunctionControl& message) - : message_(message) {} + protobufs::TransformationSetFunctionControl message) + : message_(std::move(message)) {} TransformationSetFunctionControl::TransformationSetFunctionControl( uint32_t function_id, uint32_t function_control) { diff --git a/source/fuzz/transformation_set_function_control.h b/source/fuzz/transformation_set_function_control.h index 2952cc6d..2e16e1ca 100644 --- a/source/fuzz/transformation_set_function_control.h +++ b/source/fuzz/transformation_set_function_control.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationSetFunctionControl : public Transformation { public: explicit TransformationSetFunctionControl( - const protobufs::TransformationSetFunctionControl& message); + protobufs::TransformationSetFunctionControl message); TransformationSetFunctionControl(uint32_t function_id, uint32_t function_control); diff --git a/source/fuzz/transformation_set_loop_control.cpp b/source/fuzz/transformation_set_loop_control.cpp index b2180d8a..14499606 100644 --- a/source/fuzz/transformation_set_loop_control.cpp +++ b/source/fuzz/transformation_set_loop_control.cpp @@ -18,8 +18,8 @@ namespace spvtools { namespace fuzz { TransformationSetLoopControl::TransformationSetLoopControl( - const spvtools::fuzz::protobufs::TransformationSetLoopControl& message) - : message_(message) {} + protobufs::TransformationSetLoopControl message) + : message_(std::move(message)) {} TransformationSetLoopControl::TransformationSetLoopControl( uint32_t block_id, uint32_t loop_control, uint32_t peel_count, @@ -77,12 +77,14 @@ bool TransformationSetLoopControl::IsApplicable( } } - if ((message_.loop_control() & - (SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask)) && - !(PeelCountIsSupported(ir_context) && - PartialCountIsSupported(ir_context))) { - // At least one of PeelCount or PartialCount is used, but the SPIR-V version - // in question does not support these loop controls. + // Check that PeelCount and PartialCount are supported if used. + if ((message_.loop_control() & SpvLoopControlPeelCountMask) && + !PeelCountIsSupported(ir_context)) { + return false; + } + + if ((message_.loop_control() & SpvLoopControlPartialCountMask) && + !PartialCountIsSupported(ir_context)) { return false; } @@ -183,14 +185,16 @@ bool TransformationSetLoopControl::LoopControlBitIsAddedByTransformation( bool TransformationSetLoopControl::PartialCountIsSupported( opt::IRContext* ir_context) { - // TODO(afd): We capture the universal environments for which this loop - // control is definitely not supported. The check should be refined on - // demand for other target environments. + // TODO(afd): We capture the environments for which this loop control is + // definitely not supported. The check should be refined on demand for other + // target environments. switch (ir_context->grammar().target_env()) { 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_VULKAN_1_0: + case SPV_ENV_VULKAN_1_1: return false; default: return true; @@ -199,14 +203,16 @@ bool TransformationSetLoopControl::PartialCountIsSupported( bool TransformationSetLoopControl::PeelCountIsSupported( opt::IRContext* ir_context) { - // TODO(afd): We capture the universal environments for which this loop - // control is definitely not supported. The check should be refined on - // demand for other target environments. + // TODO(afd): We capture the environments for which this loop control is + // definitely not supported. The check should be refined on demand for other + // target environments. switch (ir_context->grammar().target_env()) { 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_VULKAN_1_0: + case SPV_ENV_VULKAN_1_1: return false; default: return true; diff --git a/source/fuzz/transformation_set_loop_control.h b/source/fuzz/transformation_set_loop_control.h index c3480b19..bc17c8a4 100644 --- a/source/fuzz/transformation_set_loop_control.h +++ b/source/fuzz/transformation_set_loop_control.h @@ -29,7 +29,7 @@ class TransformationSetLoopControl : public Transformation { const static uint32_t kLoopControlFirstLiteralInOperandIndex = 3; explicit TransformationSetLoopControl( - const protobufs::TransformationSetLoopControl& message); + protobufs::TransformationSetLoopControl message); TransformationSetLoopControl(uint32_t block_id, uint32_t loop_control, uint32_t peel_count, uint32_t partial_count); diff --git a/source/fuzz/transformation_set_memory_operands_mask.cpp b/source/fuzz/transformation_set_memory_operands_mask.cpp index deb207ae..5a986ad2 100644 --- a/source/fuzz/transformation_set_memory_operands_mask.cpp +++ b/source/fuzz/transformation_set_memory_operands_mask.cpp @@ -29,9 +29,8 @@ const uint32_t kOpCopyMemorySizedFirstMemoryOperandsMaskIndex = 3; } // namespace TransformationSetMemoryOperandsMask::TransformationSetMemoryOperandsMask( - const spvtools::fuzz::protobufs::TransformationSetMemoryOperandsMask& - message) - : message_(message) {} + protobufs::TransformationSetMemoryOperandsMask message) + : message_(std::move(message)) {} TransformationSetMemoryOperandsMask::TransformationSetMemoryOperandsMask( const protobufs::InstructionDescriptor& memory_access_instruction, @@ -53,7 +52,9 @@ bool TransformationSetMemoryOperandsMask::IsApplicable( SpvOpCopyMemory || message_.memory_access_instruction().target_instruction_opcode() == SpvOpCopyMemorySized); - assert(MultipleMemoryOperandMasksAreSupported(ir_context)); + assert(MultipleMemoryOperandMasksAreSupported(ir_context) && + "Multiple memory operand masks are not supported for this SPIR-V " + "version."); } auto instruction = @@ -205,14 +206,16 @@ uint32_t TransformationSetMemoryOperandsMask::GetInOperandIndexForMask( bool TransformationSetMemoryOperandsMask:: MultipleMemoryOperandMasksAreSupported(opt::IRContext* ir_context) { - // TODO(afd): We capture the universal environments for which this loop - // control is definitely not supported. The check should be refined on - // demand for other target environments. + // TODO(afd): We capture the environments for which this loop control is + // definitely not supported. The check should be refined on demand for other + // target environments. switch (ir_context->grammar().target_env()) { 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_VULKAN_1_0: + case SPV_ENV_VULKAN_1_1: return false; default: return true; diff --git a/source/fuzz/transformation_set_memory_operands_mask.h b/source/fuzz/transformation_set_memory_operands_mask.h index 7357b1ad..c52fbdba 100644 --- a/source/fuzz/transformation_set_memory_operands_mask.h +++ b/source/fuzz/transformation_set_memory_operands_mask.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationSetMemoryOperandsMask : public Transformation { public: explicit TransformationSetMemoryOperandsMask( - const protobufs::TransformationSetMemoryOperandsMask& message); + protobufs::TransformationSetMemoryOperandsMask message); TransformationSetMemoryOperandsMask( const protobufs::InstructionDescriptor& memory_access_instruction, diff --git a/source/fuzz/transformation_set_selection_control.cpp b/source/fuzz/transformation_set_selection_control.cpp index 625187ea..6dddbdf8 100644 --- a/source/fuzz/transformation_set_selection_control.cpp +++ b/source/fuzz/transformation_set_selection_control.cpp @@ -18,8 +18,8 @@ namespace spvtools { namespace fuzz { TransformationSetSelectionControl::TransformationSetSelectionControl( - const spvtools::fuzz::protobufs::TransformationSetSelectionControl& message) - : message_(message) {} + protobufs::TransformationSetSelectionControl message) + : message_(std::move(message)) {} TransformationSetSelectionControl::TransformationSetSelectionControl( uint32_t block_id, uint32_t selection_control) { diff --git a/source/fuzz/transformation_set_selection_control.h b/source/fuzz/transformation_set_selection_control.h index 56b58852..93b19049 100644 --- a/source/fuzz/transformation_set_selection_control.h +++ b/source/fuzz/transformation_set_selection_control.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationSetSelectionControl : public Transformation { public: explicit TransformationSetSelectionControl( - const protobufs::TransformationSetSelectionControl& message); + protobufs::TransformationSetSelectionControl message); TransformationSetSelectionControl(uint32_t block_id, uint32_t selection_control); diff --git a/source/fuzz/transformation_split_block.cpp b/source/fuzz/transformation_split_block.cpp index b383c405..e15dffa3 100644 --- a/source/fuzz/transformation_split_block.cpp +++ b/source/fuzz/transformation_split_block.cpp @@ -24,8 +24,8 @@ namespace spvtools { namespace fuzz { TransformationSplitBlock::TransformationSplitBlock( - const spvtools::fuzz::protobufs::TransformationSplitBlock& message) - : message_(message) {} + protobufs::TransformationSplitBlock message) + : message_(std::move(message)) {} TransformationSplitBlock::TransformationSplitBlock( const protobufs::InstructionDescriptor& instruction_to_split_before, @@ -109,24 +109,37 @@ void TransformationSplitBlock::Apply( split_before); // The split does not automatically add a branch between the two parts of // the original block, so we add one. - block_to_split->AddInstruction(MakeUnique<opt::Instruction>( + auto branch_instruction = MakeUnique<opt::Instruction>( ir_context, SpvOpBranch, 0, 0, std::initializer_list<opt::Operand>{opt::Operand( - spv_operand_type_t::SPV_OPERAND_TYPE_ID, {message_.fresh_id()})})); + spv_operand_type_t::SPV_OPERAND_TYPE_ID, {message_.fresh_id()})}); + auto branch_instruction_ptr = branch_instruction.get(); + block_to_split->AddInstruction(std::move(branch_instruction)); + + // Inform the def-use manager about the branch instruction, and record its + // block. + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(branch_instruction_ptr); + ir_context->set_instr_block(branch_instruction_ptr, block_to_split); + // If we split before OpPhi instructions, we need to update their // predecessor operand so that the block they used to be inside is now the // predecessor. - new_bb->ForEachPhiInst([block_to_split](opt::Instruction* phi_inst) { + new_bb->ForEachPhiInst([block_to_split, + ir_context](opt::Instruction* phi_inst) { assert( phi_inst->NumInOperands() == 2 && "Precondition: a block can only be split before an OpPhi if the block" "has exactly one predecessor."); phi_inst->SetInOperand(1, {block_to_split->id()}); + ir_context->UpdateDefUse(phi_inst); }); - // Invalidate all analyses + // We have updated the def-use manager and the instruction to block mapping, + // but other analyses (especially control flow-related ones) need to be + // recomputed. ir_context->InvalidateAnalysesExceptFor( - opt::IRContext::Analysis::kAnalysisNone); + opt::IRContext::Analysis::kAnalysisDefUse | + opt::IRContext::Analysis::kAnalysisInstrToBlockMapping); // If the block being split was dead, the new block arising from the split is // also dead. diff --git a/source/fuzz/transformation_split_block.h b/source/fuzz/transformation_split_block.h index 27bf6f8c..ace77b5b 100644 --- a/source/fuzz/transformation_split_block.h +++ b/source/fuzz/transformation_split_block.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationSplitBlock : public Transformation { public: explicit TransformationSplitBlock( - const protobufs::TransformationSplitBlock& message); + protobufs::TransformationSplitBlock message); TransformationSplitBlock( const protobufs::InstructionDescriptor& instruction_to_split_before, diff --git a/source/fuzz/transformation_store.cpp b/source/fuzz/transformation_store.cpp index 460ca016..f8c6d013 100644 --- a/source/fuzz/transformation_store.cpp +++ b/source/fuzz/transformation_store.cpp @@ -20,9 +20,8 @@ namespace spvtools { namespace fuzz { -TransformationStore::TransformationStore( - const spvtools::fuzz::protobufs::TransformationStore& message) - : message_(message) {} +TransformationStore::TransformationStore(protobufs::TransformationStore message) + : message_(std::move(message)) {} TransformationStore::TransformationStore( uint32_t pointer_id, uint32_t value_id, @@ -110,13 +109,20 @@ bool TransformationStore::IsApplicable( void TransformationStore::Apply(opt::IRContext* ir_context, TransformationContext* /*unused*/) const { - FindInstruction(message_.instruction_to_insert_before(), ir_context) - ->InsertBefore(MakeUnique<opt::Instruction>( - ir_context, SpvOpStore, 0, 0, - opt::Instruction::OperandList( - {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}, - {SPV_OPERAND_TYPE_ID, {message_.value_id()}}}))); - ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + auto insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + auto new_instruction = MakeUnique<opt::Instruction>( + ir_context, SpvOpStore, 0, 0, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}, + {SPV_OPERAND_TYPE_ID, {message_.value_id()}}})); + auto new_instruction_ptr = new_instruction.get(); + insert_before->InsertBefore(std::move(new_instruction)); + // Inform the def-use manager about the new instruction and record its basic + // block. + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr); + ir_context->set_instr_block(new_instruction_ptr, + ir_context->get_instr_block(insert_before)); } protobufs::Transformation TransformationStore::ToMessage() const { diff --git a/source/fuzz/transformation_store.h b/source/fuzz/transformation_store.h index 70520489..18ba1d7d 100644 --- a/source/fuzz/transformation_store.h +++ b/source/fuzz/transformation_store.h @@ -25,7 +25,7 @@ namespace fuzz { class TransformationStore : public Transformation { public: - explicit TransformationStore(const protobufs::TransformationStore& message); + explicit TransformationStore(protobufs::TransformationStore message); TransformationStore( uint32_t pointer_id, uint32_t value_id, diff --git a/source/fuzz/transformation_swap_commutable_operands.cpp b/source/fuzz/transformation_swap_commutable_operands.cpp index b8bdb790..a02e95a0 100644 --- a/source/fuzz/transformation_swap_commutable_operands.cpp +++ b/source/fuzz/transformation_swap_commutable_operands.cpp @@ -21,9 +21,8 @@ namespace spvtools { namespace fuzz { TransformationSwapCommutableOperands::TransformationSwapCommutableOperands( - const spvtools::fuzz::protobufs::TransformationSwapCommutableOperands& - message) - : message_(message) {} + protobufs::TransformationSwapCommutableOperands message) + : message_(std::move(message)) {} TransformationSwapCommutableOperands::TransformationSwapCommutableOperands( const protobufs::InstructionDescriptor& instruction_descriptor) { diff --git a/source/fuzz/transformation_swap_commutable_operands.h b/source/fuzz/transformation_swap_commutable_operands.h index c291c3ea..5e211f19 100644 --- a/source/fuzz/transformation_swap_commutable_operands.h +++ b/source/fuzz/transformation_swap_commutable_operands.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationSwapCommutableOperands : public Transformation { public: explicit TransformationSwapCommutableOperands( - const protobufs::TransformationSwapCommutableOperands& message); + protobufs::TransformationSwapCommutableOperands message); TransformationSwapCommutableOperands( const protobufs::InstructionDescriptor& instruction_descriptor); diff --git a/source/fuzz/transformation_swap_conditional_branch_operands.cpp b/source/fuzz/transformation_swap_conditional_branch_operands.cpp index 866fee66..340836d1 100644 --- a/source/fuzz/transformation_swap_conditional_branch_operands.cpp +++ b/source/fuzz/transformation_swap_conditional_branch_operands.cpp @@ -22,9 +22,8 @@ namespace fuzz { TransformationSwapConditionalBranchOperands:: TransformationSwapConditionalBranchOperands( - const spvtools::fuzz::protobufs:: - TransformationSwapConditionalBranchOperands& message) - : message_(message) {} + protobufs::TransformationSwapConditionalBranchOperands message) + : message_(std::move(message)) {} TransformationSwapConditionalBranchOperands:: TransformationSwapConditionalBranchOperands( @@ -70,11 +69,13 @@ void TransformationSwapConditionalBranchOperands::Apply( // We are swapping the labels in OpBranchConditional. This means that we must // invert the guard as well. We are using OpLogicalNot for that purpose here. - iter.InsertBefore(MakeUnique<opt::Instruction>( + auto new_instruction = MakeUnique<opt::Instruction>( ir_context, SpvOpLogicalNot, condition_inst->type_id(), message_.fresh_id(), opt::Instruction::OperandList{ - {SPV_OPERAND_TYPE_ID, {condition_inst->result_id()}}})); + {SPV_OPERAND_TYPE_ID, {condition_inst->result_id()}}}); + auto new_instruction_ptr = new_instruction.get(); + iter.InsertBefore(std::move(new_instruction)); fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); @@ -89,9 +90,13 @@ void TransformationSwapConditionalBranchOperands::Apply( std::swap(branch_inst->GetInOperand(3), branch_inst->GetInOperand(4)); } - // Make sure the changes are analyzed. - ir_context->InvalidateAnalysesExceptFor( - opt::IRContext::Analysis::kAnalysisNone); + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr); + ir_context->set_instr_block(new_instruction_ptr, block); + ir_context->get_def_use_mgr()->EraseUseRecordsOfOperandIds(branch_inst); + ir_context->get_def_use_mgr()->AnalyzeInstUse(branch_inst); + + // No analyses need to be invalidated since the transformation is local to a + // block and the def-use and instruction-to-block mappings have been updated. } protobufs::Transformation diff --git a/source/fuzz/transformation_swap_conditional_branch_operands.h b/source/fuzz/transformation_swap_conditional_branch_operands.h index 022c54a7..165ab806 100644 --- a/source/fuzz/transformation_swap_conditional_branch_operands.h +++ b/source/fuzz/transformation_swap_conditional_branch_operands.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationSwapConditionalBranchOperands : public Transformation { public: explicit TransformationSwapConditionalBranchOperands( - const protobufs::TransformationSwapConditionalBranchOperands& message); + protobufs::TransformationSwapConditionalBranchOperands message); TransformationSwapConditionalBranchOperands( const protobufs::InstructionDescriptor& instruction_descriptor, diff --git a/source/fuzz/transformation_swap_function_variables.cpp b/source/fuzz/transformation_swap_function_variables.cpp new file mode 100644 index 00000000..aec32fe1 --- /dev/null +++ b/source/fuzz/transformation_swap_function_variables.cpp @@ -0,0 +1,93 @@ +// Copyright (c) 2021 Mostafa Ashraf +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_swap_function_variables.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationSwapFunctionVariables::TransformationSwapFunctionVariables( + protobufs::TransformationSwapFunctionVariables message) + : message_(std::move(message)) {} + +TransformationSwapFunctionVariables::TransformationSwapFunctionVariables( + uint32_t result_id1, uint32_t result_id2) { + message_.set_result_id1(result_id1); + message_.set_result_id2(result_id2); +} + +bool TransformationSwapFunctionVariables::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + uint32_t result_id1 = message_.result_id1(); + uint32_t result_id2 = message_.result_id2(); + + assert((result_id1 != result_id2) && "Two results ids are equal"); + + // The result ids used in the message must refer to instructions. + auto instruction1 = ir_context->get_def_use_mgr()->GetDef(result_id1); + auto instruction2 = ir_context->get_def_use_mgr()->GetDef(result_id2); + if (instruction1 == nullptr || instruction2 == nullptr) { + return false; + } + // Both instructions must be variables. + if (instruction1->opcode() != SpvOpVariable || + instruction2->opcode() != SpvOpVariable) { + return false; + } + + // Both variable instructions must be in some basic block (as they are + // function-local variables), and they must be in the same block (as they need + // to be variables of the same function). + auto* block_1 = ir_context->get_instr_block(result_id1); + auto* block_2 = ir_context->get_instr_block(result_id2); + if (block_1 == nullptr || block_2 == nullptr) { + return false; + } + + return block_1 == block_2; +} + +void TransformationSwapFunctionVariables::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + // The result ids used in the message must refer to instructions. + auto instruction1 = + ir_context->get_def_use_mgr()->GetDef(message_.result_id1()); + auto instruction2 = + ir_context->get_def_use_mgr()->GetDef(message_.result_id2()); + + std::unique_ptr<opt::Instruction> temp_instruction = + MakeUnique<opt::Instruction>(); + + temp_instruction->InsertBefore(instruction1); + instruction1->InsertAfter(instruction2); + instruction2->InsertAfter(temp_instruction.get()); + temp_instruction->RemoveFromList(); +} + +protobufs::Transformation TransformationSwapFunctionVariables::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_swap_function_variables() = message_; + return result; +} + +std::unordered_set<uint32_t> TransformationSwapFunctionVariables::GetFreshIds() + const { + return std::unordered_set<uint32_t>(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/transformation_swap_function_variables.h b/source/fuzz/transformation_swap_function_variables.h new file mode 100644 index 00000000..eb383f4f --- /dev/null +++ b/source/fuzz/transformation_swap_function_variables.h @@ -0,0 +1,57 @@ +// Copyright (c) 2021 Mostafa Ashraf +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_SWAP_FUNCTION_VARIABLES_H_ +#define SOURCE_FUZZ_TRANSFORMATION_SWAP_FUNCTION_VARIABLES_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +// A transformation that swaps two variable declaration instructions that appear +// in the same function. +class TransformationSwapFunctionVariables : public Transformation { + public: + explicit TransformationSwapFunctionVariables( + protobufs::TransformationSwapFunctionVariables message); + + TransformationSwapFunctionVariables(uint32_t result_id1, uint32_t result_id2); + + // - |message_.result_id1| and |message_.result_id2| must be the ids of + // distinct OpVariable instructions appearing in the same function. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Swaps two OpVariable instructions with result ids |message_.result_id1| + // and |message_.result_id2|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + protobufs::Transformation ToMessage() const override; + + std::unordered_set<uint32_t> GetFreshIds() const override; + + private: + protobufs::TransformationSwapFunctionVariables message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_SWAP_FUNCTION_VARIABLES_H_ diff --git a/source/fuzz/transformation_swap_two_functions.cpp b/source/fuzz/transformation_swap_two_functions.cpp new file mode 100644 index 00000000..85d9e79c --- /dev/null +++ b/source/fuzz/transformation_swap_two_functions.cpp @@ -0,0 +1,72 @@ +// Copyright (c) 2021 Shiyu Liu +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_swap_two_functions.h" + +#include "source/opt/function.h" +#include "source/opt/module.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationSwapTwoFunctions::TransformationSwapTwoFunctions( + protobufs::TransformationSwapTwoFunctions message) + : message_(std::move(message)) {} + +TransformationSwapTwoFunctions::TransformationSwapTwoFunctions(uint32_t id1, + uint32_t id2) { + assert(id1 != id2 && "The two function ids cannot be the same."); + message_.set_function_id1(id1); + message_.set_function_id2(id2); +} + +bool TransformationSwapTwoFunctions::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + auto func1_ptr = ir_context->GetFunction(message_.function_id1()); + auto func2_ptr = ir_context->GetFunction(message_.function_id2()); + return func1_ptr != nullptr && func2_ptr != nullptr; +} + +void TransformationSwapTwoFunctions::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + opt::Module::iterator func1_it = + fuzzerutil::GetFunctionIterator(ir_context, message_.function_id1()); + opt::Module::iterator func2_it = + fuzzerutil::GetFunctionIterator(ir_context, message_.function_id2()); + + assert(func1_it != ir_context->module()->end() && + "Could not find function 1."); + assert(func2_it != ir_context->module()->end() && + "Could not find function 2."); + + // Two function pointers are all set, swap the two functions within the + // module. + std::iter_swap(func1_it.Get(), func2_it.Get()); +} + +protobufs::Transformation TransformationSwapTwoFunctions::ToMessage() const { + protobufs::Transformation result; + *result.mutable_swap_two_functions() = message_; + return result; +} + +std::unordered_set<uint32_t> TransformationSwapTwoFunctions::GetFreshIds() + const { + return std::unordered_set<uint32_t>(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/transformation_swap_two_functions.h b/source/fuzz/transformation_swap_two_functions.h new file mode 100644 index 00000000..1b6d6e80 --- /dev/null +++ b/source/fuzz/transformation_swap_two_functions.h @@ -0,0 +1,56 @@ +// Copyright (c) 2021 Shiyu Liu +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_SWAP_TWO_FUNCTIONS_H_ +#define SOURCE_FUZZ_TRANSFORMATION_SWAP_TWO_FUNCTIONS_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationSwapTwoFunctions : public Transformation { + public: + explicit TransformationSwapTwoFunctions( + protobufs::TransformationSwapTwoFunctions message); + + TransformationSwapTwoFunctions(uint32_t function_id1, uint32_t function_id2); + + // |function_id1| and |function_id1| should all be existing ids. + // Swap function operation is only permitted if: + // - both ids must be ids of functions. + // - both ids can be found in the module. + // - function_id1 and function_id2 are not the same. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // OpFunction with |function_id1| and |function_id1| are swapped. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set<uint32_t> GetFreshIds() const override; + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationSwapTwoFunctions message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_SWAP_TWO_FUNCTIONS_H_ diff --git a/source/fuzz/transformation_toggle_access_chain_instruction.cpp b/source/fuzz/transformation_toggle_access_chain_instruction.cpp index 1952a343..34523fe8 100644 --- a/source/fuzz/transformation_toggle_access_chain_instruction.cpp +++ b/source/fuzz/transformation_toggle_access_chain_instruction.cpp @@ -22,9 +22,8 @@ namespace fuzz { TransformationToggleAccessChainInstruction:: TransformationToggleAccessChainInstruction( - const spvtools::fuzz::protobufs:: - TransformationToggleAccessChainInstruction& message) - : message_(message) {} + protobufs::TransformationToggleAccessChainInstruction message) + : message_(std::move(message)) {} TransformationToggleAccessChainInstruction:: TransformationToggleAccessChainInstruction( diff --git a/source/fuzz/transformation_toggle_access_chain_instruction.h b/source/fuzz/transformation_toggle_access_chain_instruction.h index 977b0d7a..be2718b0 100644 --- a/source/fuzz/transformation_toggle_access_chain_instruction.h +++ b/source/fuzz/transformation_toggle_access_chain_instruction.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationToggleAccessChainInstruction : public Transformation { public: explicit TransformationToggleAccessChainInstruction( - const protobufs::TransformationToggleAccessChainInstruction& message); + protobufs::TransformationToggleAccessChainInstruction message); TransformationToggleAccessChainInstruction( const protobufs::InstructionDescriptor& instruction_descriptor); diff --git a/source/fuzz/transformation_vector_shuffle.cpp b/source/fuzz/transformation_vector_shuffle.cpp index 05af18e6..ac0e3ccb 100644 --- a/source/fuzz/transformation_vector_shuffle.cpp +++ b/source/fuzz/transformation_vector_shuffle.cpp @@ -21,8 +21,8 @@ namespace spvtools { namespace fuzz { TransformationVectorShuffle::TransformationVectorShuffle( - const spvtools::fuzz::protobufs::TransformationVectorShuffle& message) - : message_(message) {} + protobufs::TransformationVectorShuffle message) + : message_(std::move(message)) {} TransformationVectorShuffle::TransformationVectorShuffle( const protobufs::InstructionDescriptor& instruction_to_insert_before, @@ -130,13 +130,18 @@ void TransformationVectorShuffle::Apply( // Add a shuffle instruction right before the instruction identified by // |message_.instruction_to_insert_before|. - FindInstruction(message_.instruction_to_insert_before(), ir_context) - ->InsertBefore(MakeUnique<opt::Instruction>( + auto insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + opt::Instruction* new_instruction = + insert_before->InsertBefore(MakeUnique<opt::Instruction>( ir_context, SpvOpVectorShuffle, result_type_id, message_.fresh_id(), shuffle_operands)); fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); - ir_context->InvalidateAnalysesExceptFor( - opt::IRContext::Analysis::kAnalysisNone); + // Inform the def-use manager about the new instruction and record its basic + // block. + ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction); + ir_context->set_instr_block(new_instruction, + ir_context->get_instr_block(insert_before)); AddDataSynonymFacts(ir_context, transformation_context); } diff --git a/source/fuzz/transformation_vector_shuffle.h b/source/fuzz/transformation_vector_shuffle.h index cf08a62c..7360906f 100644 --- a/source/fuzz/transformation_vector_shuffle.h +++ b/source/fuzz/transformation_vector_shuffle.h @@ -27,7 +27,7 @@ namespace fuzz { class TransformationVectorShuffle : public Transformation { public: explicit TransformationVectorShuffle( - const protobufs::TransformationVectorShuffle& message); + protobufs::TransformationVectorShuffle message); TransformationVectorShuffle( const protobufs::InstructionDescriptor& instruction_to_insert_before, diff --git a/source/fuzz/transformation_wrap_early_terminator_in_function.cpp b/source/fuzz/transformation_wrap_early_terminator_in_function.cpp index 4c436f5e..468d809f 100644 --- a/source/fuzz/transformation_wrap_early_terminator_in_function.cpp +++ b/source/fuzz/transformation_wrap_early_terminator_in_function.cpp @@ -23,9 +23,8 @@ namespace fuzz { TransformationWrapEarlyTerminatorInFunction:: TransformationWrapEarlyTerminatorInFunction( - const spvtools::fuzz::protobufs:: - TransformationWrapEarlyTerminatorInFunction& message) - : message_(message) {} + protobufs::TransformationWrapEarlyTerminatorInFunction message) + : message_(std::move(message)) {} TransformationWrapEarlyTerminatorInFunction:: TransformationWrapEarlyTerminatorInFunction( diff --git a/source/fuzz/transformation_wrap_early_terminator_in_function.h b/source/fuzz/transformation_wrap_early_terminator_in_function.h index 00151d40..d6e55517 100644 --- a/source/fuzz/transformation_wrap_early_terminator_in_function.h +++ b/source/fuzz/transformation_wrap_early_terminator_in_function.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationWrapEarlyTerminatorInFunction : public Transformation { public: explicit TransformationWrapEarlyTerminatorInFunction( - const protobufs::TransformationWrapEarlyTerminatorInFunction& message); + protobufs::TransformationWrapEarlyTerminatorInFunction message); TransformationWrapEarlyTerminatorInFunction( uint32_t fresh_id, diff --git a/source/fuzz/transformation_wrap_region_in_selection.cpp b/source/fuzz/transformation_wrap_region_in_selection.cpp index 9924e368..01c98cca 100644 --- a/source/fuzz/transformation_wrap_region_in_selection.cpp +++ b/source/fuzz/transformation_wrap_region_in_selection.cpp @@ -20,8 +20,8 @@ namespace spvtools { namespace fuzz { TransformationWrapRegionInSelection::TransformationWrapRegionInSelection( - const protobufs::TransformationWrapRegionInSelection& message) - : message_(message) {} + protobufs::TransformationWrapRegionInSelection message) + : message_(std::move(message)) {} TransformationWrapRegionInSelection::TransformationWrapRegionInSelection( uint32_t region_entry_block_id, uint32_t region_exit_block_id, diff --git a/source/fuzz/transformation_wrap_region_in_selection.h b/source/fuzz/transformation_wrap_region_in_selection.h index 57f4f64f..66d16dad 100644 --- a/source/fuzz/transformation_wrap_region_in_selection.h +++ b/source/fuzz/transformation_wrap_region_in_selection.h @@ -26,7 +26,7 @@ namespace fuzz { class TransformationWrapRegionInSelection : public Transformation { public: explicit TransformationWrapRegionInSelection( - const protobufs::TransformationWrapRegionInSelection& message); + protobufs::TransformationWrapRegionInSelection message); TransformationWrapRegionInSelection(uint32_t region_entry_block_id, uint32_t region_exit_block_id, diff --git a/source/opcode.cpp b/source/opcode.cpp index d87e8287..c96cde8d 100644 --- a/source/opcode.cpp +++ b/source/opcode.cpp @@ -417,8 +417,10 @@ bool spvOpcodeIsAtomicWithLoad(const SpvOp opcode) { case SpvOpAtomicISub: case SpvOpAtomicSMin: case SpvOpAtomicUMin: + case SpvOpAtomicFMinEXT: case SpvOpAtomicSMax: case SpvOpAtomicUMax: + case SpvOpAtomicFMaxEXT: case SpvOpAtomicAnd: case SpvOpAtomicOr: case SpvOpAtomicXor: diff --git a/source/operand.cpp b/source/operand.cpp index 5a69fb24..c00c9b64 100644 --- a/source/operand.cpp +++ b/source/operand.cpp @@ -229,6 +229,9 @@ const char* spvOperandTypeStr(spv_operand_type_t type) { return "ray query committed intersection type"; case SPV_OPERAND_TYPE_RAY_QUERY_CANDIDATE_INTERSECTION_TYPE: return "ray query candidate intersection type"; + case SPV_OPERAND_TYPE_PACKED_VECTOR_FORMAT: + case SPV_OPERAND_TYPE_OPTIONAL_PACKED_VECTOR_FORMAT: + return "packed vector format"; case SPV_OPERAND_TYPE_IMAGE: case SPV_OPERAND_TYPE_OPTIONAL_IMAGE: return "image"; @@ -269,6 +272,10 @@ const char* spvOperandTypeStr(spv_operand_type_t type) { return "FP denorm mode"; case SPV_OPERAND_TYPE_FPOPERATION_MODE: return "FP operation mode"; + case SPV_OPERAND_TYPE_QUANTIZATION_MODES: + return "quantization mode"; + case SPV_OPERAND_TYPE_OVERFLOW_MODES: + return "overflow mode"; case SPV_OPERAND_TYPE_NONE: return "NONE"; @@ -355,6 +362,9 @@ bool spvOperandIsConcrete(spv_operand_type_t type) { case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_IMPORTED_ENTITY: case SPV_OPERAND_TYPE_FPDENORM_MODE: case SPV_OPERAND_TYPE_FPOPERATION_MODE: + case SPV_OPERAND_TYPE_QUANTIZATION_MODES: + case SPV_OPERAND_TYPE_OVERFLOW_MODES: + case SPV_OPERAND_TYPE_PACKED_VECTOR_FORMAT: return true; default: break; @@ -390,6 +400,7 @@ bool spvOperandIsOptional(spv_operand_type_t type) { case SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER: case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_STRING: case SPV_OPERAND_TYPE_OPTIONAL_ACCESS_QUALIFIER: + case SPV_OPERAND_TYPE_OPTIONAL_PACKED_VECTOR_FORMAT: case SPV_OPERAND_TYPE_OPTIONAL_CIV: return true; default: diff --git a/source/opt/CMakeLists.txt b/source/opt/CMakeLists.txt index 14a6bee7..88d56589 100644 --- a/source/opt/CMakeLists.txt +++ b/source/opt/CMakeLists.txt @@ -62,6 +62,7 @@ set(SPIRV_TOOLS_OPT_SOURCES instruction.h instruction_list.h instrument_pass.h + interp_fixup_pass.h ir_builder.h ir_context.h ir_loader.h @@ -165,6 +166,7 @@ set(SPIRV_TOOLS_OPT_SOURCES instruction.cpp instruction_list.cpp instrument_pass.cpp + interp_fixup_pass.cpp ir_context.cpp ir_loader.cpp licm_pass.cpp diff --git a/source/opt/aggressive_dead_code_elim_pass.cpp b/source/opt/aggressive_dead_code_elim_pass.cpp index 81b2232f..7cffff57 100644 --- a/source/opt/aggressive_dead_code_elim_pass.cpp +++ b/source/opt/aggressive_dead_code_elim_pass.cpp @@ -996,6 +996,9 @@ void AggressiveDCEPass::InitExtensions() { "SPV_EXT_physical_storage_buffer", "SPV_KHR_terminate_invocation", "SPV_KHR_shader_clock", + "SPV_KHR_vulkan_memory_model", + "SPV_KHR_subgroup_uniform_control_flow", + "SPV_KHR_integer_dot_product", }); } diff --git a/source/opt/code_sink.cpp b/source/opt/code_sink.cpp index e49029fe..cd777974 100644 --- a/source/opt/code_sink.cpp +++ b/source/opt/code_sink.cpp @@ -218,8 +218,10 @@ bool CodeSinkingPass::HasUniformMemorySync() { case SpvOpAtomicISub: case SpvOpAtomicSMin: case SpvOpAtomicUMin: + case SpvOpAtomicFMinEXT: case SpvOpAtomicSMax: case SpvOpAtomicUMax: + case SpvOpAtomicFMaxEXT: case SpvOpAtomicAnd: case SpvOpAtomicOr: case SpvOpAtomicXor: diff --git a/source/opt/constants.cpp b/source/opt/constants.cpp index cf24295d..19ca6008 100644 --- a/source/opt/constants.cpp +++ b/source/opt/constants.cpp @@ -389,6 +389,36 @@ const Constant* ConstantManager::GetConstant( return cst ? RegisterConstant(std::move(cst)) : nullptr; } +const Constant* ConstantManager::GetNumericVectorConstantWithWords( + const Vector* type, const std::vector<uint32_t>& literal_words) { + const auto* element_type = type->element_type(); + uint32_t words_per_element = 0; + if (const auto* float_type = element_type->AsFloat()) + words_per_element = float_type->width() / 32; + else if (const auto* int_type = element_type->AsInteger()) + words_per_element = int_type->width() / 32; + + if (words_per_element != 1 && words_per_element != 2) return nullptr; + + if (words_per_element * type->element_count() != + static_cast<uint32_t>(literal_words.size())) { + return nullptr; + } + + std::vector<uint32_t> element_ids; + for (uint32_t i = 0; i < type->element_count(); ++i) { + auto first_word = literal_words.begin() + (words_per_element * i); + std::vector<uint32_t> const_data(first_word, + first_word + words_per_element); + const analysis::Constant* element_constant = + GetConstant(element_type, const_data); + auto element_id = GetDefiningInstruction(element_constant)->result_id(); + element_ids.push_back(element_id); + } + + return GetConstant(type, element_ids); +} + uint32_t ConstantManager::GetFloatConst(float val) { Type* float_type = context()->get_type_mgr()->GetFloatType(); utils::FloatProxy<float> v(val); diff --git a/source/opt/constants.h b/source/opt/constants.h index e17ae6b6..95d984fc 100644 --- a/source/opt/constants.h +++ b/source/opt/constants.h @@ -58,7 +58,7 @@ class ConstantManager; class Constant { public: Constant() = delete; - virtual ~Constant() {} + virtual ~Constant() = default; // Make a deep copy of this constant. virtual std::unique_ptr<Constant> Copy() const = 0; @@ -506,10 +506,11 @@ class ConstantManager { IRContext* context() const { return ctx_; } // Gets or creates a unique Constant instance of type |type| and a vector of - // constant defining words |words|. If a Constant instance existed already in - // the constant pool, it returns a pointer to it. Otherwise, it creates one - // using CreateConstant. If a new Constant instance cannot be created, it - // returns nullptr. + // constant defining words or ids for elements of Vector type + // |literal_words_or_ids|. If a Constant instance existed already in the + // constant pool, it returns a pointer to it. Otherwise, it creates one using + // CreateConstant. If a new Constant instance cannot be created, it returns + // nullptr. const Constant* GetConstant( const Type* type, const std::vector<uint32_t>& literal_words_or_ids); @@ -519,6 +520,14 @@ class ConstantManager { literal_words_or_ids.end())); } + // Gets or creates a unique Constant instance of Vector type |type| with + // numeric elements and a vector of constant defining words |literal_words|. + // If a Constant instance existed already in the constant pool, it returns a + // pointer to it. Otherwise, it creates one using CreateConstant. If a new + // Constant instance cannot be created, it returns nullptr. + const Constant* GetNumericVectorConstantWithWords( + const Vector* type, const std::vector<uint32_t>& literal_words); + // Gets or creates a Constant instance to hold the constant value of the given // instruction. It returns a pointer to a Constant instance or nullptr if it // could not create the constant. diff --git a/source/opt/eliminate_dead_functions_util.cpp b/source/opt/eliminate_dead_functions_util.cpp index 6b5234bb..1379120f 100644 --- a/source/opt/eliminate_dead_functions_util.cpp +++ b/source/opt/eliminate_dead_functions_util.cpp @@ -23,9 +23,11 @@ Module::iterator EliminateFunction(IRContext* context, Module::iterator* func_iter) { bool first_func = *func_iter == context->module()->begin(); bool seen_func_end = false; + std::unordered_set<Instruction*> to_kill; (*func_iter) ->ForEachInst( - [context, first_func, func_iter, &seen_func_end](Instruction* inst) { + [context, first_func, func_iter, &seen_func_end, + &to_kill](Instruction* inst) { if (inst->opcode() == SpvOpFunctionEnd) { seen_func_end = true; } @@ -33,6 +35,7 @@ Module::iterator EliminateFunction(IRContext* context, // global values if this is the first function. if (seen_func_end && inst->opcode() == SpvOpExtInst) { assert(inst->IsNonSemanticInstruction()); + if (to_kill.find(inst) != to_kill.end()) return; std::unique_ptr<Instruction> clone(inst->Clone(context)); context->ForgetUses(inst); context->AnalyzeDefUse(clone.get()); @@ -44,12 +47,17 @@ Module::iterator EliminateFunction(IRContext* context, prev_func_iter->AddNonSemanticInstruction(std::move(clone)); } inst->ToNop(); - } else { - context->KillNonSemanticInfo(inst); + } else if (to_kill.find(inst) == to_kill.end()) { + context->CollectNonSemanticTree(inst, &to_kill); context->KillInst(inst); } }, true, true); + + for (auto* dead : to_kill) { + context->KillInst(dead); + } + return func_iter->Erase(); } diff --git a/source/opt/fix_storage_class.cpp b/source/opt/fix_storage_class.cpp index 03da0d0d..04eb1326 100644 --- a/source/opt/fix_storage_class.cpp +++ b/source/opt/fix_storage_class.cpp @@ -222,6 +222,16 @@ bool FixStorageClass::PropagateType(Instruction* inst, uint32_t type_id, uint32_t pointee_type_id = GetPointeeTypeId(ptr_inst); if (obj_type_id != pointee_type_id) { + if (context()->get_type_mgr()->GetType(obj_type_id)->AsImage() && + context()->get_type_mgr()->GetType(pointee_type_id)->AsImage()) { + // When storing an image, allow the type mismatch + // and let the later legalization passes eliminate the OpStore. + // This is to support assigning an image to a variable, + // where the assigned image does not have a pre-defined + // image format. + return false; + } + uint32_t copy_id = GenerateCopy(obj_inst, pointee_type_id, inst); inst->SetInOperand(1, {copy_id}); context()->UpdateDefUse(inst); diff --git a/source/opt/folding_rules.cpp b/source/opt/folding_rules.cpp index 010eec9c..e3e926c3 100644 --- a/source/opt/folding_rules.cpp +++ b/source/opt/folding_rules.cpp @@ -124,6 +124,66 @@ Instruction* NonConstInput(IRContext* context, const analysis::Constant* c, inst->GetSingleWordInOperand(in_op)); } +std::vector<uint32_t> ExtractInts(uint64_t val) { + std::vector<uint32_t> words; + words.push_back(static_cast<uint32_t>(val)); + words.push_back(static_cast<uint32_t>(val >> 32)); + return words; +} + +std::vector<uint32_t> GetWordsFromScalarIntConstant( + const analysis::IntConstant* c) { + assert(c != nullptr); + uint32_t width = c->type()->AsInteger()->width(); + assert(width == 32 || width == 64); + if (width == 64) { + uint64_t uval = static_cast<uint64_t>(c->GetU64()); + return ExtractInts(uval); + } + return {c->GetU32()}; +} + +std::vector<uint32_t> GetWordsFromScalarFloatConstant( + const analysis::FloatConstant* c) { + assert(c != nullptr); + uint32_t width = c->type()->AsFloat()->width(); + assert(width == 32 || width == 64); + if (width == 64) { + utils::FloatProxy<double> result(c->GetDouble()); + return result.GetWords(); + } + utils::FloatProxy<float> result(c->GetFloat()); + return result.GetWords(); +} + +std::vector<uint32_t> GetWordsFromNumericScalarOrVectorConstant( + analysis::ConstantManager* const_mgr, const analysis::Constant* c) { + if (const auto* float_constant = c->AsFloatConstant()) { + return GetWordsFromScalarFloatConstant(float_constant); + } else if (const auto* int_constant = c->AsIntConstant()) { + return GetWordsFromScalarIntConstant(int_constant); + } else if (const auto* vec_constant = c->AsVectorConstant()) { + std::vector<uint32_t> words; + for (const auto* comp : vec_constant->GetComponents()) { + auto comp_in_words = + GetWordsFromNumericScalarOrVectorConstant(const_mgr, comp); + words.insert(words.end(), comp_in_words.begin(), comp_in_words.end()); + } + return words; + } + return {}; +} + +const analysis::Constant* ConvertWordsToNumericScalarOrVectorConstant( + analysis::ConstantManager* const_mgr, const std::vector<uint32_t>& words, + const analysis::Type* type) { + if (type->AsInteger() || type->AsFloat()) + return const_mgr->GetConstant(type, words); + if (const auto* vec_type = type->AsVector()) + return const_mgr->GetNumericVectorConstantWithWords(vec_type, words); + return nullptr; +} + // Returns the negation of |c|. |c| must be a 32 or 64 bit floating point // constant. uint32_t NegateFloatingPointConstant(analysis::ConstantManager* const_mgr, @@ -146,13 +206,6 @@ uint32_t NegateFloatingPointConstant(analysis::ConstantManager* const_mgr, return const_mgr->GetDefiningInstruction(negated_const)->result_id(); } -std::vector<uint32_t> ExtractInts(uint64_t val) { - std::vector<uint32_t> words; - words.push_back(static_cast<uint32_t>(val)); - words.push_back(static_cast<uint32_t>(val >> 32)); - return words; -} - // Negates the integer constant |c|. Returns the id of the defining instruction. uint32_t NegateIntegerConstant(analysis::ConstantManager* const_mgr, const analysis::Constant* c) { @@ -470,7 +523,7 @@ uint32_t PerformFloatingPointOperation(analysis::ConstantManager* const_mgr, float fval = val.getAsFloat(); \ if (!IsValidResult(fval)) return 0; \ words = val.GetWords(); \ - } + } static_assert(true, "require extra semicolon") switch (opcode) { case SpvOpFMul: FOLD_OP(*); @@ -522,7 +575,7 @@ uint32_t PerformIntegerOperation(analysis::ConstantManager* const_mgr, uint32_t val = input1->GetU32() op input2->GetU32(); \ words.push_back(val); \ } \ - } + } static_assert(true, "require extra semicalon") switch (opcode) { case SpvOpIMul: FOLD_OP(*); @@ -1796,6 +1849,35 @@ FoldingRule RedundantPhi() { }; } +FoldingRule BitCastScalarOrVector() { + return [](IRContext* context, Instruction* inst, + const std::vector<const analysis::Constant*>& constants) { + assert(inst->opcode() == SpvOpBitcast && constants.size() == 1); + if (constants[0] == nullptr) return false; + + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + if (HasFloatingPoint(type) && !inst->IsFloatingPointFoldingAllowed()) + return false; + + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + std::vector<uint32_t> words = + GetWordsFromNumericScalarOrVectorConstant(const_mgr, constants[0]); + if (words.size() == 0) return false; + + const analysis::Constant* bitcasted_constant = + ConvertWordsToNumericScalarOrVectorConstant(const_mgr, words, type); + if (!bitcasted_constant) return false; + + auto new_feeder_id = + const_mgr->GetDefiningInstruction(bitcasted_constant, inst->type_id()) + ->result_id(); + inst->SetOpcode(SpvOpCopyObject); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {new_feeder_id}}}); + return true; + }; +} + FoldingRule RedundantSelect() { // An OpSelect instruction where both values are the same or the condition is // constant can be replaced by one of the values @@ -2423,6 +2505,8 @@ void FoldingRules::AddFoldingRules() { // Note that the order in which rules are added to the list matters. If a rule // applies to the instruction, the rest of the rules will not be attempted. // Take that into consideration. + rules_[SpvOpBitcast].push_back(BitCastScalarOrVector()); + rules_[SpvOpCompositeConstruct].push_back(CompositeExtractFeedingConstruct); rules_[SpvOpCompositeExtract].push_back(InsertFeedingExtract()); diff --git a/source/opt/graphics_robust_access_pass.cpp b/source/opt/graphics_robust_access_pass.cpp index 46483e48..1b28f9b5 100644 --- a/source/opt/graphics_robust_access_pass.cpp +++ b/source/opt/graphics_robust_access_pass.cpp @@ -272,10 +272,11 @@ void GraphicsRobustAccessPass::ClampIndicesForAccessChain( // Replaces one of the OpAccessChain index operands with a new value. // Updates def-use analysis. - auto replace_index = [&inst, def_use_mgr](uint32_t operand_index, - Instruction* new_value) { + auto replace_index = [this, &inst, def_use_mgr](uint32_t operand_index, + Instruction* new_value) { inst.SetOperand(operand_index, {new_value->result_id()}); def_use_mgr->AnalyzeInstUse(&inst); + module_status_.modified = true; return SPV_SUCCESS; }; diff --git a/source/opt/inline_pass.h b/source/opt/inline_pass.h index abe773af..9a5429ba 100644 --- a/source/opt/inline_pass.h +++ b/source/opt/inline_pass.h @@ -37,7 +37,7 @@ class InlinePass : public Pass { using cbb_ptr = const BasicBlock*; public: - virtual ~InlinePass() = default; + virtual ~InlinePass() override = default; protected: InlinePass(); diff --git a/source/opt/inst_bindless_check_pass.cpp b/source/opt/inst_bindless_check_pass.cpp index 00857343..5607239a 100644 --- a/source/opt/inst_bindless_check_pass.cpp +++ b/source/opt/inst_bindless_check_pass.cpp @@ -27,13 +27,16 @@ static const int kSpvCopyObjectOperandIdInIdx = 0; static const int kSpvLoadPtrIdInIdx = 0; static const int kSpvAccessChainBaseIdInIdx = 0; static const int kSpvAccessChainIndex0IdInIdx = 1; +static const int kSpvTypeArrayTypeIdInIdx = 0; static const int kSpvTypeArrayLengthIdInIdx = 1; static const int kSpvConstantValueInIdx = 0; static const int kSpvVariableStorageClassInIdx = 0; +static const int kSpvTypePtrTypeIdInIdx = 1; static const int kSpvTypeImageDim = 1; static const int kSpvTypeImageDepth = 2; static const int kSpvTypeImageArrayed = 3; static const int kSpvTypeImageMS = 4; +static const int kSpvTypeImageSampled = 5; } // anonymous namespace // Avoid unused variable warning/error on Linux @@ -206,13 +209,40 @@ bool InstBindlessCheckPass::AnalyzeDescriptorReference(Instruction* ref_inst, var_inst->GetSingleWordInOperand(kSpvVariableStorageClassInIdx); switch (storage_class) { case SpvStorageClassUniform: - case SpvStorageClassUniformConstant: case SpvStorageClassStorageBuffer: break; default: return false; break; } + // Check for deprecated storage block form + if (storage_class == SpvStorageClassUniform) { + uint32_t var_ty_id = var_inst->type_id(); + Instruction* var_ty_inst = get_def_use_mgr()->GetDef(var_ty_id); + uint32_t ptr_ty_id = + var_ty_inst->GetSingleWordInOperand(kSpvTypePtrTypeIdInIdx); + Instruction* ptr_ty_inst = get_def_use_mgr()->GetDef(ptr_ty_id); + SpvOp ptr_ty_op = ptr_ty_inst->opcode(); + uint32_t block_ty_id = + (ptr_ty_op == SpvOpTypeArray || ptr_ty_op == SpvOpTypeRuntimeArray) + ? ptr_ty_inst->GetSingleWordInOperand(kSpvTypeArrayTypeIdInIdx) + : ptr_ty_id; + assert(get_def_use_mgr()->GetDef(block_ty_id)->opcode() == + SpvOpTypeStruct && + "unexpected block type"); + bool block_found = get_decoration_mgr()->FindDecoration( + block_ty_id, SpvDecorationBlock, + [](const Instruction&) { return true; }); + if (!block_found) { + // If block decoration not found, verify deprecated form of SSBO + bool buffer_block_found = get_decoration_mgr()->FindDecoration( + block_ty_id, SpvDecorationBufferBlock, + [](const Instruction&) { return true; }); + USE_ASSERT(buffer_block_found && "block decoration not found"); + storage_class = SpvStorageClassStorageBuffer; + } + } + ref->strg_class = storage_class; Instruction* desc_type_inst = GetPointeeTypeInst(var_inst); switch (desc_type_inst->opcode()) { case SpvOpTypeArray: @@ -665,8 +695,10 @@ void InstBindlessCheckPass::GenDescInitCheckCode( // for the referenced value. Instruction* ult_inst = builder.AddBinaryOp(GetBoolId(), SpvOpULessThan, ref_id, init_id); - uint32_t error = - init_check ? kInstErrorBindlessUninit : kInstErrorBindlessBuffOOB; + uint32_t error = init_check ? kInstErrorBindlessUninit + : (ref.strg_class == SpvStorageClassUniform + ? kInstErrorBuffOOBUniform + : kInstErrorBuffOOBStorage); uint32_t error_id = builder.GetUintConstantId(error); GenCheckCode(ult_inst->result_id(), error_id, init_check ? 0 : ref_id, init_check ? builder.GetUintConstantId(0u) : init_id, stage_idx, @@ -732,7 +764,11 @@ void InstBindlessCheckPass::GenTexBuffCheckCode( // for the referenced value. Instruction* ult_inst = builder.AddBinaryOp(GetBoolId(), SpvOpULessThan, coord_id, size_id); - uint32_t error_id = builder.GetUintConstantId(kInstErrorBindlessBuffOOB); + uint32_t error = + (image_ty_inst->GetSingleWordInOperand(kSpvTypeImageSampled) == 2) + ? kInstErrorBuffOOBStorageTexel + : kInstErrorBuffOOBUniformTexel; + uint32_t error_id = builder.GetUintConstantId(error); GenCheckCode(ult_inst->result_id(), error_id, coord_id, size_id, stage_idx, &ref, new_blocks); // Move original block's remaining code into remainder/merge block and add diff --git a/source/opt/inst_bindless_check_pass.h b/source/opt/inst_bindless_check_pass.h index a7dff75f..cd961805 100644 --- a/source/opt/inst_bindless_check_pass.h +++ b/source/opt/inst_bindless_check_pass.h @@ -130,6 +130,7 @@ class InstBindlessCheckPass : public InstrumentPass { uint32_t ptr_id; uint32_t var_id; uint32_t desc_idx_id; + uint32_t strg_class; Instruction* ref_inst; } RefAnalysis; diff --git a/source/opt/instruction.h b/source/opt/instruction.h index 47432210..3e557dd7 100644 --- a/source/opt/instruction.h +++ b/source/opt/instruction.h @@ -209,7 +209,7 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> { Instruction(Instruction&&); Instruction& operator=(Instruction&&); - virtual ~Instruction() = default; + ~Instruction() override = default; // Returns a newly allocated instruction that has the same operands, result, // and type as |this|. The new instruction is not linked into any list. diff --git a/source/opt/instruction_list.h b/source/opt/instruction_list.h index 417cbd76..b3e42745 100644 --- a/source/opt/instruction_list.h +++ b/source/opt/instruction_list.h @@ -53,7 +53,7 @@ class InstructionList : public utils::IntrusiveList<Instruction> { } // Destroy this list and any instructions in the list. - inline virtual ~InstructionList(); + inline ~InstructionList() override; class iterator : public utils::IntrusiveList<Instruction>::iterator { public: diff --git a/source/opt/interp_fixup_pass.cpp b/source/opt/interp_fixup_pass.cpp new file mode 100644 index 00000000..ad29e6a7 --- /dev/null +++ b/source/opt/interp_fixup_pass.cpp @@ -0,0 +1,131 @@ +// Copyright (c) 2021 The Khronos Group Inc. +// Copyright (c) 2021 Valve Corporation +// Copyright (c) 2021 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/interp_fixup_pass.h" + +#include <set> +#include <string> + +#include "ir_builder.h" +#include "source/opt/ir_context.h" +#include "type_manager.h" + +namespace spvtools { +namespace opt { + +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. +bool ReplaceInternalInterpolate(IRContext* ctx, Instruction* inst, + const std::vector<const analysis::Constant*>&) { + uint32_t glsl450_ext_inst_id = + ctx->get_feature_mgr()->GetExtInstImportId_GLSLstd450(); + assert(glsl450_ext_inst_id != 0); + + uint32_t ext_opcode = inst->GetSingleWordInOperand(1); + + uint32_t op1_id = inst->GetSingleWordInOperand(2); + + Instruction* load_inst = ctx->get_def_use_mgr()->GetDef(op1_id); + if (load_inst->opcode() != SpvOpLoad) return false; + + Instruction* base_inst = load_inst->GetBaseAddress(); + USE_ASSERT(base_inst->opcode() == SpvOpVariable && + base_inst->GetSingleWordInOperand(kSpvVariableStorageClassInIdx) == + SpvStorageClassInput && + "unexpected interpolant in InterpolateAt*"); + + uint32_t ptr_id = load_inst->GetSingleWordInOperand(0); + uint32_t op2_id = (ext_opcode != GLSLstd450InterpolateAtCentroid) + ? inst->GetSingleWordInOperand(3) + : 0; + + Instruction::OperandList new_operands; + new_operands.push_back({SPV_OPERAND_TYPE_ID, {glsl450_ext_inst_id}}); + new_operands.push_back( + {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, {ext_opcode}}); + new_operands.push_back({SPV_OPERAND_TYPE_ID, {ptr_id}}); + if (op2_id != 0) new_operands.push_back({SPV_OPERAND_TYPE_ID, {op2_id}}); + + inst->SetInOperands(std::move(new_operands)); + ctx->UpdateDefUse(inst); + return true; +} + +class InterpFoldingRules : public FoldingRules { + public: + explicit InterpFoldingRules(IRContext* ctx) : FoldingRules(ctx) {} + + protected: + virtual void AddFoldingRules() override { + uint32_t extension_id = + context()->get_feature_mgr()->GetExtInstImportId_GLSLstd450(); + + if (extension_id != 0) { + ext_rules_[{extension_id, GLSLstd450InterpolateAtCentroid}].push_back( + ReplaceInternalInterpolate); + ext_rules_[{extension_id, GLSLstd450InterpolateAtSample}].push_back( + ReplaceInternalInterpolate); + ext_rules_[{extension_id, GLSLstd450InterpolateAtOffset}].push_back( + ReplaceInternalInterpolate); + } + } +}; + +class InterpConstFoldingRules : public ConstantFoldingRules { + public: + InterpConstFoldingRules(IRContext* ctx) : ConstantFoldingRules(ctx) {} + + protected: + virtual void AddFoldingRules() override {} +}; + +} // namespace + +Pass::Status InterpFixupPass::Process() { + bool changed = false; + + // Traverse the body of the functions to replace instructions that require + // the extensions. + InstructionFolder folder( + context(), + std::unique_ptr<InterpFoldingRules>(new InterpFoldingRules(context())), + MakeUnique<InterpConstFoldingRules>(context())); + for (Function& func : *get_module()) { + func.ForEachInst([&changed, &folder](Instruction* inst) { + if (folder.FoldInstruction(inst)) { + changed = true; + } + }); + } + + return changed ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/source/opt/interp_fixup_pass.h b/source/opt/interp_fixup_pass.h new file mode 100644 index 00000000..e112b651 --- /dev/null +++ b/source/opt/interp_fixup_pass.h @@ -0,0 +1,54 @@ +// Copyright (c) 2021 The Khronos Group Inc. +// Copyright (c) 2021 Valve Corporation +// Copyright (c) 2021 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_INTERP_FIXUP_H +#define SOURCE_OPT_INTERP_FIXUP_H + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// Replaces overloaded internal form for GLSLstd450Interpolate* instructions +// with external form. Specifically, removes OpLoad from the first argument +// and replaces it with the pointer for the OpLoad. glslang generates the +// internal form. This pass is called as part of glslang HLSL legalization. +class InterpFixupPass : public Pass { + public: + const char* name() const override { return "interp-fixup"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators | + IRContext::kAnalysisCFG | IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisNameMap | + IRContext::kAnalysisScalarEvolution | + IRContext::kAnalysisRegisterPressure | + IRContext::kAnalysisValueNumberTable | + IRContext::kAnalysisStructuredCFG | + IRContext::kAnalysisBuiltinVarId | + IRContext::kAnalysisIdToFuncMapping | IRContext::kAnalysisTypes | + IRContext::kAnalysisDefUse | IRContext::kAnalysisConstants; + } +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_INTERP_FIXUP_H diff --git a/source/opt/ir_context.cpp b/source/opt/ir_context.cpp index 82107b5c..03afe6e8 100644 --- a/source/opt/ir_context.cpp +++ b/source/opt/ir_context.cpp @@ -214,10 +214,10 @@ Instruction* IRContext::KillInst(Instruction* inst) { return next_instruction; } -void IRContext::KillNonSemanticInfo(Instruction* inst) { +void IRContext::CollectNonSemanticTree( + Instruction* inst, std::unordered_set<Instruction*>* to_kill) { if (!inst->HasResultId()) return; std::vector<Instruction*> work_list; - std::vector<Instruction*> to_kill; std::unordered_set<Instruction*> seen; work_list.push_back(inst); @@ -225,17 +225,13 @@ void IRContext::KillNonSemanticInfo(Instruction* inst) { auto* i = work_list.back(); work_list.pop_back(); get_def_use_mgr()->ForEachUser( - i, [&work_list, &to_kill, &seen](Instruction* user) { + i, [&work_list, to_kill, &seen](Instruction* user) { if (user->IsNonSemanticInstruction() && seen.insert(user).second) { work_list.push_back(user); - to_kill.push_back(user); + to_kill->insert(user); } }); } - - for (auto* dead : to_kill) { - KillInst(dead); - } } bool IRContext::KillDef(uint32_t id) { diff --git a/source/opt/ir_context.h b/source/opt/ir_context.h index 5aa25acd..aab35162 100644 --- a/source/opt/ir_context.h +++ b/source/opt/ir_context.h @@ -403,8 +403,10 @@ class IRContext { // instruction exists. Instruction* KillInst(Instruction* inst); - // Removes the non-semantic instruction tree that uses |inst|'s result id. - void KillNonSemanticInfo(Instruction* inst); + // Collects the non-semantic instruction tree that uses |inst|'s result id + // to be killed later. + void CollectNonSemanticTree(Instruction* inst, + std::unordered_set<Instruction*>* to_kill); // Returns true if all of the given analyses are valid. bool AreAnalysesValid(Analysis set) { return (set & valid_analyses_) == set; } diff --git a/source/opt/iterator.h b/source/opt/iterator.h index 444d457c..2280582d 100644 --- a/source/opt/iterator.h +++ b/source/opt/iterator.h @@ -30,18 +30,14 @@ namespace opt { // std::unique_ptr managed elements in the vector, behaving like we are using // std::vector<|ValueType|>. template <typename ValueType, bool IsConst = false> -class UptrVectorIterator - : public std::iterator<std::random_access_iterator_tag, - typename std::conditional<IsConst, const ValueType, - ValueType>::type> { +class UptrVectorIterator { public: - using super = std::iterator< - std::random_access_iterator_tag, - typename std::conditional<IsConst, const ValueType, ValueType>::type>; + using iterator_category = std::random_access_iterator_tag; + using value_type = ValueType; - using pointer = typename super::pointer; - using reference = typename super::reference; - using difference_type = typename super::difference_type; + using pointer = value_type*; + using reference = value_type&; + using difference_type = std::ptrdiff_t; // Type aliases. We need to apply constness properly if |IsConst| is true. using Uptr = std::unique_ptr<ValueType>; @@ -174,11 +170,7 @@ inline IteratorRange<IteratorType> make_const_range( // // Currently this iterator is always an input iterator. template <typename SubIterator, typename Predicate> -class FilterIterator - : public std::iterator< - std::input_iterator_tag, typename SubIterator::value_type, - typename SubIterator::difference_type, typename SubIterator::pointer, - typename SubIterator::reference> { +class FilterIterator { public: // Iterator interface. using iterator_category = typename SubIterator::iterator_category; diff --git a/source/opt/local_access_chain_convert_pass.cpp b/source/opt/local_access_chain_convert_pass.cpp index 205cd7a3..5c10ecec 100644 --- a/source/opt/local_access_chain_convert_pass.cpp +++ b/source/opt/local_access_chain_convert_pass.cpp @@ -418,6 +418,8 @@ void LocalAccessChainConvertPass::InitExtensions() { "SPV_KHR_ray_query", "SPV_EXT_fragment_invocation_density", "SPV_KHR_terminate_invocation", + "SPV_KHR_subgroup_uniform_control_flow", + "SPV_KHR_integer_dot_product", }); } diff --git a/source/opt/local_single_block_elim_pass.cpp b/source/opt/local_single_block_elim_pass.cpp index 5f35ee1a..05ed28ae 100644 --- a/source/opt/local_single_block_elim_pass.cpp +++ b/source/opt/local_single_block_elim_pass.cpp @@ -270,6 +270,8 @@ void LocalSingleBlockLoadStoreElimPass::InitExtensions() { "SPV_EXT_fragment_invocation_density", "SPV_EXT_physical_storage_buffer", "SPV_KHR_terminate_invocation", + "SPV_KHR_subgroup_uniform_control_flow", + "SPV_KHR_integer_dot_product", }); } diff --git a/source/opt/local_single_store_elim_pass.cpp b/source/opt/local_single_store_elim_pass.cpp index d322a2fb..7eb4b1fd 100644 --- a/source/opt/local_single_store_elim_pass.cpp +++ b/source/opt/local_single_store_elim_pass.cpp @@ -123,6 +123,8 @@ void LocalSingleStoreElimPass::InitExtensionAllowList() { "SPV_EXT_fragment_invocation_density", "SPV_EXT_physical_storage_buffer", "SPV_KHR_terminate_invocation", + "SPV_KHR_subgroup_uniform_control_flow", + "SPV_KHR_integer_dot_product", }); } bool LocalSingleStoreElimPass::ProcessVariable(Instruction* var_inst) { diff --git a/source/opt/loop_unroller.cpp b/source/opt/loop_unroller.cpp index 6cdced46..df68bd20 100644 --- a/source/opt/loop_unroller.cpp +++ b/source/opt/loop_unroller.cpp @@ -797,6 +797,9 @@ void LoopUnrollerUtilsImpl::CloseUnrolledLoop(Loop* loop) { for (BasicBlock* block : loop_blocks_inorder_) { RemapOperands(block); } + for (auto& block_itr : blocks_to_add_) { + RemapOperands(block_itr.get()); + } // Rewrite the last phis, since they may still reference the original phi. for (Instruction* last_phi : state_.previous_phis_) { diff --git a/source/opt/mem_pass.h b/source/opt/mem_pass.h index dcc16b65..5a77670d 100644 --- a/source/opt/mem_pass.h +++ b/source/opt/mem_pass.h @@ -38,7 +38,7 @@ namespace opt { // utility functions and supporting state. class MemPass : public Pass { public: - virtual ~MemPass() = default; + virtual ~MemPass() override = default; // Returns an undef value for the given |var_id|'s type. uint32_t GetUndefVal(uint32_t var_id) { diff --git a/source/opt/optimizer.cpp b/source/opt/optimizer.cpp index 909442cc..a5d10c3d 100644 --- a/source/opt/optimizer.cpp +++ b/source/opt/optimizer.cpp @@ -155,7 +155,8 @@ Optimizer& Optimizer::RegisterLegalizationPasses() { .RegisterPass(CreateVectorDCEPass()) .RegisterPass(CreateDeadInsertElimPass()) .RegisterPass(CreateReduceLoadSizePass()) - .RegisterPass(CreateAggressiveDCEPass()); + .RegisterPass(CreateAggressiveDCEPass()) + .RegisterPass(CreateInterpolateFixupPass()); } Optimizer& Optimizer::RegisterPerformancePasses() { @@ -494,6 +495,8 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) { RegisterPass(CreateWrapOpKillPass()); } else if (pass_name == "amd-ext-to-khr") { RegisterPass(CreateAmdExtToKhrPass()); + } else if (pass_name == "interpolate-fixup") { + RegisterPass(CreateInterpolateFixupPass()); } else { Errorf(consumer(), nullptr, {}, "Unknown flag '--%s'. Use --help for a list of valid flags", @@ -925,4 +928,9 @@ Optimizer::PassToken CreateAmdExtToKhrPass() { MakeUnique<opt::AmdExtensionToKhrPass>()); } +Optimizer::PassToken CreateInterpolateFixupPass() { + return MakeUnique<Optimizer::PassToken::Impl>( + MakeUnique<opt::InterpFixupPass>()); +} + } // namespace spvtools diff --git a/source/opt/passes.h b/source/opt/passes.h index 1bc94c7e..bfb34af7 100644 --- a/source/opt/passes.h +++ b/source/opt/passes.h @@ -46,6 +46,7 @@ #include "source/opt/inst_bindless_check_pass.h" #include "source/opt/inst_buff_addr_check_pass.h" #include "source/opt/inst_debug_printf_pass.h" +#include "source/opt/interp_fixup_pass.h" #include "source/opt/licm_pass.h" #include "source/opt/local_access_chain_convert_pass.h" #include "source/opt/local_redundancy_elimination.h" diff --git a/source/opt/scalar_replacement_pass.cpp b/source/opt/scalar_replacement_pass.cpp index ba2d0675..4d47bdd8 100644 --- a/source/opt/scalar_replacement_pass.cpp +++ b/source/opt/scalar_replacement_pass.cpp @@ -861,6 +861,9 @@ bool ScalarReplacementPass::CheckUsesRelaxed(const Instruction* inst) const { case SpvOpStore: if (!CheckStore(user, index)) ok = false; break; + case SpvOpImageTexelPointer: + if (!CheckImageTexelPointer(index)) ok = false; + break; default: ok = false; break; @@ -870,6 +873,10 @@ bool ScalarReplacementPass::CheckUsesRelaxed(const Instruction* inst) const { return ok; } +bool ScalarReplacementPass::CheckImageTexelPointer(uint32_t index) const { + return index == 2u; +} + bool ScalarReplacementPass::CheckLoad(const Instruction* inst, uint32_t index) const { if (index != 2u) return false; diff --git a/source/opt/scalar_replacement_pass.h b/source/opt/scalar_replacement_pass.h index 1f6c9281..9e9f0739 100644 --- a/source/opt/scalar_replacement_pass.h +++ b/source/opt/scalar_replacement_pass.h @@ -142,6 +142,10 @@ class ScalarReplacementPass : public Pass { // of |inst| and the store is not to volatile memory. bool CheckStore(const Instruction* inst, uint32_t index) const; + // Returns true if |index| is the pointer operand of an OpImageTexelPointer + // instruction. + bool CheckImageTexelPointer(uint32_t index) const; + // Creates a variable of type |typeId| from the |index|'th element of // |varInst|. The new variable is added to |replacements|. If the variable // could not be created, then |nullptr| is appended to |replacements|. diff --git a/source/opt/type_manager.cpp b/source/opt/type_manager.cpp index ce9c2c14..7935ad33 100644 --- a/source/opt/type_manager.cpp +++ b/source/opt/type_manager.cpp @@ -223,7 +223,7 @@ uint32_t TypeManager::GetTypeInstruction(const Type* type) { case Type::k##kind: \ typeInst = MakeUnique<Instruction>(context(), SpvOpType##kind, 0, id, \ std::initializer_list<Operand>{}); \ - break; + break DefineParameterlessCase(Void); DefineParameterlessCase(Bool); DefineParameterlessCase(Sampler); @@ -513,7 +513,7 @@ Type* TypeManager::RebuildType(const Type& type) { #define DefineNoSubtypeCase(kind) \ case Type::k##kind: \ rebuilt_ty.reset(type.Clone().release()); \ - return type_pool_.insert(std::move(rebuilt_ty)).first->get(); + return type_pool_.insert(std::move(rebuilt_ty)).first->get() DefineNoSubtypeCase(Void); DefineNoSubtypeCase(Bool); diff --git a/source/opt/types.h b/source/opt/types.h index d5be9be4..9ecd41a6 100644 --- a/source/opt/types.h +++ b/source/opt/types.h @@ -101,7 +101,7 @@ class Type { Type(Kind k) : kind_(k) {} - virtual ~Type() {} + virtual ~Type() = default; // Attaches a decoration directly on this type. void AddDecoration(std::vector<uint32_t>&& d) { diff --git a/source/reduce/change_operand_reduction_opportunity.cpp b/source/reduce/change_operand_reduction_opportunity.cpp index c3f6fd7b..18e340bf 100644 --- a/source/reduce/change_operand_reduction_opportunity.cpp +++ b/source/reduce/change_operand_reduction_opportunity.cpp @@ -14,6 +14,8 @@ #include "source/reduce/change_operand_reduction_opportunity.h" +#include "source/opt/ir_context.h" + namespace spvtools { namespace reduce { @@ -26,6 +28,7 @@ bool ChangeOperandReductionOpportunity::PreconditionHolds() { void ChangeOperandReductionOpportunity::Apply() { inst_->SetOperand(operand_index_, {new_id_}); + inst_->context()->get_def_use_mgr()->UpdateDefUse(inst_); } } // namespace reduce diff --git a/source/reduce/change_operand_to_undef_reduction_opportunity.cpp b/source/reduce/change_operand_to_undef_reduction_opportunity.cpp index 8e33da66..7cc06a03 100644 --- a/source/reduce/change_operand_to_undef_reduction_opportunity.cpp +++ b/source/reduce/change_operand_to_undef_reduction_opportunity.cpp @@ -35,6 +35,7 @@ void ChangeOperandToUndefReductionOpportunity::Apply() { assert(operand_type_id); auto undef_id = FindOrCreateGlobalUndef(context_, operand_type_id); inst_->SetOperand(operand_index_, {undef_id}); + context_->InvalidateAnalyses(opt::IRContext::kAnalysisDefUse); } } // namespace reduce diff --git a/source/reduce/reducer.cpp b/source/reduce/reducer.cpp index 18eeaeb6..16bb94fe 100644 --- a/source/reduce/reducer.cpp +++ b/source/reduce/reducer.cpp @@ -54,10 +54,10 @@ void Reducer::SetInterestingnessFunction( } Reducer::ReductionResultStatus Reducer::Run( - std::vector<uint32_t>&& binary_in, std::vector<uint32_t>* binary_out, + const std::vector<uint32_t>& binary_in, std::vector<uint32_t>* binary_out, spv_const_reducer_options options, spv_validator_options validator_options) { - std::vector<uint32_t> current_binary(std::move(binary_in)); + std::vector<uint32_t> current_binary(binary_in); spvtools::SpirvTools tools(target_env_); assert(tools.IsValid() && "Failed to create SPIRV-Tools interface"); @@ -138,13 +138,13 @@ void Reducer::AddDefaultReductionPasses() { } void Reducer::AddReductionPass( - std::unique_ptr<ReductionOpportunityFinder>&& finder) { + std::unique_ptr<ReductionOpportunityFinder> finder) { passes_.push_back( spvtools::MakeUnique<ReductionPass>(target_env_, std::move(finder))); -} +} void Reducer::AddCleanupReductionPass( - std::unique_ptr<ReductionOpportunityFinder>&& finder) { + std::unique_ptr<ReductionOpportunityFinder> finder) { cleanup_passes_.push_back( spvtools::MakeUnique<ReductionPass>(target_env_, std::move(finder))); } diff --git a/source/reduce/reducer.h b/source/reduce/reducer.h index 864ce757..f3ba1806 100644 --- a/source/reduce/reducer.h +++ b/source/reduce/reducer.h @@ -84,17 +84,17 @@ class Reducer { // Adds a reduction pass based on the given finder to the sequence of passes // that will be iterated over. - void AddReductionPass(std::unique_ptr<ReductionOpportunityFinder>&& finder); + void AddReductionPass(std::unique_ptr<ReductionOpportunityFinder> finder); // Adds a cleanup reduction pass based on the given finder to the sequence of // passes that will run after other passes. void AddCleanupReductionPass( - std::unique_ptr<ReductionOpportunityFinder>&& finder); + std::unique_ptr<ReductionOpportunityFinder> finder); // Reduces the given SPIR-V module |binary_out|. // The reduced binary ends up in |binary_out|. // A status is returned. - ReductionResultStatus Run(std::vector<uint32_t>&& binary_in, + ReductionResultStatus Run(const std::vector<uint32_t>& binary_in, std::vector<uint32_t>* binary_out, spv_const_reducer_options options, spv_validator_options validator_options); diff --git a/source/reduce/remove_block_reduction_opportunity.cpp b/source/reduce/remove_block_reduction_opportunity.cpp index aa481059..55e95769 100644 --- a/source/reduce/remove_block_reduction_opportunity.cpp +++ b/source/reduce/remove_block_reduction_opportunity.cpp @@ -20,12 +20,11 @@ namespace spvtools { namespace reduce { RemoveBlockReductionOpportunity::RemoveBlockReductionOpportunity( - opt::Function* function, opt::BasicBlock* block) - : function_(function), block_(block) { + opt::IRContext* context, opt::Function* function, opt::BasicBlock* block) + : context_(context), function_(function), block_(block) { // precondition: assert(block_->begin() != block_->end() && - block_->begin()->context()->get_def_use_mgr()->NumUsers( - block_->id()) == 0 && + context_->get_def_use_mgr()->NumUsers(block_->id()) == 0 && "RemoveBlockReductionOpportunity block must have 0 references"); } @@ -38,10 +37,8 @@ void RemoveBlockReductionOpportunity::Apply() { // We need an iterator pointing to the block, hence the loop. for (auto bi = function_->begin(); bi != function_->end(); ++bi) { if (bi->id() == block_->id()) { - bi->KillAllInsts(true); bi.Erase(); - // Block removal changes the function, but we don't use analyses, so no - // need to invalidate them. + context_->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); return; } } diff --git a/source/reduce/remove_block_reduction_opportunity.h b/source/reduce/remove_block_reduction_opportunity.h index 4b358abc..03fede52 100644 --- a/source/reduce/remove_block_reduction_opportunity.h +++ b/source/reduce/remove_block_reduction_opportunity.h @@ -27,7 +27,8 @@ namespace reduce { class RemoveBlockReductionOpportunity : public ReductionOpportunity { public: // Creates the opportunity to remove |block| in |function| in |context|. - RemoveBlockReductionOpportunity(opt::Function* function, + RemoveBlockReductionOpportunity(opt::IRContext* context, + opt::Function* function, opt::BasicBlock* block); bool PreconditionHolds() override; @@ -36,6 +37,7 @@ class RemoveBlockReductionOpportunity : public ReductionOpportunity { void Apply() override; private: + opt::IRContext* context_; opt::Function* function_; opt::BasicBlock* block_; }; diff --git a/source/reduce/remove_block_reduction_opportunity_finder.cpp b/source/reduce/remove_block_reduction_opportunity_finder.cpp index 27a4570c..3b13728a 100644 --- a/source/reduce/remove_block_reduction_opportunity_finder.cpp +++ b/source/reduce/remove_block_reduction_opportunity_finder.cpp @@ -32,8 +32,8 @@ RemoveBlockReductionOpportunityFinder::GetAvailableOpportunities( for (auto* function : GetTargetFunctions(context, target_function)) { for (auto bi = function->begin(); bi != function->end(); ++bi) { if (IsBlockValidOpportunity(context, function, &bi)) { - result.push_back( - MakeUnique<RemoveBlockReductionOpportunity>(function, &*bi)); + result.push_back(MakeUnique<RemoveBlockReductionOpportunity>( + context, function, &*bi)); } } } diff --git a/source/reduce/remove_function_reduction_opportunity.cpp b/source/reduce/remove_function_reduction_opportunity.cpp index ecad6707..4b85058b 100644 --- a/source/reduce/remove_function_reduction_opportunity.cpp +++ b/source/reduce/remove_function_reduction_opportunity.cpp @@ -29,8 +29,8 @@ void RemoveFunctionReductionOpportunity::Apply() { for (opt::Module::iterator function_it = context_->module()->begin(); function_it != context_->module()->end(); ++function_it) { if (&*function_it == function_) { - opt::eliminatedeadfunctionsutil::EliminateFunction(context_, - &function_it); + function_it.Erase(); + context_->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); return; } } diff --git a/source/reduce/remove_struct_member_reduction_opportunity.cpp b/source/reduce/remove_struct_member_reduction_opportunity.cpp index 787c629b..da096e1e 100644 --- a/source/reduce/remove_struct_member_reduction_opportunity.cpp +++ b/source/reduce/remove_struct_member_reduction_opportunity.cpp @@ -129,6 +129,8 @@ void RemoveStructMemberReductionOpportunity::Apply() { // Remove the member from the struct type. struct_type_->RemoveInOperand(member_index_); + + context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); } void RemoveStructMemberReductionOpportunity::AdjustAccessedIndices( diff --git a/source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.cpp b/source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.cpp index ca17f9eb..a2be0c4d 100644 --- a/source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.cpp +++ b/source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.cpp @@ -51,6 +51,8 @@ void SimpleConditionalBranchToBranchReductionOpportunity::Apply() { {{SPV_OPERAND_TYPE_ID, {conditional_branch_instruction_->GetSingleWordInOperand( kTrueBranchOperandIndex)}}}); + conditional_branch_instruction_->context()->InvalidateAnalysesExceptFor( + opt::IRContext::kAnalysisNone); } } // namespace reduce diff --git a/source/val/basic_block.h b/source/val/basic_block.h index 5eea4f92..5af4b9e4 100644 --- a/source/val/basic_block.h +++ b/source/val/basic_block.h @@ -139,9 +139,14 @@ class BasicBlock { /// @brief A BasicBlock dominator iterator class /// /// This iterator will iterate over the (post)dominators of the block - class DominatorIterator - : public std::iterator<std::forward_iterator_tag, BasicBlock*> { + class DominatorIterator { public: + using iterator_category = std::forward_iterator_tag; + using value_type = BasicBlock*; + using pointer = value_type*; + using reference = value_type&; + using difference_type = std::ptrdiff_t; + /// @brief Constructs the end of dominator iterator /// /// This will create an iterator which will represent the element diff --git a/source/val/construct.cpp b/source/val/construct.cpp index 733856cb..53008690 100644 --- a/source/val/construct.cpp +++ b/source/val/construct.cpp @@ -78,7 +78,12 @@ Construct::ConstructBlockSet Construct::blocks(Function* function) const { ConstructBlockSet construct_blocks; std::unordered_set<BasicBlock*> corresponding_headers; for (auto& other : corresponding_constructs()) { - corresponding_headers.insert(other->entry_block()); + // The corresponding header can be the same block as this construct's + // header for loops with no loop construct. In those cases, don't add the + // loop header as it prevents finding any blocks in the construct. + if (type() != ConstructType::kContinue || other->entry_block() != header) { + corresponding_headers.insert(other->entry_block()); + } } std::vector<BasicBlock*> stack; stack.push_back(const_cast<BasicBlock*>(header)); diff --git a/source/val/validate.cpp b/source/val/validate.cpp index a2e116b1..45b6a463 100644 --- a/source/val/validate.cpp +++ b/source/val/validate.cpp @@ -143,6 +143,7 @@ spv_result_t ValidateEntryPoints(ValidationState_t& _) { if (_.recursive_entry_points().find(entry_point) != _.recursive_entry_points().end()) { return _.diag(SPV_ERROR_INVALID_BINARY, _.FindDef(entry_point)) + << _.VkErrorID(4634) << "Entry points may not have a call graph with cycles."; } } diff --git a/source/val/validate_atomics.cpp b/source/val/validate_atomics.cpp index dd263a79..fa53ca1f 100644 --- a/source/val/validate_atomics.cpp +++ b/source/val/validate_atomics.cpp @@ -47,6 +47,72 @@ bool IsStorageClassAllowedByUniversalRules(uint32_t storage_class) { } } +bool HasReturnType(uint32_t opcode) { + switch (opcode) { + case SpvOpAtomicStore: + case SpvOpAtomicFlagClear: + return false; + break; + default: + return true; + } +} + +bool HasOnlyFloatReturnType(uint32_t opcode) { + switch (opcode) { + case SpvOpAtomicFAddEXT: + case SpvOpAtomicFMinEXT: + case SpvOpAtomicFMaxEXT: + return true; + break; + default: + return false; + } +} + +bool HasOnlyIntReturnType(uint32_t opcode) { + switch (opcode) { + case SpvOpAtomicCompareExchange: + case SpvOpAtomicCompareExchangeWeak: + case SpvOpAtomicIIncrement: + case SpvOpAtomicIDecrement: + case SpvOpAtomicIAdd: + case SpvOpAtomicISub: + case SpvOpAtomicSMin: + case SpvOpAtomicUMin: + case SpvOpAtomicSMax: + case SpvOpAtomicUMax: + case SpvOpAtomicAnd: + case SpvOpAtomicOr: + case SpvOpAtomicXor: + return true; + break; + default: + return false; + } +} + +bool HasIntOrFloatReturnType(uint32_t opcode) { + switch (opcode) { + case SpvOpAtomicLoad: + case SpvOpAtomicExchange: + return true; + break; + default: + return false; + } +} + +bool HasOnlyBoolReturnType(uint32_t opcode) { + switch (opcode) { + case SpvOpAtomicFlagTestAndSet: + return true; + break; + default: + return false; + } +} + } // namespace namespace spvtools { @@ -55,12 +121,6 @@ namespace val { // Validates correctness of atomic instructions. spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) { const SpvOp opcode = inst->opcode(); - const uint32_t result_type = inst->type_id(); - bool is_atomic_float_opcode = false; - if (opcode == SpvOpAtomicLoad || opcode == SpvOpAtomicStore || - opcode == SpvOpAtomicFAddEXT || opcode == SpvOpAtomicExchange) { - is_atomic_float_opcode = true; - } switch (opcode) { case SpvOpAtomicLoad: case SpvOpAtomicStore: @@ -74,128 +134,47 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) { case SpvOpAtomicISub: case SpvOpAtomicSMin: case SpvOpAtomicUMin: + case SpvOpAtomicFMinEXT: case SpvOpAtomicSMax: case SpvOpAtomicUMax: + case SpvOpAtomicFMaxEXT: case SpvOpAtomicAnd: case SpvOpAtomicOr: case SpvOpAtomicXor: case SpvOpAtomicFlagTestAndSet: case SpvOpAtomicFlagClear: { - if (_.HasCapability(SpvCapabilityKernel) && - (opcode == SpvOpAtomicLoad || opcode == SpvOpAtomicExchange || - opcode == SpvOpAtomicCompareExchange)) { - if (!_.IsFloatScalarType(result_type) && - !_.IsIntScalarType(result_type)) { + const uint32_t result_type = inst->type_id(); + + // All current atomics only are scalar result + // Validate return type first so can just check if pointer type is same + // (if applicable) + if (HasReturnType(opcode)) { + if (HasOnlyFloatReturnType(opcode) && + !_.IsFloatScalarType(result_type)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << spvOpcodeString(opcode) - << ": expected Result Type to be int or float scalar type"; - } - } else if (opcode == SpvOpAtomicFlagTestAndSet) { - if (!_.IsBoolScalarType(result_type)) { + << ": expected Result Type to be float scalar type"; + } else if (HasOnlyIntReturnType(opcode) && + !_.IsIntScalarType(result_type)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << spvOpcodeString(opcode) - << ": expected Result Type to be bool scalar type"; - } - } else if (opcode == SpvOpAtomicFlagClear || opcode == SpvOpAtomicStore) { - assert(result_type == 0); - } else { - if (_.IsFloatScalarType(result_type)) { - if (is_atomic_float_opcode) { - if (opcode == SpvOpAtomicFAddEXT) { - if ((_.GetBitWidth(result_type) == 32) && - (!_.HasCapability(SpvCapabilityAtomicFloat32AddEXT))) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << spvOpcodeString(opcode) - << ": float add atomics require the AtomicFloat32AddEXT " - "capability"; - } - if ((_.GetBitWidth(result_type) == 64) && - (!_.HasCapability(SpvCapabilityAtomicFloat64AddEXT))) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << spvOpcodeString(opcode) - << ": float add atomics require the AtomicFloat64AddEXT " - "capability"; - } - } - } else { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << spvOpcodeString(opcode) - << ": expected Result Type to be int scalar type"; - } - } else if (_.IsIntScalarType(result_type) && - opcode == SpvOpAtomicFAddEXT) { + << ": expected Result Type to be integer scalar type"; + } else if (HasIntOrFloatReturnType(opcode) && + !_.IsFloatScalarType(result_type) && + !_.IsIntScalarType(result_type)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << spvOpcodeString(opcode) - << ": expected Result Type to be float scalar type"; - } else if (!_.IsFloatScalarType(result_type) && - !_.IsIntScalarType(result_type)) { - switch (opcode) { - case SpvOpAtomicFAddEXT: - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << spvOpcodeString(opcode) - << ": expected Result Type to be float scalar type"; - case SpvOpAtomicIIncrement: - case SpvOpAtomicIDecrement: - case SpvOpAtomicIAdd: - case SpvOpAtomicISub: - case SpvOpAtomicSMin: - case SpvOpAtomicSMax: - case SpvOpAtomicUMin: - case SpvOpAtomicUMax: - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << spvOpcodeString(opcode) - << ": expected Result Type to be integer scalar type"; - default: - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << spvOpcodeString(opcode) - << ": expected Result Type to be int or float scalar type"; - } - } - - if (spvIsVulkanEnv(_.context()->target_env) && - (_.GetBitWidth(result_type) != 32 && - (_.GetBitWidth(result_type) != 64 || - !_.HasCapability(SpvCapabilityInt64ImageEXT)))) { - switch (opcode) { - case SpvOpAtomicSMin: - case SpvOpAtomicUMin: - case SpvOpAtomicSMax: - case SpvOpAtomicUMax: - case SpvOpAtomicAnd: - case SpvOpAtomicOr: - case SpvOpAtomicXor: - case SpvOpAtomicIAdd: - case SpvOpAtomicISub: - case SpvOpAtomicFAddEXT: - case SpvOpAtomicLoad: - case SpvOpAtomicStore: - case SpvOpAtomicExchange: - case SpvOpAtomicIIncrement: - case SpvOpAtomicIDecrement: - case SpvOpAtomicCompareExchangeWeak: - case SpvOpAtomicCompareExchange: { - if (_.GetBitWidth(result_type) == 64 && - _.IsIntScalarType(result_type) && - !_.HasCapability(SpvCapabilityInt64Atomics)) - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << spvOpcodeString(opcode) - << ": 64-bit atomics require the Int64Atomics " - "capability"; - } break; - default: - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << spvOpcodeString(opcode) - << ": according to the Vulkan spec atomic Result Type " - "needs " - "to be a 32-bit int scalar type"; - } + << ": expected Result Type to be integer or float scalar type"; + } else if (HasOnlyBoolReturnType(opcode) && + !_.IsBoolScalarType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Result Type to be bool scalar type"; } } - uint32_t operand_index = - opcode == SpvOpAtomicFlagClear || opcode == SpvOpAtomicStore ? 0 : 2; + uint32_t operand_index = HasReturnType(opcode) ? 2 : 0; const uint32_t pointer_type = _.GetOperandTypeId(inst, operand_index++); - uint32_t data_type = 0; uint32_t storage_class = 0; if (!_.GetPointerTypeInfo(pointer_type, &data_type, &storage_class)) { @@ -204,6 +183,14 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) { << ": expected Pointer to be of type OpTypePointer"; } + // Can't use result_type because OpAtomicStore doesn't have a result + if (_.GetBitWidth(data_type) == 64 && _.IsIntScalarType(data_type) && + !_.HasCapability(SpvCapabilityInt64Atomics)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": 64-bit atomics require the Int64Atomics capability"; + } + // Validate storage class against universal rules if (!IsStorageClassAllowedByUniversalRules(storage_class)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) @@ -213,6 +200,7 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) { // Then Shader rules if (_.HasCapability(SpvCapabilityShader)) { + // Vulkan environment rule if (spvIsVulkanEnv(_.context()->target_env)) { if ((storage_class != SpvStorageClassUniform) && (storage_class != SpvStorageClassStorageBuffer) && @@ -231,6 +219,47 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) { << ": Function storage class forbidden when the Shader " "capability is declared."; } + + if (opcode == SpvOpAtomicFAddEXT) { + // result type being float checked already + if ((_.GetBitWidth(result_type) == 32) && + (!_.HasCapability(SpvCapabilityAtomicFloat32AddEXT))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": float add atomics require the AtomicFloat32AddEXT " + "capability"; + } + if ((_.GetBitWidth(result_type) == 64) && + (!_.HasCapability(SpvCapabilityAtomicFloat64AddEXT))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": float add atomics require the AtomicFloat64AddEXT " + "capability"; + } + } else if (opcode == SpvOpAtomicFMinEXT || + opcode == SpvOpAtomicFMaxEXT) { + if ((_.GetBitWidth(result_type) == 16) && + (!_.HasCapability(SpvCapabilityAtomicFloat16MinMaxEXT))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": float min/max atomics require the " + "AtomicFloat16MinMaxEXT capability"; + } + if ((_.GetBitWidth(result_type) == 32) && + (!_.HasCapability(SpvCapabilityAtomicFloat32MinMaxEXT))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": float min/max atomics require the " + "AtomicFloat32MinMaxEXT capability"; + } + if ((_.GetBitWidth(result_type) == 64) && + (!_.HasCapability(SpvCapabilityAtomicFloat64MinMaxEXT))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": float min/max atomics require the " + "AtomicFloat64MinMaxEXT capability"; + } + } } // And finally OpenCL environment rules @@ -254,27 +283,27 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) { } } + // If result and pointer type are different, need to do special check here if (opcode == SpvOpAtomicFlagTestAndSet || opcode == SpvOpAtomicFlagClear) { if (!_.IsIntScalarType(data_type) || _.GetBitWidth(data_type) != 32) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << spvOpcodeString(opcode) - << ": expected Pointer to point to a value of 32-bit int type"; + << ": expected Pointer to point to a value of 32-bit integer " + "type"; } } else if (opcode == SpvOpAtomicStore) { if (!_.IsFloatScalarType(data_type) && !_.IsIntScalarType(data_type)) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << spvOpcodeString(opcode) - << ": expected Pointer to be a pointer to int or float " + << ": expected Pointer to be a pointer to integer or float " << "scalar type"; } - } else { - if (data_type != result_type) { - return _.diag(SPV_ERROR_INVALID_DATA, inst) - << spvOpcodeString(opcode) - << ": expected Pointer to point to a value of type Result " - "Type"; - } + } else if (data_type != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Pointer to point to a value of type Result " + "Type"; } auto memory_scope = inst->GetOperandAs<const uint32_t>(operand_index++); @@ -283,14 +312,15 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) { } const auto equal_semantics_index = operand_index++; - if (auto error = ValidateMemorySemantics(_, inst, equal_semantics_index)) + if (auto error = ValidateMemorySemantics(_, inst, equal_semantics_index, + memory_scope)) return error; if (opcode == SpvOpAtomicCompareExchange || opcode == SpvOpAtomicCompareExchangeWeak) { const auto unequal_semantics_index = operand_index++; - if (auto error = - ValidateMemorySemantics(_, inst, unequal_semantics_index)) + if (auto error = ValidateMemorySemantics( + _, inst, unequal_semantics_index, memory_scope)) return error; // Volatile bits must match for equal and unequal semantics. Previous diff --git a/source/val/validate_barriers.cpp b/source/val/validate_barriers.cpp index b499c8c0..3a9e3e7c 100644 --- a/source/val/validate_barriers.cpp +++ b/source/val/validate_barriers.cpp @@ -69,7 +69,7 @@ spv_result_t BarriersPass(ValidationState_t& _, const Instruction* inst) { return error; } - if (auto error = ValidateMemorySemantics(_, inst, 2)) { + if (auto error = ValidateMemorySemantics(_, inst, 2, memory_scope)) { return error; } break; @@ -82,7 +82,7 @@ spv_result_t BarriersPass(ValidationState_t& _, const Instruction* inst) { return error; } - if (auto error = ValidateMemorySemantics(_, inst, 1)) { + if (auto error = ValidateMemorySemantics(_, inst, 1, memory_scope)) { return error; } break; @@ -119,7 +119,7 @@ spv_result_t BarriersPass(ValidationState_t& _, const Instruction* inst) { return error; } - if (auto error = ValidateMemorySemantics(_, inst, 2)) { + if (auto error = ValidateMemorySemantics(_, inst, 2, memory_scope)) { return error; } break; diff --git a/source/val/validate_cfg.cpp b/source/val/validate_cfg.cpp index a5f6e6a1..36f632a8 100644 --- a/source/val/validate_cfg.cpp +++ b/source/val/validate_cfg.cpp @@ -654,18 +654,15 @@ spv_result_t ValidateStructuredSelections( << "Selection must be structured"; } } else if (terminator->opcode() == SpvOpSwitch) { - uint32_t count = 0; - // Mark the targets as seen now, but only error out if this block was - // missing a merge instruction and there were multiple unseen labels. + if (!merge) { + return _.diag(SPV_ERROR_INVALID_CFG, terminator) + << "OpSwitch must be preceeded by an OpSelectionMerge " + "instruction"; + } + // Mark the targets as seen. for (uint32_t i = 1; i < terminator->operands().size(); i += 2) { const auto target = terminator->GetOperandAs<uint32_t>(i); - if (seen.insert(target).second) { - count++; - } - } - if (!merge && count > 1) { - return _.diag(SPV_ERROR_INVALID_CFG, terminator) - << "Selection must be structured"; + seen.insert(target); } } } diff --git a/source/val/validate_decorations.cpp b/source/val/validate_decorations.cpp index ed336b47..f076b04c 100644 --- a/source/val/validate_decorations.cpp +++ b/source/val/validate_decorations.cpp @@ -1619,7 +1619,7 @@ spv_result_t CheckLocationDecoration(ValidationState_t& vstate, { \ spv_result_t e##LINE = (X); \ if (e##LINE != SPV_SUCCESS) return e##LINE; \ - } + } static_assert(true, "require extra semicolon") #define PASS_OR_BAIL(X) PASS_OR_BAIL_AT_LINE(X, __LINE__) // Check rules for decorations where we start from the decoration rather diff --git a/source/val/validate_extensions.cpp b/source/val/validate_extensions.cpp index dc8c0243..a7167fc1 100644 --- a/source/val/validate_extensions.cpp +++ b/source/val/validate_extensions.cpp @@ -692,8 +692,8 @@ spv_result_t ValidateExtension(ValidationState_t& _, const Instruction* inst) { if (extension == ExtensionToString(kSPV_KHR_workgroup_memory_explicit_layout)) { return _.diag(SPV_ERROR_WRONG_VERSION, inst) - << "SPV_KHR_workgroup_memory_explicit_layout extension " - "requires SPIR-V version 1.4 or later."; + << "SPV_KHR_workgroup_memory_explicit_layout extension " + "requires SPIR-V version 1.4 or later."; } } @@ -1372,7 +1372,16 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) { << "or vector type"; } - const uint32_t interpolant_type = _.GetOperandTypeId(inst, 4); + // If HLSL legalization and first operand is an OpLoad, use load + // pointer as the interpolant lvalue. Else use interpolate first + // operand. + uint32_t interp_id = inst->GetOperandAs<uint32_t>(4); + auto* interp_inst = _.FindDef(interp_id); + uint32_t interpolant_type = (_.options()->before_hlsl_legalization && + interp_inst->opcode() == SpvOpLoad) + ? _.GetOperandTypeId(interp_inst, 2) + : _.GetOperandTypeId(inst, 4); + uint32_t interpolant_storage_class = 0; uint32_t interpolant_data_type = 0; if (!_.GetPointerTypeInfo(interpolant_type, &interpolant_data_type, diff --git a/source/val/validate_image.cpp b/source/val/validate_image.cpp index f7ddec73..e5968d06 100644 --- a/source/val/validate_image.cpp +++ b/source/val/validate_image.cpp @@ -442,7 +442,8 @@ spv_result_t ValidateImageOperands(ValidationState_t& _, << " components, but given " << offset_size; } - if (spvIsVulkanEnv(_.context()->target_env)) { + if (!_.options()->before_hlsl_legalization && + spvIsVulkanEnv(_.context()->target_env)) { if (opcode != SpvOpImageGather && opcode != SpvOpImageDrefGather && opcode != SpvOpImageSparseGather && opcode != SpvOpImageSparseDrefGather) { @@ -1457,12 +1458,21 @@ spv_result_t ValidateImageGather(ValidationState_t& _, } if (opcode == SpvOpImageGather || opcode == SpvOpImageSparseGather) { - const uint32_t component_index_type = _.GetOperandTypeId(inst, 4); + const uint32_t component = inst->GetOperandAs<uint32_t>(4); + const uint32_t component_index_type = _.GetTypeId(component); if (!_.IsIntScalarType(component_index_type) || _.GetBitWidth(component_index_type) != 32) { return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected Component to be 32-bit int scalar"; } + if (spvIsVulkanEnv(_.context()->target_env)) { + if (!spvOpcodeIsConstant(_.GetIdOpcode(component))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4664) + << "Expected Component Operand to be a const object for Vulkan " + "environment"; + } + } } else { assert(opcode == SpvOpImageDrefGather || opcode == SpvOpImageSparseDrefGather); @@ -1500,8 +1510,8 @@ spv_result_t ValidateImageRead(ValidationState_t& _, const Instruction* inst) { if (spvIsVulkanEnv(target_env)) { if (_.GetDimension(actual_result_type) != 4) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Expected " << GetActualResultTypeStr(opcode) - << " to have 4 components"; + << _.VkErrorID(4780) << "Expected " + << GetActualResultTypeStr(opcode) << " to have 4 components"; } } // Check OpenCL below, after we get the image info. diff --git a/source/val/validate_logicals.cpp b/source/val/validate_logicals.cpp index 5886dbf5..bb35f558 100644 --- a/source/val/validate_logicals.cpp +++ b/source/val/validate_logicals.cpp @@ -188,7 +188,7 @@ spv_result_t LogicalsPass(ValidationState_t& _, const Instruction* inst) { case SpvOpTypeStruct: { if (!composites) return fail(); break; - }; + } default: return fail(); diff --git a/source/val/validate_memory.cpp b/source/val/validate_memory.cpp index 2318c962..a4bc0fab 100644 --- a/source/val/validate_memory.cpp +++ b/source/val/validate_memory.cpp @@ -466,6 +466,7 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { if (!_.IsValidStorageClass(storage_class)) { return _.diag(SPV_ERROR_INVALID_BINARY, inst) + << _.VkErrorID(4643) << "Invalid storage class for target environment"; } @@ -536,18 +537,14 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { if (!IsAllowedTypeOrArrayOfSame( _, pointee, {SpvOpTypeImage, SpvOpTypeSampler, SpvOpTypeSampledImage, - SpvOpTypeAccelerationStructureNV, - SpvOpTypeAccelerationStructureKHR, SpvOpTypeRayQueryKHR})) { + SpvOpTypeAccelerationStructureKHR})) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "UniformConstant OpVariable <id> '" << _.getIdName(inst->id()) - << "' has illegal type.\n" - << "From Vulkan spec, section 14.5.2:\n" + << _.VkErrorID(4655) << "UniformConstant OpVariable <id> '" + << _.getIdName(inst->id()) << "' has illegal type.\n" << "Variables identified with the UniformConstant storage class " << "are used only as handles to refer to opaque resources. Such " << "variables must be typed as OpTypeImage, OpTypeSampler, " - << "OpTypeSampledImage, OpTypeAccelerationStructureNV, " - "OpTypeAccelerationStructureKHR, " - "OpTypeRayQueryKHR, " + << "OpTypeSampledImage, OpTypeAccelerationStructureKHR, " << "or an array of one of these types."; } } diff --git a/source/val/validate_memory_semantics.cpp b/source/val/validate_memory_semantics.cpp index 6c3e1d44..d9189313 100644 --- a/source/val/validate_memory_semantics.cpp +++ b/source/val/validate_memory_semantics.cpp @@ -25,7 +25,8 @@ namespace val { spv_result_t ValidateMemorySemantics(ValidationState_t& _, const Instruction* inst, - uint32_t operand_index) { + uint32_t operand_index, + uint32_t memory_scope) { const SpvOp opcode = inst->opcode(); const auto id = inst->GetOperandAs<const uint32_t>(operand_index); bool is_int32 = false, is_const_int32 = false; @@ -178,6 +179,18 @@ spv_result_t ValidateMemorySemantics(ValidationState_t& _, "of the following bits set: Acquire, Release, " "AcquireRelease " "or SequentiallyConsistent"; + } else if (opcode != SpvOpMemoryBarrier && num_memory_order_set_bits) { + // should leave only atomics and control barriers for Vulkan env + bool memory_is_int32 = false, memory_is_const_int32 = false; + uint32_t memory_value = 0; + std::tie(memory_is_int32, memory_is_const_int32, memory_value) = + _.EvalInt32IfConst(memory_scope); + if (memory_is_int32 && memory_value == SpvScopeInvocation) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4641) << spvOpcodeString(opcode) + << ": Vulkan specification requires Memory Semantics to be None " + "if used with Invocation Memory Scope"; + } } if (opcode == SpvOpMemoryBarrier && !includes_storage_class) { diff --git a/source/val/validate_memory_semantics.h b/source/val/validate_memory_semantics.h index 72a3e100..9e6f93a3 100644 --- a/source/val/validate_memory_semantics.h +++ b/source/val/validate_memory_semantics.h @@ -22,7 +22,8 @@ namespace val { spv_result_t ValidateMemorySemantics(ValidationState_t& _, const Instruction* inst, - uint32_t operand_index); + uint32_t operand_index, + uint32_t memory_scope); } // namespace val } // namespace spvtools diff --git a/source/val/validate_misc.cpp b/source/val/validate_misc.cpp index 0c30f3ca..3bc15ca0 100644 --- a/source/val/validate_misc.cpp +++ b/source/val/validate_misc.cpp @@ -72,6 +72,37 @@ spv_result_t ValidateShaderClock(ValidationState_t& _, return SPV_SUCCESS; } +spv_result_t ValidateAssumeTrue(ValidationState_t& _, const Instruction* inst) { + const auto operand_type_id = _.GetOperandTypeId(inst, 0); + if (!operand_type_id || !_.IsBoolScalarType(operand_type_id)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Value operand of OpAssumeTrueKHR must be a boolean scalar"; + } + return SPV_SUCCESS; +} + +spv_result_t ValidateExpect(ValidationState_t& _, const Instruction* inst) { + const auto result_type = inst->type_id(); + if (!_.IsBoolScalarOrVectorType(result_type) && + !_.IsIntScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Result of OpExpectKHR must be a scalar or vector of integer " + "type or boolean type"; + } + + if (_.GetOperandTypeId(inst, 2) != result_type) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Type of Value operand of OpExpectKHR does not match the result " + "type "; + } + if (_.GetOperandTypeId(inst, 3) != result_type) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Type of ExpectedValue operand of OpExpectKHR does not match the " + "result type "; + } + return SPV_SUCCESS; +} + } // namespace spv_result_t MiscPass(ValidationState_t& _, const Instruction* inst) { @@ -152,6 +183,16 @@ spv_result_t MiscPass(ValidationState_t& _, const Instruction* inst) { return error; } break; + case SpvOpAssumeTrueKHR: + if (auto error = ValidateAssumeTrue(_, inst)) { + return error; + } + break; + case SpvOpExpectKHR: + if (auto error = ValidateExpect(_, inst)) { + return error; + } + break; default: break; } diff --git a/source/val/validate_scopes.cpp b/source/val/validate_scopes.cpp index a92f7fd3..29ba5831 100644 --- a/source/val/validate_scopes.cpp +++ b/source/val/validate_scopes.cpp @@ -105,21 +105,30 @@ spv_result_t ValidateExecutionScope(ValidationState_t& _, } } - // If OpControlBarrier is used in fragment, vertex, tessellation evaluation, - // or geometry stages, the execution Scope must be Subgroup. + // OpControlBarrier must only use Subgroup execution scope for a subset of + // execution models. if (opcode == SpvOpControlBarrier && value != SpvScopeSubgroup) { + std::string errorVUID = _.VkErrorID(4682); _.function(inst->function()->id()) - ->RegisterExecutionModelLimitation([](SpvExecutionModel model, - std::string* message) { + ->RegisterExecutionModelLimitation([errorVUID]( + SpvExecutionModel model, + std::string* message) { if (model == SpvExecutionModelFragment || model == SpvExecutionModelVertex || model == SpvExecutionModelGeometry || - model == SpvExecutionModelTessellationEvaluation) { + model == SpvExecutionModelTessellationEvaluation || + model == SpvExecutionModelRayGenerationKHR || + model == SpvExecutionModelIntersectionKHR || + model == SpvExecutionModelAnyHitKHR || + model == SpvExecutionModelClosestHitKHR || + model == SpvExecutionModelMissKHR) { if (message) { *message = - "in Vulkan evironment, OpControlBarrier execution scope " - "must be Subgroup for Fragment, Vertex, Geometry and " - "TessellationEvaluation execution models"; + errorVUID + + "in Vulkan environment, OpControlBarrier execution scope " + "must be Subgroup for Fragment, Vertex, Geometry, " + "TessellationEvaluation, RayGeneration, Intersection, " + "AnyHit, ClosestHit, and Miss execution models"; } return false; } @@ -127,11 +136,34 @@ spv_result_t ValidateExecutionScope(ValidationState_t& _, }); } + // Only subset of execution models support Workgroup. + if (value == SpvScopeWorkgroup) { + std::string errorVUID = _.VkErrorID(4637); + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + [errorVUID](SpvExecutionModel model, std::string* message) { + if (model != SpvExecutionModelTaskNV && + model != SpvExecutionModelMeshNV && + model != SpvExecutionModelTessellationControl && + model != SpvExecutionModelGLCompute) { + if (message) { + *message = + errorVUID + + "in Vulkan environment, Workgroup execution scope is " + "only for TaskNV, MeshNV, TessellationControl, and " + "GLCompute execution models"; + } + return false; + } + return true; + }); + } + // Vulkan generic rules // Scope for execution must be limited to Workgroup or Subgroup if (value != SpvScopeWorkgroup && value != SpvScopeSubgroup) { return _.diag(SPV_ERROR_INVALID_DATA, inst) - << spvOpcodeString(opcode) + << _.VkErrorID(4636) << spvOpcodeString(opcode) << ": in Vulkan environment Execution Scope is limited to " << "Workgroup and Subgroup"; } diff --git a/source/val/validate_type.cpp b/source/val/validate_type.cpp index 6a5ea3c1..612fc5c2 100644 --- a/source/val/validate_type.cpp +++ b/source/val/validate_type.cpp @@ -427,7 +427,8 @@ spv_result_t ValidateTypeStruct(ValidationState_t& _, const Instruction* inst) { if (spvIsVulkanEnv(_.context()->target_env) && !_.options()->before_hlsl_legalization && ContainsOpaqueType(_, inst)) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "In " << spvLogStringForEnv(_.context()->target_env) + << _.VkErrorID(4667) << "In " + << spvLogStringForEnv(_.context()->target_env) << ", OpTypeStruct must not contain an opaque type."; } @@ -462,6 +463,7 @@ spv_result_t ValidateTypePointer(ValidationState_t& _, if (!_.IsValidStorageClass(storage_class)) { return _.diag(SPV_ERROR_INVALID_BINARY, inst) + << _.VkErrorID(4643) << "Invalid storage class for target environment"; } diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp index 10c0fcdf..52821636 100644 --- a/source/val/validation_state.cpp +++ b/source/val/validation_state.cpp @@ -520,17 +520,39 @@ void ValidationState_t::RegisterDebugInstruction(const Instruction* inst) { void ValidationState_t::RegisterInstruction(Instruction* inst) { if (inst->id()) all_definitions_.insert(std::make_pair(inst->id(), inst)); - // If the instruction is using an OpTypeSampledImage as an operand, it should - // be recorded. The validator will ensure that all usages of an - // OpTypeSampledImage and its definition are in the same basic block. + // Some validation checks are easier by getting all the consumers for (uint16_t i = 0; i < inst->operands().size(); ++i) { const spv_parsed_operand_t& operand = inst->operand(i); - if (SPV_OPERAND_TYPE_ID == operand.type) { + if ((SPV_OPERAND_TYPE_ID == operand.type) || + (SPV_OPERAND_TYPE_TYPE_ID == operand.type)) { const uint32_t operand_word = inst->word(operand.offset); Instruction* operand_inst = FindDef(operand_word); - if (operand_inst && SpvOpSampledImage == operand_inst->opcode()) { + if (!operand_inst) { + continue; + } + + // If the instruction is using an OpTypeSampledImage as an operand, it + // should be recorded. The validator will ensure that all usages of an + // OpTypeSampledImage and its definition are in the same basic block. + if ((SPV_OPERAND_TYPE_ID == operand.type) && + (SpvOpSampledImage == operand_inst->opcode())) { RegisterSampledImageConsumer(operand_word, inst); } + + // In order to track storage classes (not Function) used per execution + // model we can't use RegisterExecutionModelLimitation on instructions + // like OpTypePointer which are going to be in the pre-function section. + // Instead just need to register storage class usage for consumers in a + // function block. + if (inst->function()) { + if (operand_inst->opcode() == SpvOpTypePointer) { + RegisterStorageClassConsumer( + operand_inst->GetOperandAs<SpvStorageClass>(1), inst); + } else if (operand_inst->opcode() == SpvOpVariable) { + RegisterStorageClassConsumer( + operand_inst->GetOperandAs<SpvStorageClass>(2), inst); + } + } } } } @@ -550,6 +572,57 @@ void ValidationState_t::RegisterSampledImageConsumer(uint32_t sampled_image_id, sampled_image_consumers_[sampled_image_id].push_back(consumer); } +void ValidationState_t::RegisterStorageClassConsumer( + SpvStorageClass storage_class, Instruction* consumer) { + if (spvIsVulkanEnv(context()->target_env)) { + if (storage_class == SpvStorageClassOutput) { + std::string errorVUID = VkErrorID(4644); + function(consumer->function()->id()) + ->RegisterExecutionModelLimitation([errorVUID]( + SpvExecutionModel model, std::string* message) { + if (model == SpvExecutionModelGLCompute || + model == SpvExecutionModelRayGenerationKHR || + model == SpvExecutionModelIntersectionKHR || + model == SpvExecutionModelAnyHitKHR || + model == SpvExecutionModelClosestHitKHR || + model == SpvExecutionModelMissKHR || + model == SpvExecutionModelCallableKHR) { + if (message) { + *message = + errorVUID + + "in Vulkan evironment, Output Storage Class must not be " + "used in GLCompute, RayGenerationKHR, IntersectionKHR, " + "AnyHitKHR, ClosestHitKHR, MissKHR, or CallableKHR " + "execution models"; + } + return false; + } + return true; + }); + } + + if (storage_class == SpvStorageClassWorkgroup) { + std::string errorVUID = VkErrorID(4645); + function(consumer->function()->id()) + ->RegisterExecutionModelLimitation([errorVUID]( + SpvExecutionModel model, std::string* message) { + if (model != SpvExecutionModelGLCompute && + model != SpvExecutionModelTaskNV && + model != SpvExecutionModelMeshNV) { + if (message) { + *message = + errorVUID + + "in Vulkan evironment, Workgroup Storage Class is limited " + "to MeshNV, TaskNV, and GLCompute execution model"; + } + return false; + } + return true; + }); + } + } +} + uint32_t ValidationState_t::getIdBound() const { return id_bound_; } void ValidationState_t::setIdBound(const uint32_t bound) { id_bound_ = bound; } @@ -1255,12 +1328,12 @@ bool ValidationState_t::IsValidStorageClass( case SpvStorageClassFunction: case SpvStorageClassPushConstant: case SpvStorageClassPhysicalStorageBuffer: - case SpvStorageClassRayPayloadNV: - case SpvStorageClassIncomingRayPayloadNV: - case SpvStorageClassHitAttributeNV: - case SpvStorageClassCallableDataNV: - case SpvStorageClassIncomingCallableDataNV: - case SpvStorageClassShaderRecordBufferNV: + case SpvStorageClassRayPayloadKHR: + case SpvStorageClassIncomingRayPayloadKHR: + case SpvStorageClassHitAttributeKHR: + case SpvStorageClassCallableDataKHR: + case SpvStorageClassIncomingCallableDataKHR: + case SpvStorageClassShaderRecordBufferKHR: return true; default: return false; @@ -1676,16 +1749,30 @@ std::string ValidationState_t::VkErrorID(uint32_t id, return VUID_WRAP(VUID-ShadingRateKHR-ShadingRateKHR-04492); case 4633: return VUID_WRAP(VUID-StandaloneSpirv-None-04633); + case 4634: + return VUID_WRAP(VUID-StandaloneSpirv-None-04634); case 4635: return VUID_WRAP(VUID-StandaloneSpirv-None-04635); + case 4636: + return VUID_WRAP(VUID-StandaloneSpirv-None-04636); + case 4637: + return VUID_WRAP(VUID-StandaloneSpirv-None-04637); case 4638: return VUID_WRAP(VUID-StandaloneSpirv-None-04638); case 4639: return VUID_WRAP(VUID-StandaloneSpirv-None-04639); case 4640: return VUID_WRAP(VUID-StandaloneSpirv-None-04640); + case 4641: + return VUID_WRAP(VUID-StandaloneSpirv-None-04641); case 4642: return VUID_WRAP(VUID-StandaloneSpirv-None-04642); + case 4643: + return VUID_WRAP(VUID-StandaloneSpirv-None-04643); + case 4644: + return VUID_WRAP(VUID-StandaloneSpirv-None-04644); + case 4645: + return VUID_WRAP(VUID-StandaloneSpirv-None-04645); case 4651: return VUID_WRAP(VUID-StandaloneSpirv-OpVariable-04651); case 4652: @@ -1694,6 +1781,8 @@ std::string ValidationState_t::VkErrorID(uint32_t id, return VUID_WRAP(VUID-StandaloneSpirv-OriginLowerLeft-04653); case 4654: return VUID_WRAP(VUID-StandaloneSpirv-PixelCenterInteger-04654); + case 4655: + return VUID_WRAP(VUID-StandaloneSpirv-UniformConstant-04655); case 4656: return VUID_WRAP(VUID-StandaloneSpirv-OpTypeImage-04656); case 4657: @@ -1706,12 +1795,18 @@ std::string ValidationState_t::VkErrorID(uint32_t id, return VUID_WRAP(VUID-StandaloneSpirv-Offset-04662); case 4663: return VUID_WRAP(VUID-StandaloneSpirv-Offset-04663); + case 4664: + return VUID_WRAP(VUID-StandaloneSpirv-OpImageGather-04664); + case 4667: + return VUID_WRAP(VUID-StandaloneSpirv-None-04667); case 4669: return VUID_WRAP(VUID-StandaloneSpirv-GLSLShared-04669); case 4675: return VUID_WRAP(VUID-StandaloneSpirv-FPRoundingMode-04675); case 4677: return VUID_WRAP(VUID-StandaloneSpirv-Invariant-04677); + case 4682: + return VUID_WRAP(VUID-StandaloneSpirv-OpControlBarrier-04682); case 4683: return VUID_WRAP(VUID-StandaloneSpirv-LocalSize-04683); case 4685: @@ -1730,9 +1825,11 @@ 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 4780: + return VUID_WRAP(VUID-StandaloneSpirv-Result-04780); default: return ""; // unknown id - }; + } // clang-format on } diff --git a/source/val/validation_state.h b/source/val/validation_state.h index 85111391..57634bf4 100644 --- a/source/val/validation_state.h +++ b/source/val/validation_state.h @@ -465,6 +465,10 @@ class ValidationState_t { void RegisterSampledImageConsumer(uint32_t sampled_image_id, Instruction* consumer); + // Record a function's storage class consumer instruction + void RegisterStorageClassConsumer(SpvStorageClass storage_class, + Instruction* consumer); + /// Returns the set of Global Variables. std::unordered_set<uint32_t>& global_vars() { return global_vars_; } diff --git a/test/binary_parse_test.cpp b/test/binary_parse_test.cpp index 93e87bdd..9a13f22c 100644 --- a/test/binary_parse_test.cpp +++ b/test/binary_parse_test.cpp @@ -198,7 +198,7 @@ ParsedInstruction MakeParsedInt32TypeInstruction(uint32_t result_id) { class BinaryParseTest : public spvtest::TextToBinaryTestBase<::testing::Test> { protected: - ~BinaryParseTest() { spvDiagnosticDestroy(diagnostic_); } + ~BinaryParseTest() override { spvDiagnosticDestroy(diagnostic_); } void Parse(const SpirvVector& words, spv_result_t expected_result, bool flip_words = false) { diff --git a/test/binary_to_text_test.cpp b/test/binary_to_text_test.cpp index e8a02fd5..9cad9661 100644 --- a/test/binary_to_text_test.cpp +++ b/test/binary_to_text_test.cpp @@ -36,12 +36,12 @@ class BinaryToText : public ::testing::Test { public: BinaryToText() : context(spvContextCreate(SPV_ENV_UNIVERSAL_1_0)), binary(nullptr) {} - ~BinaryToText() { + ~BinaryToText() override { spvBinaryDestroy(binary); spvContextDestroy(context); } - virtual void SetUp() { + void SetUp() override { const char* textStr = R"( OpSource OpenCL_C 12 OpMemoryModel Physical64 OpenCL @@ -72,7 +72,7 @@ class BinaryToText : public ::testing::Test { ASSERT_EQ(SPV_SUCCESS, error); } - virtual void TearDown() { + void TearDown() override { spvBinaryDestroy(binary); binary = nullptr; } diff --git a/test/enum_string_mapping_test.cpp b/test/enum_string_mapping_test.cpp index 9bbd8ca8..52aa653e 100644 --- a/test/enum_string_mapping_test.cpp +++ b/test/enum_string_mapping_test.cpp @@ -181,6 +181,8 @@ INSTANTIATE_TEST_SUITE_P( {SpvCapabilityDeviceGroup, "DeviceGroup"}, {SpvCapabilityAtomicFloat32AddEXT, "AtomicFloat32AddEXT"}, {SpvCapabilityAtomicFloat64AddEXT, "AtomicFloat64AddEXT"}, + {SpvCapabilityAtomicFloat32MinMaxEXT, "AtomicFloat32MinMaxEXT"}, + {SpvCapabilityAtomicFloat64MinMaxEXT, "AtomicFloat64MinMaxEXT"}, {SpvCapabilityMultiView, "MultiView"}, {SpvCapabilityInt64ImageEXT, "Int64ImageEXT"}, {SpvCapabilitySampleMaskOverrideCoverageNV, diff --git a/test/fuzz/CMakeLists.txt b/test/fuzz/CMakeLists.txt index 2e93293c..8acebde6 100644 --- a/test/fuzz/CMakeLists.txt +++ b/test/fuzz/CMakeLists.txt @@ -17,6 +17,7 @@ if (${SPIRV_BUILD_FUZZER}) set(SOURCES fuzz_test_util.h + available_instructions_test.cpp call_graph_test.cpp comparator_deep_blocks_first_test.cpp data_synonym_transformation_test.cpp @@ -30,6 +31,7 @@ if (${SPIRV_BUILD_FUZZER}) fuzzer_pass_construct_composites_test.cpp fuzzer_pass_donate_modules_test.cpp fuzzer_pass_outline_functions_test.cpp + fuzzerutil_test.cpp instruction_descriptor_test.cpp fuzzer_pass_test.cpp replayer_test.cpp @@ -113,6 +115,8 @@ if (${SPIRV_BUILD_FUZZER}) transformation_store_test.cpp transformation_swap_commutable_operands_test.cpp transformation_swap_conditional_branch_operands_test.cpp + transformation_swap_function_variables_test.cpp + transformation_swap_two_functions_test.cpp transformation_toggle_access_chain_instruction_test.cpp transformation_record_synonymous_constants_test.cpp transformation_vector_shuffle_test.cpp diff --git a/test/fuzz/available_instructions_test.cpp b/test/fuzz/available_instructions_test.cpp new file mode 100644 index 00000000..dc8a3b5a --- /dev/null +++ b/test/fuzz/available_instructions_test.cpp @@ -0,0 +1,328 @@ +// Copyright (c) 2021 Alastair F. Donaldson +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/available_instructions.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(AvailableInstructionsTest, BasicTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFloat 32 + %9 = OpTypePointer Function %8 + %10 = OpTypeFunction %6 %7 %9 + %15 = OpTypeVector %8 2 + %16 = OpTypePointer Private %15 + %17 = OpVariable %16 Private + %18 = OpConstant %8 1 + %19 = OpConstant %8 2 + %20 = OpConstantComposite %15 %18 %19 + %21 = OpTypeVector %8 4 + %22 = OpTypePointer Private %21 + %23 = OpVariable %22 Private + %24 = OpConstant %8 10 + %25 = OpConstant %8 20 + %26 = OpConstant %8 30 + %27 = OpConstant %8 40 + %28 = OpConstantComposite %21 %24 %25 %26 %27 + %31 = OpTypeInt 32 0 + %32 = OpConstant %31 0 + %33 = OpTypePointer Private %8 + %41 = OpTypeBool + %46 = OpConstant %6 1 + %54 = OpConstant %6 10 + %57 = OpConstant %31 3 + %61 = OpConstant %6 0 + %66 = OpConstant %6 3 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %55 = OpVariable %7 Function + %56 = OpVariable %9 Function + %65 = OpVariable %7 Function + %68 = OpVariable %7 Function + OpStore %17 %20 + OpStore %23 %28 + OpStore %55 %54 + %58 = OpAccessChain %33 %23 %57 + %59 = OpLoad %8 %58 + OpStore %56 %59 + %60 = OpFunctionCall %6 %13 %55 %56 + %100 = OpCopyObject %21 %28 + %62 = OpSGreaterThan %41 %60 %61 + OpSelectionMerge %64 None + OpBranchConditional %62 %63 %67 + %63 = OpLabel + OpStore %65 %66 + %101 = OpCopyObject %21 %28 + OpBranch %64 + %67 = OpLabel + OpStore %68 %61 + OpBranch %69 + %69 = OpLabel + OpLoopMerge %71 %72 None + OpBranch %73 + %73 = OpLabel + %74 = OpLoad %6 %68 + %75 = OpSLessThan %41 %74 %54 + OpBranchConditional %75 %70 %71 + %70 = OpLabel + %76 = OpLoad %6 %65 + %77 = OpIAdd %6 %76 %46 + OpStore %65 %77 + OpBranch %72 + %72 = OpLabel + %78 = OpLoad %6 %68 + %79 = OpIAdd %6 %78 %46 + OpStore %68 %79 + OpBranch %69 + %71 = OpLabel + %102 = OpCopyObject %21 %28 + OpBranch %64 + %64 = OpLabel + OpReturn + OpFunctionEnd + %13 = OpFunction %6 None %10 + %11 = OpFunctionParameter %7 + %12 = OpFunctionParameter %9 + %14 = OpLabel + %29 = OpVariable %7 Function + %30 = OpLoad %6 %11 + %34 = OpAccessChain %33 %17 %32 + %35 = OpLoad %8 %34 + %36 = OpConvertFToS %6 %35 + %37 = OpIAdd %6 %30 %36 + OpStore %29 %37 + %38 = OpLoad %6 %11 + %39 = OpLoad %8 %12 + %40 = OpConvertFToS %6 %39 + %42 = OpSLessThan %41 %38 %40 + %103 = OpCopyObject %21 %28 + OpSelectionMerge %44 None + OpBranchConditional %42 %43 %48 + %43 = OpLabel + %45 = OpLoad %6 %29 + %47 = OpIAdd %6 %45 %46 + OpStore %29 %47 + OpBranch %44 + %48 = OpLabel + %49 = OpLoad %6 %29 + %50 = OpISub %6 %49 %46 + OpStore %29 %50 + OpBranch %44 + %44 = OpLabel + %51 = OpLoad %6 %29 + OpReturnValue %51 + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + opt::Instruction* i1 = context->get_def_use_mgr()->GetDef(55); + opt::Instruction* i2 = context->get_def_use_mgr()->GetDef(101); + opt::Instruction* i3 = &*context->cfg()->block(67)->begin(); + opt::Instruction* i4 = context->get_def_use_mgr()->GetDef(74); + opt::Instruction* i5 = context->get_def_use_mgr()->GetDef(102); + opt::Instruction* i6 = context->get_def_use_mgr()->GetDef(30); + opt::Instruction* i7 = context->get_def_use_mgr()->GetDef(47); + opt::Instruction* i8 = context->get_def_use_mgr()->GetDef(50); + opt::Instruction* i9 = context->get_def_use_mgr()->GetDef(51); + + { + AvailableInstructions no_instructions( + context.get(), + [](opt::IRContext*, opt::Instruction*) -> bool { return false; }); + for (auto i : {i1, i2, i3, i4, i5, i6, i7, i8, i9}) { + auto available = no_instructions.GetAvailableBeforeInstruction(i); + ASSERT_EQ(0, available.size()); + ASSERT_TRUE(available.empty()); + } + } + { + AvailableInstructions all_instructions( + context.get(), + [](opt::IRContext*, opt::Instruction*) -> bool { return true; }); + { + auto available = all_instructions.GetAvailableBeforeInstruction(i1); + ASSERT_FALSE(available.empty()); + ASSERT_EQ(30, available.size()); + ASSERT_EQ(SpvOpTypeVoid, available[0]->opcode()); + ASSERT_EQ(SpvOpVariable, available[15]->opcode()); + } + { + auto available = all_instructions.GetAvailableBeforeInstruction(i2); + ASSERT_FALSE(available.empty()); + ASSERT_EQ(46, available.size()); + ASSERT_EQ(SpvOpTypeVoid, available[0]->opcode()); + ASSERT_EQ(SpvOpTypePointer, available[3]->opcode()); + ASSERT_EQ(SpvOpVariable, available[15]->opcode()); + ASSERT_EQ(SpvOpFunctionCall, available[40]->opcode()); + ASSERT_EQ(SpvOpStore, available[45]->opcode()); + } + { + auto available = all_instructions.GetAvailableBeforeInstruction(i3); + ASSERT_FALSE(available.empty()); + ASSERT_EQ(45, available.size()); + ASSERT_EQ(SpvOpTypeVoid, available[0]->opcode()); + ASSERT_EQ(SpvOpTypePointer, available[3]->opcode()); + ASSERT_EQ(SpvOpVariable, available[15]->opcode()); + ASSERT_EQ(SpvOpFunctionCall, available[40]->opcode()); + ASSERT_EQ(SpvOpBranchConditional, available[44]->opcode()); + } + { + auto available = all_instructions.GetAvailableBeforeInstruction(i6); + ASSERT_FALSE(available.empty()); + ASSERT_EQ(33, available.size()); + ASSERT_EQ(SpvOpTypeVoid, available[0]->opcode()); + ASSERT_EQ(SpvOpTypeFloat, available[4]->opcode()); + ASSERT_EQ(SpvOpTypePointer, available[8]->opcode()); + ASSERT_EQ(SpvOpConstantComposite, available[12]->opcode()); + ASSERT_EQ(SpvOpConstant, available[16]->opcode()); + ASSERT_EQ(SpvOpFunctionParameter, available[30]->opcode()); + ASSERT_EQ(SpvOpFunctionParameter, available[31]->opcode()); + ASSERT_EQ(SpvOpVariable, available[32]->opcode()); + } + } + { + AvailableInstructions vector_instructions( + context.get(), + [](opt::IRContext* ir_context, opt::Instruction* inst) -> bool { + return inst->type_id() != 0 && ir_context->get_type_mgr() + ->GetType(inst->type_id()) + ->AsVector() != nullptr; + }); + { + auto available = vector_instructions.GetAvailableBeforeInstruction(i4); + ASSERT_FALSE(available.empty()); + ASSERT_EQ(3, available.size()); + ASSERT_EQ(SpvOpConstantComposite, available[0]->opcode()); + ASSERT_EQ(SpvOpConstantComposite, available[1]->opcode()); + ASSERT_EQ(SpvOpCopyObject, available[2]->opcode()); + } + { + auto available = vector_instructions.GetAvailableBeforeInstruction(i5); + ASSERT_FALSE(available.empty()); + ASSERT_EQ(3, available.size()); + ASSERT_EQ(SpvOpConstantComposite, available[0]->opcode()); + ASSERT_EQ(SpvOpConstantComposite, available[1]->opcode()); + ASSERT_EQ(SpvOpCopyObject, available[2]->opcode()); + } + { + auto available = vector_instructions.GetAvailableBeforeInstruction(i6); + ASSERT_FALSE(available.empty()); + ASSERT_EQ(2, available.size()); + ASSERT_EQ(SpvOpConstantComposite, available[0]->opcode()); + ASSERT_EQ(SpvOpConstantComposite, available[1]->opcode()); + } + } + { + AvailableInstructions integer_add_instructions( + context.get(), [](opt::IRContext*, opt::Instruction* inst) -> bool { + return inst->opcode() == SpvOpIAdd; + }); + { + auto available = + integer_add_instructions.GetAvailableBeforeInstruction(i7); + ASSERT_FALSE(available.empty()); + ASSERT_EQ(1, available.size()); + ASSERT_EQ(SpvOpIAdd, available[0]->opcode()); + } + { + auto available = + integer_add_instructions.GetAvailableBeforeInstruction(i8); + ASSERT_FALSE(available.empty()); + ASSERT_EQ(1, available.size()); + ASSERT_EQ(SpvOpIAdd, available[0]->opcode()); + } + { + auto available = + integer_add_instructions.GetAvailableBeforeInstruction(i9); + ASSERT_FALSE(available.empty()); + ASSERT_EQ(1, available.size()); + ASSERT_EQ(SpvOpIAdd, available[0]->opcode()); + } + } +} + +TEST(AvailableInstructionsTest, UnreachableBlock) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpName %4 "main" + OpName %8 "x" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + %12 = OpLoad %6 %8 + OpReturn + %10 = OpLabel + %11 = OpLoad %6 %8 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + AvailableInstructions all_instructions( + context.get(), + [](opt::IRContext*, opt::Instruction*) -> bool { return true; }); + ASSERT_EQ(7, all_instructions + .GetAvailableBeforeInstruction( + context->get_def_use_mgr()->GetDef(12)) + .size()); + +#ifndef NDEBUG + ASSERT_DEATH(all_instructions.GetAvailableBeforeInstruction( + context->get_def_use_mgr()->GetDef(11)), + "Availability can only be queried for reachable instructions."); +#endif +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/test/fuzz/fuzz_test_util.cpp b/test/fuzz/fuzz_test_util.cpp index 28d3f892..bf0a4ff5 100644 --- a/test/fuzz/fuzz_test_util.cpp +++ b/test/fuzz/fuzz_test_util.cpp @@ -160,13 +160,19 @@ void ApplyAndCheckFreshIds( const Transformation& transformation, opt::IRContext* ir_context, TransformationContext* transformation_context, const std::unordered_set<uint32_t>& issued_overflow_ids) { + // To ensure that we cover all ToMessage and message-based constructor methods + // in our tests, we turn this into a message and back into a transformation, + // and use the reconstructed transformation in the rest of the function. + auto message = transformation.ToMessage(); + auto reconstructed_transformation = Transformation::FromMessage(message); + opt::analysis::DefUseManager::IdToDefMap before_transformation = ir_context->get_def_use_mgr()->id_to_defs(); - transformation.Apply(ir_context, transformation_context); + reconstructed_transformation->Apply(ir_context, transformation_context); opt::analysis::DefUseManager::IdToDefMap after_transformation = ir_context->get_def_use_mgr()->id_to_defs(); std::unordered_set<uint32_t> fresh_ids_for_transformation = - transformation.GetFreshIds(); + reconstructed_transformation->GetFreshIds(); for (auto& entry : after_transformation) { uint32_t id = entry.first; bool introduced_by_transformation_message = diff --git a/test/fuzz/fuzzer_pass_add_opphi_synonyms_test.cpp b/test/fuzz/fuzzer_pass_add_opphi_synonyms_test.cpp index f7a09964..734f47af 100644 --- a/test/fuzz/fuzzer_pass_add_opphi_synonyms_test.cpp +++ b/test/fuzz/fuzzer_pass_add_opphi_synonyms_test.cpp @@ -128,8 +128,8 @@ TEST(FuzzerPassAddOpPhiSynonymsTest, HelperFunctions) { kConsoleMessageConsumer)); TransformationContext transformation_context( MakeUnique<FactManager>(context.get()), validator_options); - PseudoRandomGenerator prng(0); - FuzzerContext fuzzer_context(&prng, 100); + FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100, + false); protobufs::TransformationSequence transformation_sequence; FuzzerPassAddOpPhiSynonyms fuzzer_pass(context.get(), &transformation_context, diff --git a/test/fuzz/fuzzer_pass_construct_composites_test.cpp b/test/fuzz/fuzzer_pass_construct_composites_test.cpp index d49d1d68..a02176b2 100644 --- a/test/fuzz/fuzzer_pass_construct_composites_test.cpp +++ b/test/fuzz/fuzzer_pass_construct_composites_test.cpp @@ -77,7 +77,8 @@ TEST(FuzzerPassConstructCompositesTest, IsomorphicStructs) { const auto env = SPV_ENV_UNIVERSAL_1_3; const auto consumer = nullptr; - auto prng = MakeUnique<PseudoRandomGenerator>(0); + FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100, + false); for (uint32_t i = 0; i < 10; i++) { const auto context = @@ -87,7 +88,6 @@ TEST(FuzzerPassConstructCompositesTest, IsomorphicStructs) { context.get(), validator_options, kConsoleMessageConsumer)); TransformationContext transformation_context( MakeUnique<FactManager>(context.get()), validator_options); - FuzzerContext fuzzer_context(prng.get(), 100); protobufs::TransformationSequence transformation_sequence; FuzzerPassConstructComposites fuzzer_pass( @@ -158,7 +158,8 @@ TEST(FuzzerPassConstructCompositesTest, IsomorphicArrays) { const auto env = SPV_ENV_UNIVERSAL_1_3; const auto consumer = nullptr; - auto prng = MakeUnique<PseudoRandomGenerator>(0); + FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100, + false); for (uint32_t i = 0; i < 10; i++) { const auto context = @@ -168,7 +169,6 @@ TEST(FuzzerPassConstructCompositesTest, IsomorphicArrays) { context.get(), validator_options, kConsoleMessageConsumer)); TransformationContext transformation_context( MakeUnique<FactManager>(context.get()), validator_options); - FuzzerContext fuzzer_context(prng.get(), 100); protobufs::TransformationSequence transformation_sequence; FuzzerPassConstructComposites fuzzer_pass( diff --git a/test/fuzz/fuzzer_pass_donate_modules_test.cpp b/test/fuzz/fuzzer_pass_donate_modules_test.cpp index 1a7cd4ab..f11885d4 100644 --- a/test/fuzz/fuzzer_pass_donate_modules_test.cpp +++ b/test/fuzz/fuzzer_pass_donate_modules_test.cpp @@ -204,8 +204,8 @@ TEST(FuzzerPassDonateModulesTest, BasicDonation) { TransformationContext transformation_context( MakeUnique<FactManager>(recipient_context.get()), validator_options); - PseudoRandomGenerator prng(0); - FuzzerContext fuzzer_context(&prng, 100); + FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100, + false); protobufs::TransformationSequence transformation_sequence; FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), @@ -285,8 +285,8 @@ TEST(FuzzerPassDonateModulesTest, DonationWithUniforms) { TransformationContext transformation_context( MakeUnique<FactManager>(recipient_context.get()), validator_options); - PseudoRandomGenerator prng(0); - FuzzerContext fuzzer_context(&prng, 100); + FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100, + false); protobufs::TransformationSequence transformation_sequence; FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), @@ -416,8 +416,8 @@ TEST(FuzzerPassDonateModulesTest, DonationWithInputAndOutputVariables) { TransformationContext transformation_context( MakeUnique<FactManager>(recipient_context.get()), validator_options); - PseudoRandomGenerator prng(0); - FuzzerContext fuzzer_context(&prng, 100); + FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100, + false); protobufs::TransformationSequence transformation_sequence; FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), @@ -511,8 +511,8 @@ TEST(FuzzerPassDonateModulesTest, DonateFunctionTypeWithDifferentPointers) { TransformationContext transformation_context( MakeUnique<FactManager>(recipient_context.get()), validator_options); - PseudoRandomGenerator prng(0); - FuzzerContext fuzzer_context(&prng, 100); + FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100, + false); protobufs::TransformationSequence transformation_sequence; FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), @@ -581,8 +581,8 @@ TEST(FuzzerPassDonateModulesTest, DonateOpConstantNull) { TransformationContext transformation_context( MakeUnique<FactManager>(recipient_context.get()), validator_options); - PseudoRandomGenerator prng(0); - FuzzerContext fuzzer_context(&prng, 100); + FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100, + false); protobufs::TransformationSequence transformation_sequence; FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), @@ -709,8 +709,8 @@ TEST(FuzzerPassDonateModulesTest, DonateCodeThatUsesImages) { TransformationContext transformation_context( MakeUnique<FactManager>(recipient_context.get()), validator_options); - PseudoRandomGenerator prng(0); - FuzzerContext fuzzer_context(&prng, 100); + FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100, + false); protobufs::TransformationSequence transformation_sequence; FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), @@ -805,8 +805,8 @@ TEST(FuzzerPassDonateModulesTest, DonateCodeThatUsesSampler) { TransformationContext transformation_context( MakeUnique<FactManager>(recipient_context.get()), validator_options); - PseudoRandomGenerator prng(0); - FuzzerContext fuzzer_context(&prng, 100); + FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100, + false); protobufs::TransformationSequence transformation_sequence; FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), @@ -937,8 +937,8 @@ TEST(FuzzerPassDonateModulesTest, DonateCodeThatUsesImageStructField) { TransformationContext transformation_context( MakeUnique<FactManager>(recipient_context.get()), validator_options); - PseudoRandomGenerator prng(0); - FuzzerContext fuzzer_context(&prng, 100); + FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100, + false); protobufs::TransformationSequence transformation_sequence; FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), @@ -1073,8 +1073,8 @@ TEST(FuzzerPassDonateModulesTest, DonateCodeThatUsesImageFunctionParameter) { TransformationContext transformation_context( MakeUnique<FactManager>(recipient_context.get()), validator_options); - PseudoRandomGenerator prng(0); - FuzzerContext fuzzer_context(&prng, 100); + FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100, + false); protobufs::TransformationSequence transformation_sequence; FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), @@ -1155,8 +1155,8 @@ TEST(FuzzerPassDonateModulesTest, DonateShaderWithImageStorageClass) { TransformationContext transformation_context( MakeUnique<FactManager>(recipient_context.get()), validator_options); - PseudoRandomGenerator prng(0); - FuzzerContext fuzzer_context(&prng, 100); + FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100, + false); protobufs::TransformationSequence transformation_sequence; FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), @@ -1242,8 +1242,8 @@ TEST(FuzzerPassDonateModulesTest, DonateComputeShaderWithRuntimeArray) { TransformationContext transformation_context( MakeUnique<FactManager>(recipient_context.get()), validator_options); - PseudoRandomGenerator prng(0); - FuzzerContext fuzzer_context(&prng, 100); + FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100, + false); protobufs::TransformationSequence transformation_sequence; FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), @@ -1346,8 +1346,8 @@ TEST(FuzzerPassDonateModulesTest, DonateComputeShaderWithRuntimeArrayLivesafe) { TransformationContext transformation_context( MakeUnique<FactManager>(recipient_context.get()), validator_options); - PseudoRandomGenerator prng(0); - FuzzerContext fuzzer_context(&prng, 100); + FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100, + false); protobufs::TransformationSequence transformation_sequence; FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), @@ -1418,8 +1418,8 @@ TEST(FuzzerPassDonateModulesTest, DonateComputeShaderWithWorkgroupVariables) { TransformationContext transformation_context( MakeUnique<FactManager>(recipient_context.get()), validator_options); - PseudoRandomGenerator prng(0); - FuzzerContext fuzzer_context(&prng, 100); + FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100, + false); protobufs::TransformationSequence transformation_sequence; FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), @@ -1528,8 +1528,8 @@ TEST(FuzzerPassDonateModulesTest, DonateComputeShaderWithAtomics) { TransformationContext transformation_context( MakeUnique<FactManager>(recipient_context.get()), validator_options); - PseudoRandomGenerator prng(0); - FuzzerContext fuzzer_context(&prng, 100); + FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100, + false); protobufs::TransformationSequence transformation_sequence; FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), @@ -1712,8 +1712,8 @@ TEST(FuzzerPassDonateModulesTest, Miscellaneous1) { TransformationContext transformation_context( MakeUnique<FactManager>(recipient_context.get()), validator_options); - PseudoRandomGenerator rng(0); - FuzzerContext fuzzer_context(&rng, 100); + FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100, + false); protobufs::TransformationSequence transformation_sequence; FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), @@ -1784,8 +1784,8 @@ TEST(FuzzerPassDonateModulesTest, OpSpecConstantInstructions) { TransformationContext transformation_context( MakeUnique<FactManager>(recipient_context.get()), validator_options); - PseudoRandomGenerator prng(0); - FuzzerContext fuzzer_context(&prng, 100); + FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100, + false); protobufs::TransformationSequence transformation_sequence; FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), @@ -1941,8 +1941,8 @@ TEST(FuzzerPassDonateModulesTest, DonationSupportsOpTypeRuntimeArray) { TransformationContext transformation_context( MakeUnique<FactManager>(recipient_context.get()), validator_options); - PseudoRandomGenerator rng(0); - FuzzerContext fuzzer_context(&rng, 100); + FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100, + false); protobufs::TransformationSequence transformation_sequence; FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), @@ -2014,8 +2014,8 @@ TEST(FuzzerPassDonateModulesTest, HandlesCapabilities) { TransformationContext transformation_context( MakeUnique<FactManager>(recipient_context.get()), validator_options); - PseudoRandomGenerator rng(0); - FuzzerContext fuzzer_context(&rng, 100); + FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100, + false); protobufs::TransformationSequence transformation_sequence; FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), @@ -2247,8 +2247,8 @@ TEST(FuzzerPassDonateModulesTest, HandlesOpPhisInMergeBlock) { TransformationContext transformation_context( MakeUnique<FactManager>(recipient_context.get()), validator_options); - PseudoRandomGenerator prng(0); - FuzzerContext fuzzer_context(&prng, 100); + FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100, + false); protobufs::TransformationSequence transformation_sequence; FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), diff --git a/test/fuzz/fuzzer_pass_outline_functions_test.cpp b/test/fuzz/fuzzer_pass_outline_functions_test.cpp index 576962c3..0d2c5bf7 100644 --- a/test/fuzz/fuzzer_pass_outline_functions_test.cpp +++ b/test/fuzz/fuzzer_pass_outline_functions_test.cpp @@ -124,8 +124,8 @@ TEST(FuzzerPassOutlineFunctionsTest, EntryIsAlreadySuitable) { kConsoleMessageConsumer)); TransformationContext transformation_context( MakeUnique<FactManager>(context.get()), validator_options); - PseudoRandomGenerator prng(0); - FuzzerContext fuzzer_context(&prng, 100); + FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100, + false); protobufs::TransformationSequence transformation_sequence; FuzzerPassOutlineFunctions fuzzer_pass(context.get(), &transformation_context, @@ -167,8 +167,8 @@ TEST(FuzzerPassOutlineFunctionsTest, EntryHasOpVariable) { kConsoleMessageConsumer)); TransformationContext transformation_context( MakeUnique<FactManager>(context.get()), validator_options); - PseudoRandomGenerator prng(0); - FuzzerContext fuzzer_context(&prng, 100); + FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100, + false); protobufs::TransformationSequence transformation_sequence; FuzzerPassOutlineFunctions fuzzer_pass(context.get(), &transformation_context, @@ -291,8 +291,8 @@ TEST(FuzzerPassOutlineFunctionsTest, EntryBlockIsHeader) { kConsoleMessageConsumer)); TransformationContext transformation_context( MakeUnique<FactManager>(context.get()), validator_options); - PseudoRandomGenerator prng(0); - FuzzerContext fuzzer_context(&prng, 100); + FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100, + false); protobufs::TransformationSequence transformation_sequence; FuzzerPassOutlineFunctions fuzzer_pass(context.get(), &transformation_context, @@ -458,8 +458,8 @@ TEST(FuzzerPassOutlineFunctionsTest, ExitBlock) { kConsoleMessageConsumer)); TransformationContext transformation_context( MakeUnique<FactManager>(context.get()), validator_options); - PseudoRandomGenerator prng(0); - FuzzerContext fuzzer_context(&prng, 100); + FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100, + false); protobufs::TransformationSequence transformation_sequence; FuzzerPassOutlineFunctions fuzzer_pass(context.get(), &transformation_context, diff --git a/test/fuzz/fuzzer_pass_test.cpp b/test/fuzz/fuzzer_pass_test.cpp index 283aa114..b035de74 100644 --- a/test/fuzz/fuzzer_pass_test.cpp +++ b/test/fuzz/fuzzer_pass_test.cpp @@ -87,8 +87,8 @@ TEST(FuzzerPassTest, ForEachInstructionWithInstructionDescriptor) { ASSERT_TRUE(dominator_analysis->IsReachable(5)); ASSERT_FALSE(dominator_analysis->IsReachable(8)); - PseudoRandomGenerator prng(0); - FuzzerContext fuzzer_context(&prng, 100); + FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100, + false); protobufs::TransformationSequence transformations; FuzzerPassMock fuzzer_pass_mock(context.get(), &transformation_context, &fuzzer_context, &transformations); diff --git a/test/fuzz/fuzzer_replayer_test.cpp b/test/fuzz/fuzzer_replayer_test.cpp index dc905741..6dc7ffb1 100644 --- a/test/fuzz/fuzzer_replayer_test.cpp +++ b/test/fuzz/fuzzer_replayer_test.cpp @@ -12,12 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "source/fuzz/fuzzer.h" -#include "source/fuzz/replayer.h" - #include "gtest/gtest.h" +#include "source/fuzz/fuzzer.h" #include "source/fuzz/fuzzer_util.h" #include "source/fuzz/pseudo_random_generator.h" +#include "source/fuzz/replayer.h" #include "source/fuzz/uniform_buffer_element_descriptor.h" #include "test/fuzz/fuzz_test_util.h" @@ -1642,37 +1641,53 @@ void RunFuzzerAndReplayer(const std::string& shader, }); } - std::vector<Fuzzer::RepeatedPassStrategy> strategies{ - Fuzzer::RepeatedPassStrategy::kSimple, - Fuzzer::RepeatedPassStrategy::kLoopedWithRecommendations, - Fuzzer::RepeatedPassStrategy::kRandomWithRecommendations}; + std::vector<RepeatedPassStrategy> strategies{ + RepeatedPassStrategy::kSimple, + RepeatedPassStrategy::kLoopedWithRecommendations, + RepeatedPassStrategy::kRandomWithRecommendations}; uint32_t strategy_index = 0; for (uint32_t seed = initial_seed; seed < initial_seed + num_runs; seed++) { spvtools::ValidatorOptions validator_options; + + std::unique_ptr<opt::IRContext> ir_context; + ASSERT_TRUE(fuzzerutil::BuildIRContext(env, kConsoleMessageConsumer, + binary_in, validator_options, + &ir_context)); + + auto fuzzer_context = MakeUnique<FuzzerContext>( + MakeUnique<PseudoRandomGenerator>(seed), + FuzzerContext::GetMinFreshId(ir_context.get()), false); + + auto transformation_context = MakeUnique<TransformationContext>( + MakeUnique<FactManager>(ir_context.get()), validator_options); + transformation_context->GetFactManager()->AddInitialFacts( + kConsoleMessageConsumer, initial_facts); + // Every 4th time we run the fuzzer, enable all fuzzer passes. bool enable_all_passes = (seed % 4) == 0; - auto fuzzer_result = - Fuzzer(env, kConsoleMessageConsumer, binary_in, initial_facts, - donor_suppliers, MakeUnique<PseudoRandomGenerator>(seed), - enable_all_passes, strategies[strategy_index], true, - validator_options) - .Run(); + Fuzzer fuzzer(std::move(ir_context), std::move(transformation_context), + std::move(fuzzer_context), kConsoleMessageConsumer, + donor_suppliers, enable_all_passes, + strategies[strategy_index], true, validator_options); + auto fuzzer_result = fuzzer.Run(0); // Cycle the repeated pass strategy so that we try a different one next time // we run the fuzzer. strategy_index = (strategy_index + 1) % static_cast<uint32_t>(strategies.size()); - ASSERT_EQ(Fuzzer::FuzzerResultStatus::kComplete, fuzzer_result.status); - ASSERT_TRUE(t.Validate(fuzzer_result.transformed_binary)); + ASSERT_NE(Fuzzer::Status::kFuzzerPassLedToInvalidModule, + fuzzer_result.status); + std::vector<uint32_t> transformed_binary; + fuzzer.GetIRContext()->module()->ToBinary(&transformed_binary, true); + ASSERT_TRUE(t.Validate(transformed_binary)); auto replayer_result = - Replayer( - env, kConsoleMessageConsumer, binary_in, initial_facts, - fuzzer_result.applied_transformations, - static_cast<uint32_t>( - fuzzer_result.applied_transformations.transformation_size()), - false, validator_options) + Replayer(env, kConsoleMessageConsumer, binary_in, initial_facts, + fuzzer.GetTransformationSequence(), + static_cast<uint32_t>( + fuzzer.GetTransformationSequence().transformation_size()), + false, validator_options) .Run(); ASSERT_EQ(Replayer::ReplayerResultStatus::kComplete, replayer_result.status); @@ -1682,12 +1697,12 @@ void RunFuzzerAndReplayer(const std::string& shader, // replay should be identical to that which resulted from fuzzing. std::string fuzzer_transformations_string; std::string replayer_transformations_string; - fuzzer_result.applied_transformations.SerializeToString( + fuzzer.GetTransformationSequence().SerializeToString( &fuzzer_transformations_string); replayer_result.applied_transformations.SerializeToString( &replayer_transformations_string); ASSERT_EQ(fuzzer_transformations_string, replayer_transformations_string); - ASSERT_TRUE(IsEqual(env, fuzzer_result.transformed_binary, + ASSERT_TRUE(IsEqual(env, transformed_binary, replayer_result.transformed_module.get())); } } diff --git a/test/fuzz/fuzzer_shrinker_test.cpp b/test/fuzz/fuzzer_shrinker_test.cpp index 6d9dad35..e7921169 100644 --- a/test/fuzz/fuzzer_shrinker_test.cpp +++ b/test/fuzz/fuzzer_shrinker_test.cpp @@ -12,15 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "source/fuzz/fuzzer.h" -#include "source/fuzz/shrinker.h" - #include <functional> #include <vector> #include "gtest/gtest.h" +#include "source/fuzz/fuzzer.h" #include "source/fuzz/fuzzer_util.h" #include "source/fuzz/pseudo_random_generator.h" +#include "source/fuzz/shrinker.h" #include "source/fuzz/uniform_buffer_element_descriptor.h" #include "test/fuzz/fuzz_test_util.h" @@ -1044,24 +1043,38 @@ void RunFuzzerAndShrinker(const std::string& shader, // Depending on the seed, decide whether to enable all passes and which // repeated pass manager to use. bool enable_all_passes = (seed % 4) == 0; - Fuzzer::RepeatedPassStrategy repeated_pass_strategy; + RepeatedPassStrategy repeated_pass_strategy; if ((seed % 3) == 0) { - repeated_pass_strategy = Fuzzer::RepeatedPassStrategy::kSimple; + repeated_pass_strategy = RepeatedPassStrategy::kSimple; } else if ((seed % 3) == 1) { - repeated_pass_strategy = - Fuzzer::RepeatedPassStrategy::kLoopedWithRecommendations; + repeated_pass_strategy = RepeatedPassStrategy::kLoopedWithRecommendations; } else { - repeated_pass_strategy = - Fuzzer::RepeatedPassStrategy::kRandomWithRecommendations; + repeated_pass_strategy = RepeatedPassStrategy::kRandomWithRecommendations; } - auto fuzzer_result = - Fuzzer(env, kConsoleMessageConsumer, binary_in, initial_facts, - donor_suppliers, MakeUnique<PseudoRandomGenerator>(seed), - enable_all_passes, repeated_pass_strategy, true, validator_options) - .Run(); - ASSERT_EQ(Fuzzer::FuzzerResultStatus::kComplete, fuzzer_result.status); - ASSERT_TRUE(t.Validate(fuzzer_result.transformed_binary)); + std::unique_ptr<opt::IRContext> ir_context; + ASSERT_TRUE(fuzzerutil::BuildIRContext( + env, kConsoleMessageConsumer, binary_in, validator_options, &ir_context)); + + auto fuzzer_context = MakeUnique<FuzzerContext>( + MakeUnique<PseudoRandomGenerator>(seed), + FuzzerContext::GetMinFreshId(ir_context.get()), false); + + auto transformation_context = MakeUnique<TransformationContext>( + MakeUnique<FactManager>(ir_context.get()), validator_options); + transformation_context->GetFactManager()->AddInitialFacts( + kConsoleMessageConsumer, initial_facts); + + Fuzzer fuzzer(std::move(ir_context), std::move(transformation_context), + std::move(fuzzer_context), kConsoleMessageConsumer, + donor_suppliers, enable_all_passes, repeated_pass_strategy, + true, validator_options); + auto fuzzer_result = fuzzer.Run(0); + ASSERT_NE(Fuzzer::Status::kFuzzerPassLedToInvalidModule, + fuzzer_result.status); + std::vector<uint32_t> transformed_binary; + fuzzer.GetIRContext()->module()->ToBinary(&transformed_binary, true); + ASSERT_TRUE(t.Validate(transformed_binary)); const uint32_t kReasonableStepLimit = 50; const uint32_t kSmallStepLimit = 20; @@ -1069,30 +1082,30 @@ void RunFuzzerAndShrinker(const std::string& shader, // With the AlwaysInteresting test, we should quickly shrink to the original // binary with no transformations remaining. RunAndCheckShrinker(env, binary_in, initial_facts, - fuzzer_result.applied_transformations, + fuzzer.GetTransformationSequence(), AlwaysInteresting().AsFunction(), binary_in, 0, kReasonableStepLimit, validator_options); // With the OnlyInterestingFirstTime test, no shrinking should be achieved. RunAndCheckShrinker( - env, binary_in, initial_facts, fuzzer_result.applied_transformations, - OnlyInterestingFirstTime().AsFunction(), fuzzer_result.transformed_binary, + env, binary_in, initial_facts, fuzzer.GetTransformationSequence(), + OnlyInterestingFirstTime().AsFunction(), transformed_binary, static_cast<uint32_t>( - fuzzer_result.applied_transformations.transformation_size()), + fuzzer.GetTransformationSequence().transformation_size()), kReasonableStepLimit, validator_options); // The PingPong test is unpredictable; passing an empty expected binary // means that we don't check anything beyond that shrinking completes // successfully. RunAndCheckShrinker( - env, binary_in, initial_facts, fuzzer_result.applied_transformations, + env, binary_in, initial_facts, fuzzer.GetTransformationSequence(), PingPong().AsFunction(), {}, 0, kSmallStepLimit, validator_options); // The InterestingThenRandom test is unpredictable; passing an empty // expected binary means that we do not check anything about shrinking // results. RunAndCheckShrinker( - env, binary_in, initial_facts, fuzzer_result.applied_transformations, + env, binary_in, initial_facts, fuzzer.GetTransformationSequence(), InterestingThenRandom(PseudoRandomGenerator(seed)).AsFunction(), {}, 0, kSmallStepLimit, validator_options); } diff --git a/test/fuzz/fuzzerutil_test.cpp b/test/fuzz/fuzzerutil_test.cpp new file mode 100644 index 00000000..7ff9b4d8 --- /dev/null +++ b/test/fuzz/fuzzerutil_test.cpp @@ -0,0 +1,1568 @@ +// Copyright (c) 2021 Shiyu Liu +// +// 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 "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(FuzzerUtilMaybeFindBlockTest, BasicTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %8 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %10 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpBranch %11 + %11 = OpLabel + OpStore %8 %9 + OpBranch %12 + %12 = OpLabel + OpStore %8 %10 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const std::unique_ptr<opt::IRContext> context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Only blocks with id 11 and 12 can be found. + // Should return nullptr when id is not a label or id was not found. + uint32_t block_id1 = 11; + uint32_t block_id2 = 12; + uint32_t block_id3 = 13; + uint32_t block_id4 = 8; + + opt::IRContext* ir_context = context.get(); + // Block with id 11 should be found. + ASSERT_TRUE(fuzzerutil::MaybeFindBlock(ir_context, block_id1) != nullptr); + // Block with id 12 should be found. + 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. + ASSERT_FALSE(fuzzerutil::MaybeFindBlock(ir_context, block_id4) != nullptr); +} + +TEST(FuzzerutilTest, FuzzerUtilMaybeGetBoolConstantTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %36 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "b1" + OpName %10 "b2" + OpName %12 "b3" + OpName %13 "b4" + OpName %16 "f1" + OpName %18 "f2" + OpName %20 "cf1" + OpName %22 "cf2" + OpName %26 "i1" + OpName %28 "i2" + OpName %30 "ci1" + OpName %32 "ci2" + OpName %36 "value" + OpDecorate %26 RelaxedPrecision + OpDecorate %28 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %32 RelaxedPrecision + OpDecorate %36 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpTypePointer Function %6 + %9 = OpConstantTrue %6 + %11 = OpConstantFalse %6 + %14 = OpTypeFloat 32 + %15 = OpTypePointer Function %14 + %17 = OpConstant %14 1.23000002 + %19 = OpConstant %14 1.11000001 + %21 = OpConstant %14 2 + %23 = OpConstant %14 3.29999995 + %24 = OpTypeInt 32 1 + %25 = OpTypePointer Function %24 + %27 = OpConstant %24 1 + %29 = OpConstant %24 100 + %31 = OpConstant %24 123 + %33 = OpConstant %24 1111 + %35 = OpTypePointer Input %14 + %36 = OpVariable %35 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %12 = OpVariable %7 Function + %13 = OpVariable %7 Function + %16 = OpVariable %15 Function + %18 = OpVariable %15 Function + %20 = OpVariable %15 Function + %22 = OpVariable %15 Function + %26 = OpVariable %25 Function + %28 = OpVariable %25 Function + %30 = OpVariable %25 Function + %32 = OpVariable %25 Function + OpStore %8 %9 + OpStore %10 %11 + OpStore %12 %9 + OpStore %13 %11 + OpStore %16 %17 + OpStore %18 %19 + OpStore %20 %21 + OpStore %22 %23 + OpStore %26 %27 + OpStore %28 %29 + OpStore %30 %31 + OpStore %32 %33 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const std::unique_ptr<opt::IRContext> context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique<FactManager>(context.get()), validator_options); + + opt::IRContext* ir_context = context.get(); + // A bool constant with value false exists and the id is 11. + ASSERT_EQ(11, fuzzerutil::MaybeGetBoolConstant( + ir_context, transformation_context, false, false)); + // A bool constant with value true exists and the id is 9. + ASSERT_EQ(9, fuzzerutil::MaybeGetBoolConstant( + ir_context, transformation_context, true, false)); +} + +TEST(FuzzerutilTest, FuzzerUtilMaybeGetBoolTypeTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %92 %52 %53 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %92 BuiltIn FragCoord + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeFloat 32 + %8 = OpTypeStruct %6 %7 + %9 = OpTypePointer Function %8 + %10 = OpTypeFunction %6 %9 + %14 = OpConstant %6 0 + %15 = OpTypePointer Function %6 + %51 = OpTypePointer Private %6 + %21 = OpConstant %6 2 + %23 = OpConstant %6 1 + %24 = OpConstant %7 1 + %25 = OpTypePointer Function %7 + %50 = OpTypePointer Private %7 + %34 = OpTypeBool + %35 = OpConstantFalse %34 + %60 = OpConstantNull %50 + %61 = OpUndef %51 + %52 = OpVariable %50 Private + %53 = OpVariable %51 Private + %80 = OpConstantComposite %8 %21 %24 + %90 = OpTypeVector %7 4 + %91 = OpTypePointer Input %90 + %92 = OpVariable %91 Input + %93 = OpConstantComposite %90 %24 %24 %24 %24 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %20 = OpVariable %9 Function + %27 = OpVariable %9 Function + %22 = OpAccessChain %15 %20 %14 + %44 = OpCopyObject %9 %20 + %26 = OpAccessChain %25 %20 %23 + %29 = OpFunctionCall %6 %12 %27 + %30 = OpAccessChain %15 %20 %14 + %45 = OpCopyObject %15 %30 + %81 = OpCopyObject %9 %27 + %33 = OpAccessChain %15 %20 %14 + OpSelectionMerge %37 None + OpBranchConditional %35 %36 %37 + %36 = OpLabel + %38 = OpAccessChain %15 %20 %14 + %40 = OpAccessChain %15 %20 %14 + %43 = OpAccessChain %15 %20 %14 + %82 = OpCopyObject %9 %27 + OpBranch %37 + %37 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %6 None %10 + %11 = OpFunctionParameter %9 + %13 = OpLabel + %46 = OpCopyObject %9 %11 + %16 = OpAccessChain %15 %11 %14 + %95 = OpCopyObject %8 %80 + OpReturnValue %21 + %100 = OpLabel + OpUnreachable + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const std::unique_ptr<opt::IRContext> context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + opt::IRContext* ir_context = context.get(); + // A bool type with result id of 34 exists. + ASSERT_TRUE(fuzzerutil::MaybeGetBoolType(ir_context)); +} + +TEST(FuzzerutilTest, FuzzerUtilMaybeGetCompositeConstantTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %54 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "b1" + OpName %10 "b2" + OpName %12 "b3" + OpName %13 "b4" + OpName %16 "f1" + OpName %18 "f2" + OpName %22 "zc" + OpName %24 "i1" + OpName %28 "i2" + OpName %30 "i3" + OpName %32 "i4" + OpName %37 "f_arr" + OpName %47 "i_arr" + OpName %54 "value" + OpDecorate %22 RelaxedPrecision + OpDecorate %24 RelaxedPrecision + OpDecorate %28 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %32 RelaxedPrecision + OpDecorate %47 RelaxedPrecision + OpDecorate %54 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpTypePointer Function %6 + %9 = OpConstantTrue %6 + %11 = OpConstantFalse %6 + %14 = OpTypeFloat 32 + %15 = OpTypePointer Function %14 + %17 = OpConstant %14 1.23000002 + %19 = OpConstant %14 1.11000001 + %20 = OpTypeInt 32 1 + %21 = OpTypePointer Function %20 + %23 = OpConstant %20 0 + %25 = OpConstant %20 1 + %26 = OpTypeInt 32 0 + %27 = OpTypePointer Function %26 + %29 = OpConstant %26 100 + %31 = OpConstant %20 -1 + %33 = OpConstant %20 -99 + %34 = OpConstant %26 5 + %35 = OpTypeArray %14 %34 + %36 = OpTypePointer Function %35 + %38 = OpConstant %14 5.5 + %39 = OpConstant %14 4.4000001 + %40 = OpConstant %14 3.29999995 + %41 = OpConstant %14 2.20000005 + %42 = OpConstant %14 1.10000002 + %43 = OpConstantComposite %35 %38 %39 %40 %41 %42 + %44 = OpConstant %26 3 + %45 = OpTypeArray %20 %44 + %46 = OpTypePointer Function %45 + %48 = OpConstant %20 3 + %49 = OpConstant %20 7 + %50 = OpConstant %20 9 + %51 = OpConstantComposite %45 %48 %49 %50 + %53 = OpTypePointer Input %14 + %54 = OpVariable %53 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %12 = OpVariable %7 Function + %13 = OpVariable %7 Function + %16 = OpVariable %15 Function + %18 = OpVariable %15 Function + %22 = OpVariable %21 Function + %24 = OpVariable %21 Function + %28 = OpVariable %27 Function + %30 = OpVariable %21 Function + %32 = OpVariable %21 Function + %37 = OpVariable %36 Function + %47 = OpVariable %46 Function + OpStore %8 %9 + OpStore %10 %11 + OpStore %12 %9 + OpStore %13 %11 + OpStore %16 %17 + OpStore %18 %19 + OpStore %22 %23 + OpStore %24 %25 + OpStore %28 %29 + OpStore %30 %31 + OpStore %32 %33 + OpStore %37 %43 + OpStore %47 %51 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const std::unique_ptr<opt::IRContext> context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique<FactManager>(context.get()), validator_options); + + opt::IRContext* ir_context = context.get(); + + // %43 = OpConstantComposite %35 %38 %39 %40 %41 %42 + // %51 = OpConstantComposite %45 %48 %49 %50 + // This should pass as a float array with 5 elements exist and its id is 43. + ASSERT_EQ(43, fuzzerutil::MaybeGetCompositeConstant( + ir_context, transformation_context, {38, 39, 40, 41, 42}, + 35, false)); + // This should pass as an int array with 3 elements exist and its id is 51. + ASSERT_EQ(51, + fuzzerutil::MaybeGetCompositeConstant( + ir_context, transformation_context, {48, 49, 50}, 45, false)); + // An int array with 2 elements does not exist. + ASSERT_EQ(0, fuzzerutil::MaybeGetCompositeConstant( + ir_context, transformation_context, {48, 49}, 45, false)); +} + +TEST(FuzzerutilTest, FuzzerUtilMaybeGetFloatConstantTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %36 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "b1" + OpName %10 "b2" + OpName %12 "b3" + OpName %13 "b4" + OpName %16 "f1" + OpName %18 "f2" + OpName %20 "cf1" + OpName %22 "cf2" + OpName %26 "i1" + OpName %28 "i2" + OpName %30 "ci1" + OpName %32 "ci2" + OpName %36 "value" + OpDecorate %26 RelaxedPrecision + OpDecorate %28 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %32 RelaxedPrecision + OpDecorate %36 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpTypePointer Function %6 + %9 = OpConstantTrue %6 + %11 = OpConstantFalse %6 + %14 = OpTypeFloat 32 + %15 = OpTypePointer Function %14 + %17 = OpConstant %14 1.23000002 + %19 = OpConstant %14 1.11000001 + %21 = OpConstant %14 2 + %23 = OpConstant %14 3.29999995 + %24 = OpTypeInt 32 1 + %25 = OpTypePointer Function %24 + %27 = OpConstant %24 1 + %29 = OpConstant %24 100 + %31 = OpConstant %24 123 + %33 = OpConstant %24 1111 + %35 = OpTypePointer Input %14 + %36 = OpVariable %35 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %12 = OpVariable %7 Function + %13 = OpVariable %7 Function + %16 = OpVariable %15 Function + %18 = OpVariable %15 Function + %20 = OpVariable %15 Function + %22 = OpVariable %15 Function + %26 = OpVariable %25 Function + %28 = OpVariable %25 Function + %30 = OpVariable %25 Function + %32 = OpVariable %25 Function + OpStore %8 %9 + OpStore %10 %11 + OpStore %12 %9 + OpStore %13 %11 + OpStore %16 %17 + OpStore %18 %19 + OpStore %20 %21 + OpStore %22 %23 + OpStore %26 %27 + OpStore %28 %29 + OpStore %30 %31 + OpStore %32 %33 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const std::unique_ptr<opt::IRContext> context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique<FactManager>(context.get()), validator_options); + + opt::IRContext* ir_context = context.get(); + + uint32_t word1 = fuzzerutil::FloatToWord(2); + uint32_t word2 = fuzzerutil::FloatToWord(1.23f); + + // A 32 bit float constant of value 2 exists and its id is 21. + ASSERT_EQ(21, fuzzerutil::MaybeGetFloatConstant( + ir_context, transformation_context, + std::vector<uint32_t>{word1}, 32, false)); + // A 32 bit float constant of value 1.23 exists and its id is 17. + ASSERT_EQ(17, fuzzerutil::MaybeGetFloatConstant( + ir_context, transformation_context, + std::vector<uint32_t>{word2}, 32, false)); +} + +TEST(FuzzerutilTest, FuzzerUtilMaybeGetFloatTypeTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %92 %52 %53 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %92 BuiltIn FragCoord + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeFloat 32 + %8 = OpTypeStruct %6 %7 + %9 = OpTypePointer Function %8 + %10 = OpTypeFunction %6 %9 + %14 = OpConstant %6 0 + %15 = OpTypePointer Function %6 + %51 = OpTypePointer Private %6 + %21 = OpConstant %6 2 + %23 = OpConstant %6 1 + %24 = OpConstant %7 1 + %25 = OpTypePointer Function %7 + %50 = OpTypePointer Private %7 + %34 = OpTypeBool + %35 = OpConstantFalse %34 + %60 = OpConstantNull %50 + %61 = OpUndef %51 + %52 = OpVariable %50 Private + %53 = OpVariable %51 Private + %80 = OpConstantComposite %8 %21 %24 + %90 = OpTypeVector %7 4 + %91 = OpTypePointer Input %90 + %92 = OpVariable %91 Input + %93 = OpConstantComposite %90 %24 %24 %24 %24 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %20 = OpVariable %9 Function + %27 = OpVariable %9 Function + %22 = OpAccessChain %15 %20 %14 + %44 = OpCopyObject %9 %20 + %26 = OpAccessChain %25 %20 %23 + %29 = OpFunctionCall %6 %12 %27 + %30 = OpAccessChain %15 %20 %14 + %45 = OpCopyObject %15 %30 + %81 = OpCopyObject %9 %27 + %33 = OpAccessChain %15 %20 %14 + OpSelectionMerge %37 None + OpBranchConditional %35 %36 %37 + %36 = OpLabel + %38 = OpAccessChain %15 %20 %14 + %40 = OpAccessChain %15 %20 %14 + %43 = OpAccessChain %15 %20 %14 + %82 = OpCopyObject %9 %27 + OpBranch %37 + %37 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %6 None %10 + %11 = OpFunctionParameter %9 + %13 = OpLabel + %46 = OpCopyObject %9 %11 + %16 = OpAccessChain %15 %11 %14 + %95 = OpCopyObject %8 %80 + OpReturnValue %21 + %100 = OpLabel + OpUnreachable + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const std::unique_ptr<opt::IRContext> context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + opt::IRContext* ir_context = context.get(); + // A float type with width = 32 and result id of 7 exists. + ASSERT_EQ(7, fuzzerutil::MaybeGetFloatType(ir_context, 32)); + + // A float int type with width = 32 exists, but the id should be 7. + ASSERT_NE(5, fuzzerutil::MaybeGetFloatType(ir_context, 32)); + + // A float type with width 30 does not exist. + ASSERT_EQ(0, fuzzerutil::MaybeGetFloatType(ir_context, 30)); +} + +TEST(FuzzerutilTest, FuzzerUtilMaybeGetIntegerConstantFromValueAndTypeTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %36 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "b1" + OpName %10 "b2" + OpName %12 "b3" + OpName %13 "b4" + OpName %16 "f1" + OpName %18 "f2" + OpName %22 "zc" + OpName %24 "i1" + OpName %28 "i2" + OpName %30 "i3" + OpName %32 "i4" + OpName %36 "value" + OpDecorate %22 RelaxedPrecision + OpDecorate %24 RelaxedPrecision + OpDecorate %28 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %32 RelaxedPrecision + OpDecorate %36 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpTypePointer Function %6 + %9 = OpConstantTrue %6 + %11 = OpConstantFalse %6 + %14 = OpTypeFloat 32 + %15 = OpTypePointer Function %14 + %17 = OpConstant %14 1.23000002 + %19 = OpConstant %14 1.11000001 + %20 = OpTypeInt 32 1 + %21 = OpTypePointer Function %20 + %23 = OpConstant %20 0 + %25 = OpConstant %20 1 + %26 = OpTypeInt 32 0 + %27 = OpTypePointer Function %26 + %29 = OpConstant %26 100 + %31 = OpConstant %20 -1 + %33 = OpConstant %20 -99 + %35 = OpTypePointer Input %14 + %36 = OpVariable %35 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %12 = OpVariable %7 Function + %13 = OpVariable %7 Function + %16 = OpVariable %15 Function + %18 = OpVariable %15 Function + %22 = OpVariable %21 Function + %24 = OpVariable %21 Function + %28 = OpVariable %27 Function + %30 = OpVariable %21 Function + %32 = OpVariable %21 Function + OpStore %8 %9 + OpStore %10 %11 + OpStore %12 %9 + OpStore %13 %11 + OpStore %16 %17 + OpStore %18 %19 + OpStore %22 %23 + OpStore %24 %25 + OpStore %28 %29 + OpStore %30 %31 + OpStore %32 %33 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const std::unique_ptr<opt::IRContext> context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + opt::IRContext* ir_context = context.get(); + + // A 32 bit signed int constant (with int type id 20) with value 1 exists and + // the id is 25. + ASSERT_EQ(25, fuzzerutil::MaybeGetIntegerConstantFromValueAndType(ir_context, + 1, 20)); + // A 32 bit unsigned int constant (with int type id 0) with value 100 exists + // and the id is 29. + ASSERT_EQ(29, fuzzerutil::MaybeGetIntegerConstantFromValueAndType(ir_context, + 100, 26)); + // A 32 bit unsigned int constant with value 50 does not exist. + ASSERT_EQ(0, fuzzerutil::MaybeGetIntegerConstantFromValueAndType(ir_context, + 50, 26)); +} + +TEST(FuzzerutilTest, FuzzerUtilMaybeGetIntegerConstantTest) { + std::string shader = R"( +OpCapability Shader + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %36 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "b1" + OpName %10 "b2" + OpName %12 "b3" + OpName %13 "b4" + OpName %16 "f1" + OpName %18 "f2" + OpName %22 "zc" + OpName %24 "i1" + OpName %28 "i2" + OpName %30 "i3" + OpName %32 "i4" + OpName %36 "value" + OpDecorate %22 RelaxedPrecision + OpDecorate %24 RelaxedPrecision + OpDecorate %28 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %32 RelaxedPrecision + OpDecorate %36 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpTypePointer Function %6 + %9 = OpConstantTrue %6 + %11 = OpConstantFalse %6 + %14 = OpTypeFloat 32 + %15 = OpTypePointer Function %14 + %17 = OpConstant %14 1.23000002 + %19 = OpConstant %14 1.11000001 + %20 = OpTypeInt 32 1 + %21 = OpTypePointer Function %20 + %23 = OpConstant %20 0 + %25 = OpConstant %20 1 + %26 = OpTypeInt 32 0 + %27 = OpTypePointer Function %26 + %29 = OpConstant %26 100 + %31 = OpConstant %20 -1 + %33 = OpConstant %20 -99 + %35 = OpTypePointer Input %14 + %36 = OpVariable %35 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %12 = OpVariable %7 Function + %13 = OpVariable %7 Function + %16 = OpVariable %15 Function + %18 = OpVariable %15 Function + %22 = OpVariable %21 Function + %24 = OpVariable %21 Function + %28 = OpVariable %27 Function + %30 = OpVariable %21 Function + %32 = OpVariable %21 Function + OpStore %8 %9 + OpStore %10 %11 + OpStore %12 %9 + OpStore %13 %11 + OpStore %16 %17 + OpStore %18 %19 + OpStore %22 %23 + OpStore %24 %25 + OpStore %28 %29 + OpStore %30 %31 + OpStore %32 %33 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const std::unique_ptr<opt::IRContext> context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique<FactManager>(context.get()), validator_options); + + opt::IRContext* ir_context = context.get(); + + // A 32 bit unsigned int constant with value 1 exists and the id is 25. + ASSERT_EQ(25, fuzzerutil::MaybeGetIntegerConstant( + ir_context, transformation_context, + std::vector<uint32_t>{1}, 32, true, false)); + // A 32 bit unsigned int constant with value 100 exists and the id is 29. + ASSERT_EQ(29, fuzzerutil::MaybeGetIntegerConstant( + ir_context, transformation_context, + std::vector<uint32_t>{100}, 32, false, false)); + // A 32 bit signed int constant with value 99 doesn't not exist and should + // return 0. + ASSERT_EQ(0, fuzzerutil::MaybeGetIntegerConstant( + ir_context, transformation_context, + std::vector<uint32_t>{99}, 32, true, false)); +} + +TEST(FuzzerutilTest, FuzzerUtilMaybeGetIntegerTypeTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %92 %52 %53 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %92 BuiltIn FragCoord + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeFloat 32 + %8 = OpTypeStruct %6 %7 + %9 = OpTypePointer Function %8 + %10 = OpTypeFunction %6 %9 + %14 = OpConstant %6 0 + %15 = OpTypePointer Function %6 + %51 = OpTypePointer Private %6 + %21 = OpConstant %6 2 + %23 = OpConstant %6 1 + %24 = OpConstant %7 1 + %25 = OpTypePointer Function %7 + %50 = OpTypePointer Private %7 + %34 = OpTypeBool + %35 = OpConstantFalse %34 + %60 = OpConstantNull %50 + %61 = OpUndef %51 + %52 = OpVariable %50 Private + %53 = OpVariable %51 Private + %80 = OpConstantComposite %8 %21 %24 + %90 = OpTypeVector %7 4 + %91 = OpTypePointer Input %90 + %92 = OpVariable %91 Input + %93 = OpConstantComposite %90 %24 %24 %24 %24 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %20 = OpVariable %9 Function + %27 = OpVariable %9 Function + %22 = OpAccessChain %15 %20 %14 + %44 = OpCopyObject %9 %20 + %26 = OpAccessChain %25 %20 %23 + %29 = OpFunctionCall %6 %12 %27 + %30 = OpAccessChain %15 %20 %14 + %45 = OpCopyObject %15 %30 + %81 = OpCopyObject %9 %27 + %33 = OpAccessChain %15 %20 %14 + OpSelectionMerge %37 None + OpBranchConditional %35 %36 %37 + %36 = OpLabel + %38 = OpAccessChain %15 %20 %14 + %40 = OpAccessChain %15 %20 %14 + %43 = OpAccessChain %15 %20 %14 + %82 = OpCopyObject %9 %27 + OpBranch %37 + %37 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %6 None %10 + %11 = OpFunctionParameter %9 + %13 = OpLabel + %46 = OpCopyObject %9 %11 + %16 = OpAccessChain %15 %11 %14 + %95 = OpCopyObject %8 %80 + OpReturnValue %21 + %100 = OpLabel + OpUnreachable + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const std::unique_ptr<opt::IRContext> context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + opt::IRContext* ir_context = context.get(); + + // A signed int type with width = 32 and result id of 6 exists. + ASSERT_EQ(6, fuzzerutil::MaybeGetIntegerType(ir_context, 32, true)); + + // A signed int type with width = 32 exists, but the id should be 6. + ASSERT_FALSE(fuzzerutil::MaybeGetIntegerType(ir_context, 32, true) == 5); + + // A int type with width = 32 and result id of 6 exists, but it should be a + // signed int. + ASSERT_EQ(0, fuzzerutil::MaybeGetIntegerType(ir_context, 32, false)); + // A signed int type with width 30 does not exist. + ASSERT_EQ(0, fuzzerutil::MaybeGetIntegerType(ir_context, 30, true)); + // An unsigned int type with width 22 does not exist. + ASSERT_EQ(0, fuzzerutil::MaybeGetIntegerType(ir_context, 22, false)); +} + +TEST(FuzzerutilTest, FuzzerUtilMaybeGetPointerTypeTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %92 %52 %53 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %92 BuiltIn FragCoord + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeFloat 32 + %8 = OpTypeStruct %6 %7 + %9 = OpTypePointer Function %8 + %10 = OpTypeFunction %6 %9 + %14 = OpConstant %6 0 + %15 = OpTypePointer Function %6 + %51 = OpTypePointer Private %6 + %21 = OpConstant %6 2 + %23 = OpConstant %6 1 + %24 = OpConstant %7 1 + %25 = OpTypePointer Function %7 + %50 = OpTypePointer Private %7 + %34 = OpTypeBool + %35 = OpConstantFalse %34 + %60 = OpConstantNull %50 + %61 = OpUndef %51 + %52 = OpVariable %50 Private + %53 = OpVariable %51 Private + %80 = OpConstantComposite %8 %21 %24 + %90 = OpTypeVector %7 4 + %91 = OpTypePointer Input %90 + %92 = OpVariable %91 Input + %93 = OpConstantComposite %90 %24 %24 %24 %24 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %20 = OpVariable %9 Function + %27 = OpVariable %9 Function + %22 = OpAccessChain %15 %20 %14 + %44 = OpCopyObject %9 %20 + %26 = OpAccessChain %25 %20 %23 + %29 = OpFunctionCall %6 %12 %27 + %30 = OpAccessChain %15 %20 %14 + %45 = OpCopyObject %15 %30 + %81 = OpCopyObject %9 %27 + %33 = OpAccessChain %15 %20 %14 + OpSelectionMerge %37 None + OpBranchConditional %35 %36 %37 + %36 = OpLabel + %38 = OpAccessChain %15 %20 %14 + %40 = OpAccessChain %15 %20 %14 + %43 = OpAccessChain %15 %20 %14 + %82 = OpCopyObject %9 %27 + OpBranch %37 + %37 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %6 None %10 + %11 = OpFunctionParameter %9 + %13 = OpLabel + %46 = OpCopyObject %9 %11 + %16 = OpAccessChain %15 %11 %14 + %95 = OpCopyObject %8 %80 + OpReturnValue %21 + %100 = OpLabel + OpUnreachable + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const std::unique_ptr<opt::IRContext> context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + opt::IRContext* ir_context = context.get(); + auto private_storage_class = SpvStorageClassPrivate; + auto function_storage_class = SpvStorageClassFunction; + auto input_storage_class = SpvStorageClassInput; + + // A valid pointer must have the correct |pointee_type_id| and |storageClass|. + // A function type pointer with id = 9 and pointee type id 8 should be found. + ASSERT_EQ(9, fuzzerutil::MaybeGetPointerType(ir_context, 8, + function_storage_class)); + // A function type pointer with id = 15 and pointee type id 6 should be found. + ASSERT_EQ(15, fuzzerutil::MaybeGetPointerType(ir_context, 6, + function_storage_class)); + // A function type pointer with id = 25 and pointee type id 7 should be found. + ASSERT_EQ(25, fuzzerutil::MaybeGetPointerType(ir_context, 7, + function_storage_class)); + + // A private type pointer with id=51 and pointee type id 6 should be found. + ASSERT_EQ(51, fuzzerutil::MaybeGetPointerType(ir_context, 6, + private_storage_class)); + // A function pointer with id=50 and pointee type id 7 should be found. + ASSERT_EQ(50, fuzzerutil::MaybeGetPointerType(ir_context, 7, + private_storage_class)); + + // A input type pointer with id=91 and pointee type id 90 should be found. + 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 + // input. + ASSERT_EQ(0, fuzzerutil::MaybeGetPointerType(ir_context, 90, + function_storage_class)); + // A input type pointer with id=91 exists but the pointee id should be 90. + ASSERT_EQ( + 0, fuzzerutil::MaybeGetPointerType(ir_context, 89, input_storage_class)); + // A input type pointer with pointee id 90 exists but result id of the pointer + // should be 91. + ASSERT_NE( + 58, fuzzerutil::MaybeGetPointerType(ir_context, 90, input_storage_class)); +} + +TEST(FuzzerutilTest, FuzzerUtilMaybeGetScalarConstantTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %56 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "b1" + OpName %10 "b2" + OpName %12 "b3" + OpName %13 "b4" + OpName %16 "f1" + OpName %18 "f2" + OpName %22 "zc" + OpName %24 "i1" + OpName %28 "i2" + OpName %30 "i" + OpName %32 "i3" + OpName %34 "i4" + OpName %39 "f_arr" + OpName %49 "i_arr" + OpName %56 "value" + OpDecorate %22 RelaxedPrecision + OpDecorate %24 RelaxedPrecision + OpDecorate %28 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %32 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %49 RelaxedPrecision + OpDecorate %56 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpTypePointer Function %6 + %9 = OpConstantTrue %6 + %11 = OpConstantFalse %6 + %14 = OpTypeFloat 32 + %15 = OpTypePointer Function %14 + %17 = OpConstant %14 1.23000002 + %19 = OpConstant %14 1.11000001 + %20 = OpTypeInt 32 1 + %21 = OpTypePointer Function %20 + %23 = OpConstant %20 0 + %25 = OpConstant %20 1 + %26 = OpTypeInt 32 0 + %27 = OpTypePointer Function %26 + %29 = OpConstant %26 100 + %31 = OpConstant %26 0 + %33 = OpConstant %20 -1 + %35 = OpConstant %20 -99 + %36 = OpConstant %26 5 + %37 = OpTypeArray %14 %36 + %38 = OpTypePointer Function %37 + %40 = OpConstant %14 5.5 + %41 = OpConstant %14 4.4000001 + %42 = OpConstant %14 3.29999995 + %43 = OpConstant %14 2.20000005 + %44 = OpConstant %14 1.10000002 + %45 = OpConstantComposite %37 %40 %41 %42 %43 %44 + %46 = OpConstant %26 3 + %47 = OpTypeArray %20 %46 + %48 = OpTypePointer Function %47 + %50 = OpConstant %20 3 + %51 = OpConstant %20 7 + %52 = OpConstant %20 9 + %53 = OpConstantComposite %47 %50 %51 %52 + %55 = OpTypePointer Input %14 + %56 = OpVariable %55 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %12 = OpVariable %7 Function + %13 = OpVariable %7 Function + %16 = OpVariable %15 Function + %18 = OpVariable %15 Function + %22 = OpVariable %21 Function + %24 = OpVariable %21 Function + %28 = OpVariable %27 Function + %30 = OpVariable %27 Function + %32 = OpVariable %21 Function + %34 = OpVariable %21 Function + %39 = OpVariable %38 Function + %49 = OpVariable %48 Function + OpStore %8 %9 + OpStore %10 %11 + OpStore %12 %9 + OpStore %13 %11 + OpStore %16 %17 + OpStore %18 %19 + OpStore %22 %23 + OpStore %24 %25 + OpStore %28 %29 + OpStore %30 %31 + OpStore %32 %33 + OpStore %34 %35 + OpStore %39 %45 + OpStore %49 %53 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const std::unique_ptr<opt::IRContext> context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique<FactManager>(context.get()), validator_options); + + opt::IRContext* ir_context = context.get(); + + std::vector<uint32_t> uint_words1 = fuzzerutil::IntToWords(100, 32, false); + std::vector<uint32_t> uint_words2 = fuzzerutil::IntToWords(0, 32, false); + std::vector<uint32_t> int_words1 = fuzzerutil::IntToWords(-99, 32, true); + std::vector<uint32_t> int_words2 = fuzzerutil::IntToWords(1, 32, true); + uint32_t float_word1 = fuzzerutil::FloatToWord(1.11f); + uint32_t float_word2 = fuzzerutil::FloatToWord(4.4f); + + // A unsigned int of value 100 that has a scalar type id of 26 exists and its + // id is 29. + ASSERT_EQ( + 29, fuzzerutil::MaybeGetScalarConstant(ir_context, transformation_context, + uint_words1, 26, false)); + // A unsigned int of value 0 that has a scalar type id of 26 exists and its id + // is 29. + ASSERT_EQ( + 31, fuzzerutil::MaybeGetScalarConstant(ir_context, transformation_context, + uint_words2, 26, false)); + // A signed int of value -99 that has a scalar type id of 20 exists and its id + // is 35. + ASSERT_EQ(35, fuzzerutil::MaybeGetScalarConstant( + ir_context, transformation_context, int_words1, 20, false)); + // A signed int of value 1 that has a scalar type id of 20 exists and its id + // is 25. + ASSERT_EQ(25, fuzzerutil::MaybeGetScalarConstant( + ir_context, transformation_context, int_words2, 20, false)); + // A float of value 1.11 that has a scalar type id of 14 exists and its id + // is 19. + ASSERT_EQ(19, fuzzerutil::MaybeGetScalarConstant( + ir_context, transformation_context, + std::vector<uint32_t>{float_word1}, 14, false)); + // A signed int of value 1 that has a scalar type id of 20 exists and its id + // is 25. + ASSERT_EQ(41, fuzzerutil::MaybeGetScalarConstant( + ir_context, transformation_context, + std::vector<uint32_t>{float_word2}, 14, false)); +} + +TEST(FuzzerutilTest, FuzzerUtilMaybeGetStructTypeTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %92 %52 %53 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %92 BuiltIn FragCoord + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeFloat 32 + %8 = OpTypeStruct %6 %7 + %9 = OpTypePointer Function %8 + %10 = OpTypeFunction %6 %9 + %14 = OpConstant %6 0 + %15 = OpTypePointer Function %6 + %51 = OpTypePointer Private %6 + %21 = OpConstant %6 2 + %23 = OpConstant %6 1 + %24 = OpConstant %7 1 + %25 = OpTypePointer Function %7 + %50 = OpTypePointer Private %7 + %34 = OpTypeBool + %35 = OpConstantFalse %34 + %60 = OpConstantNull %50 + %61 = OpUndef %51 + %52 = OpVariable %50 Private + %53 = OpVariable %51 Private + %80 = OpConstantComposite %8 %21 %24 + %90 = OpTypeVector %7 4 + %91 = OpTypePointer Input %90 + %92 = OpVariable %91 Input + %93 = OpConstantComposite %90 %24 %24 %24 %24 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %20 = OpVariable %9 Function + %27 = OpVariable %9 Function + %22 = OpAccessChain %15 %20 %14 + %44 = OpCopyObject %9 %20 + %26 = OpAccessChain %25 %20 %23 + %29 = OpFunctionCall %6 %12 %27 + %30 = OpAccessChain %15 %20 %14 + %45 = OpCopyObject %15 %30 + %81 = OpCopyObject %9 %27 + %33 = OpAccessChain %15 %20 %14 + OpSelectionMerge %37 None + OpBranchConditional %35 %36 %37 + %36 = OpLabel + %38 = OpAccessChain %15 %20 %14 + %40 = OpAccessChain %15 %20 %14 + %43 = OpAccessChain %15 %20 %14 + %82 = OpCopyObject %9 %27 + OpBranch %37 + %37 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %6 None %10 + %11 = OpFunctionParameter %9 + %13 = OpLabel + %46 = OpCopyObject %9 %11 + %16 = OpAccessChain %15 %11 %14 + %95 = OpCopyObject %8 %80 + OpReturnValue %21 + %100 = OpLabel + OpUnreachable + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const std::unique_ptr<opt::IRContext> context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + opt::IRContext* ir_context = context.get(); + + // 6 and 7 are all valid ids from OpTypeInt and OpTypeFloat + // so the result id of 8 should be found. + ASSERT_EQ(8, fuzzerutil::MaybeGetStructType(ir_context, + std::vector<uint32_t>{6, 7})); + + // |component_type_id| of 16 does not exist in the module, so such a struct + // type cannot be found. + ASSERT_EQ(0, fuzzerutil::MaybeGetStructType(ir_context, + std::vector<uint32_t>(6, 16))); + + // |component_type_id| of 10 is of OpTypeFunction type and thus the struct + // cannot be found. + ASSERT_EQ(0, fuzzerutil::MaybeGetStructType(ir_context, + std::vector<uint32_t>(6, 10))); +} + +TEST(FuzzerutilTest, FuzzerUtilMaybeGetVectorTypeTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %92 %52 %53 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %92 BuiltIn FragCoord + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeFloat 32 + %8 = OpTypeStruct %6 %7 + %9 = OpTypePointer Function %8 + %10 = OpTypeFunction %6 %9 + %14 = OpConstant %6 0 + %15 = OpTypePointer Function %6 + %51 = OpTypePointer Private %6 + %21 = OpConstant %6 2 + %23 = OpConstant %6 1 + %24 = OpConstant %7 1 + %25 = OpTypePointer Function %7 + %50 = OpTypePointer Private %7 + %34 = OpTypeBool + %35 = OpConstantFalse %34 + %60 = OpConstantNull %50 + %61 = OpUndef %51 + %52 = OpVariable %50 Private + %53 = OpVariable %51 Private + %80 = OpConstantComposite %8 %21 %24 + %90 = OpTypeVector %7 4 + %91 = OpTypePointer Input %90 + %92 = OpVariable %91 Input + %93 = OpConstantComposite %90 %24 %24 %24 %24 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %20 = OpVariable %9 Function + %27 = OpVariable %9 Function + %22 = OpAccessChain %15 %20 %14 + %44 = OpCopyObject %9 %20 + %26 = OpAccessChain %25 %20 %23 + %29 = OpFunctionCall %6 %12 %27 + %30 = OpAccessChain %15 %20 %14 + %45 = OpCopyObject %15 %30 + %81 = OpCopyObject %9 %27 + %33 = OpAccessChain %15 %20 %14 + OpSelectionMerge %37 None + OpBranchConditional %35 %36 %37 + %36 = OpLabel + %38 = OpAccessChain %15 %20 %14 + %40 = OpAccessChain %15 %20 %14 + %43 = OpAccessChain %15 %20 %14 + %82 = OpCopyObject %9 %27 + OpBranch %37 + %37 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %6 None %10 + %11 = OpFunctionParameter %9 + %13 = OpLabel + %46 = OpCopyObject %9 %11 + %16 = OpAccessChain %15 %11 %14 + %95 = OpCopyObject %8 %80 + OpReturnValue %21 + %100 = OpLabel + OpUnreachable + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const std::unique_ptr<opt::IRContext> context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + opt::IRContext* ir_context = context.get(); + // The vector type with |element_count| 4 and |component_type_id| 7 + // is present and has a result id of 90. + ASSERT_EQ(90, fuzzerutil::MaybeGetVectorType(ir_context, 7, 4)); + + // The vector type with |element_count| 3 and |component_type_id| 7 + // is not present in the module. + ASSERT_EQ(0, fuzzerutil::MaybeGetVectorType(ir_context, 7, 3)); + +#ifndef NDEBUG + // It should abort with |component_type_id| of 100 + // |component_type_id| must be a valid result id of an OpTypeInt, + // OpTypeFloat or OpTypeBool instruction in the module. + ASSERT_DEATH(fuzzerutil::MaybeGetVectorType(ir_context, 100, 4), + "\\|component_type_id\\| is invalid"); + + // It should abort with |element_count| of 5. + // |element_count| must be in the range [2,4]. + ASSERT_DEATH(fuzzerutil::MaybeGetVectorType(ir_context, 7, 5), + "Precondition: component count must be in range \\[2, 4\\]."); +#endif +} + +TEST(FuzzerutilTest, FuzzerUtilMaybeGetVoidTypeTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %92 %52 %53 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %92 BuiltIn FragCoord + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeFloat 32 + %8 = OpTypeStruct %6 %7 + %9 = OpTypePointer Function %8 + %10 = OpTypeFunction %6 %9 + %14 = OpConstant %6 0 + %15 = OpTypePointer Function %6 + %51 = OpTypePointer Private %6 + %21 = OpConstant %6 2 + %23 = OpConstant %6 1 + %24 = OpConstant %7 1 + %25 = OpTypePointer Function %7 + %50 = OpTypePointer Private %7 + %34 = OpTypeBool + %35 = OpConstantFalse %34 + %60 = OpConstantNull %50 + %61 = OpUndef %51 + %52 = OpVariable %50 Private + %53 = OpVariable %51 Private + %80 = OpConstantComposite %8 %21 %24 + %90 = OpTypeVector %7 4 + %91 = OpTypePointer Input %90 + %92 = OpVariable %91 Input + %93 = OpConstantComposite %90 %24 %24 %24 %24 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %20 = OpVariable %9 Function + %27 = OpVariable %9 Function + %22 = OpAccessChain %15 %20 %14 + %44 = OpCopyObject %9 %20 + %26 = OpAccessChain %25 %20 %23 + %29 = OpFunctionCall %6 %12 %27 + %30 = OpAccessChain %15 %20 %14 + %45 = OpCopyObject %15 %30 + %81 = OpCopyObject %9 %27 + %33 = OpAccessChain %15 %20 %14 + OpSelectionMerge %37 None + OpBranchConditional %35 %36 %37 + %36 = OpLabel + %38 = OpAccessChain %15 %20 %14 + %40 = OpAccessChain %15 %20 %14 + %43 = OpAccessChain %15 %20 %14 + %82 = OpCopyObject %9 %27 + OpBranch %37 + %37 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %6 None %10 + %11 = OpFunctionParameter %9 + %13 = OpLabel + %46 = OpCopyObject %9 %11 + %16 = OpAccessChain %15 %11 %14 + %95 = OpCopyObject %8 %80 + OpReturnValue %21 + %100 = OpLabel + OpUnreachable + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const std::unique_ptr<opt::IRContext> context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + opt::IRContext* ir_context = context.get(); + // A void type with a result id of 2 can be found. + ASSERT_EQ(2, fuzzerutil::MaybeGetVoidType(ir_context)); +} + +TEST(FuzzerutilTest, FuzzerUtilMaybeGetZeroConstantTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %56 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "b1" + OpName %10 "b2" + OpName %12 "b3" + OpName %13 "b4" + OpName %16 "f1" + OpName %18 "f2" + OpName %22 "zc" + OpName %24 "i1" + OpName %28 "i2" + OpName %30 "i" + OpName %32 "i3" + OpName %34 "i4" + OpName %39 "f_arr" + OpName %49 "i_arr" + OpName %56 "value" + OpDecorate %22 RelaxedPrecision + OpDecorate %24 RelaxedPrecision + OpDecorate %28 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %32 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %49 RelaxedPrecision + OpDecorate %56 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpTypePointer Function %6 + %9 = OpConstantTrue %6 + %11 = OpConstantFalse %6 + %14 = OpTypeFloat 32 + %15 = OpTypePointer Function %14 + %17 = OpConstant %14 1.23000002 + %19 = OpConstant %14 1.11000001 + %20 = OpTypeInt 32 1 + %21 = OpTypePointer Function %20 + %23 = OpConstant %20 0 + %25 = OpConstant %20 1 + %26 = OpTypeInt 32 0 + %27 = OpTypePointer Function %26 + %29 = OpConstant %26 100 + %31 = OpConstant %26 0 + %33 = OpConstant %20 -1 + %35 = OpConstant %20 -99 + %36 = OpConstant %26 5 + %37 = OpTypeArray %14 %36 + %38 = OpTypePointer Function %37 + %40 = OpConstant %14 5.5 + %41 = OpConstant %14 4.4000001 + %42 = OpConstant %14 3.29999995 + %43 = OpConstant %14 2.20000005 + %44 = OpConstant %14 1.10000002 + %45 = OpConstantComposite %37 %40 %41 %42 %43 %44 + %46 = OpConstant %26 3 + %47 = OpTypeArray %20 %46 + %48 = OpTypePointer Function %47 + %50 = OpConstant %20 3 + %51 = OpConstant %20 7 + %52 = OpConstant %20 9 + %53 = OpConstantComposite %47 %50 %51 %52 + %55 = OpTypePointer Input %14 + %56 = OpVariable %55 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %12 = OpVariable %7 Function + %13 = OpVariable %7 Function + %16 = OpVariable %15 Function + %18 = OpVariable %15 Function + %22 = OpVariable %21 Function + %24 = OpVariable %21 Function + %28 = OpVariable %27 Function + %30 = OpVariable %27 Function + %32 = OpVariable %21 Function + %34 = OpVariable %21 Function + %39 = OpVariable %38 Function + %49 = OpVariable %48 Function + OpStore %8 %9 + OpStore %10 %11 + OpStore %12 %9 + OpStore %13 %11 + OpStore %16 %17 + OpStore %18 %19 + OpStore %22 %23 + OpStore %24 %25 + OpStore %28 %29 + OpStore %30 %31 + OpStore %32 %33 + OpStore %34 %35 + OpStore %39 %45 + OpStore %49 %53 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const std::unique_ptr<opt::IRContext> context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique<FactManager>(context.get()), validator_options); + + opt::IRContext* ir_context = context.get(); + + // The id of a boolean constant will be returned give boolean type id 6. + uint32_t maybe_bool_id = fuzzerutil::MaybeGetZeroConstant( + ir_context, transformation_context, 6, false); + // The id of a 32 bit float constant will be returned given the float type + // id 14. + uint32_t maybe_float_id = fuzzerutil::MaybeGetZeroConstant( + ir_context, transformation_context, 14, false); + uint32_t maybe_signed_int_id = fuzzerutil::MaybeGetZeroConstant( + ir_context, transformation_context, 20, false); + uint32_t maybe_unsigned_int_id = fuzzerutil::MaybeGetZeroConstant( + ir_context, transformation_context, 26, false); + + // Lists of possible ids for float, signed int, unsigned int and array. + std::vector<uint32_t> float_ids{17, 19}; + std::vector<uint32_t> signed_int_ids{23, 25, 31, 33}; + + ASSERT_TRUE(maybe_bool_id == 9 || maybe_bool_id == 11); + ASSERT_TRUE(std::find(signed_int_ids.begin(), signed_int_ids.end(), + maybe_signed_int_id) != signed_int_ids.end()); + + // There is a unsigned int typed zero constant and its id is 31. + ASSERT_EQ(31, maybe_unsigned_int_id); + + // There is no zero float constant. + ASSERT_TRUE(std::find(float_ids.begin(), float_ids.end(), maybe_float_id) == + float_ids.end()); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/test/fuzz/shrinker_test.cpp b/test/fuzz/shrinker_test.cpp index 42cd182c..447ebecd 100644 --- a/test/fuzz/shrinker_test.cpp +++ b/test/fuzz/shrinker_test.cpp @@ -163,8 +163,8 @@ TEST(ShrinkerTest, ReduceAddedFunctions) { ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( donor_ir_context.get(), validator_options, kConsoleMessageConsumer)); - PseudoRandomGenerator random_generator(0); - FuzzerContext fuzzer_context(&random_generator, 100); + FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100, + false); TransformationContext transformation_context( MakeUnique<FactManager>(variant_ir_context.get()), validator_options); @@ -341,8 +341,8 @@ TEST(ShrinkerTest, HitStepLimitWhenReducingAddedFunctions) { ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( donor_ir_context.get(), validator_options, kConsoleMessageConsumer)); - PseudoRandomGenerator random_generator(0); - FuzzerContext fuzzer_context(&random_generator, 100); + FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0), 100, + false); TransformationContext transformation_context( MakeUnique<FactManager>(variant_ir_context.get()), validator_options); @@ -365,7 +365,6 @@ TEST(ShrinkerTest, HitStepLimitWhenReducingAddedFunctions) { if (inst->opcode() == SpvOpCopyObject) { copy_object_count++; } - }); return copy_object_count >= 8; }; diff --git a/test/fuzz/transformation_access_chain_test.cpp b/test/fuzz/transformation_access_chain_test.cpp index 5c431279..e7919816 100644 --- a/test/fuzz/transformation_access_chain_test.cpp +++ b/test/fuzz/transformation_access_chain_test.cpp @@ -127,6 +127,16 @@ TEST(TransformationAccessChainTest, BasicTest) { transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( 54); + // Check the case where the index type is not a 32-bit integer. + TransformationAccessChain invalid_index_example1( + 101, 28, {29}, MakeInstructionDescriptor(42, SpvOpReturn, 0)); + + // Since the index is not a 32-bit integer type but a 32-bit float type, + // ValidIndexComposite should return false and thus the transformation is not + // applicable. + ASSERT_FALSE(invalid_index_example1.IsApplicable(context.get(), + transformation_context)); + // Bad: id is not fresh ASSERT_FALSE(TransformationAccessChain( 43, 43, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0)) @@ -304,6 +314,20 @@ TEST(TransformationAccessChainTest, BasicTest) { ASSERT_FALSE( transformation_context.GetFactManager()->PointeeValueIsIrrelevant(107)); } + { + // Check the case where the access chain's base pointer has the irrelevant + // pointee fact; the resulting access chain should inherit this fact. + TransformationAccessChain transformation( + 107, 54, {}, MakeInstructionDescriptor(24, SpvOpLoad, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(54)); + } std::string after_transformation = R"( OpCapability Shader @@ -383,6 +407,7 @@ TEST(TransformationAccessChainTest, BasicTest) { %23 = OpConvertFToS %10 %22 %100 = OpAccessChain %70 %43 %80 %106 = OpAccessChain %11 %14 + %107 = OpAccessChain %53 %54 %24 = OpLoad %10 %14 %25 = OpIAdd %10 %23 %24 OpReturnValue %25 @@ -391,6 +416,44 @@ TEST(TransformationAccessChainTest, BasicTest) { ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); } +TEST(TransformationAccessChainTest, StructIndexMustBeConstant) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %20 = OpUndef %6 + %7 = OpTypeStruct %6 %6 + %8 = OpTypePointer Function %7 + %10 = OpConstant %6 0 + %11 = OpConstant %6 2 + %12 = OpTypePointer Function %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpVariable %8 Function + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique<FactManager>(context.get()), validator_options); + // Bad: %9 is a pointer to a struct, but %20 is not a constant. + ASSERT_FALSE(TransformationAccessChain( + 100, 9, {20}, MakeInstructionDescriptor(9, SpvOpReturn, 0)) + .IsApplicable(context.get(), transformation_context)); +} + TEST(TransformationAccessChainTest, IsomorphicStructs) { std::string shader = R"( OpCapability Shader diff --git a/test/fuzz/transformation_add_bit_instruction_synonym_test.cpp b/test/fuzz/transformation_add_bit_instruction_synonym_test.cpp index fa8f7bf3..d6b2ce51 100644 --- a/test/fuzz/transformation_add_bit_instruction_synonym_test.cpp +++ b/test/fuzz/transformation_add_bit_instruction_synonym_test.cpp @@ -937,6 +937,131 @@ TEST(TransformationAddBitInstructionSynonymTest, NoSynonymWhenBlockIsDead) { MakeDataDescriptor(166, {}), MakeDataDescriptor(39, {}))); } +TEST(TransformationAddBitInstructionSynonymTest, DifferentSingedness) { + std::string reference_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %37 "main" + +; Types + %2 = OpTypeInt 32 0 + %200 = OpTypeInt 32 1 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + +; Constants + %5 = OpConstant %2 0 + %6 = OpConstant %2 1 + %7 = OpConstant %2 2 + %8 = OpConstant %2 3 + %9 = OpConstant %2 4 + %10 = OpConstant %2 5 + %11 = OpConstant %2 6 + %12 = OpConstant %2 7 + %13 = OpConstant %2 8 + %14 = OpConstant %2 9 + %15 = OpConstant %2 10 + %16 = OpConstant %2 11 + %17 = OpConstant %2 12 + %18 = OpConstant %2 13 + %19 = OpConstant %2 14 + %20 = OpConstant %2 15 + %21 = OpConstant %2 16 + %22 = OpConstant %2 17 + %23 = OpConstant %2 18 + %24 = OpConstant %2 19 + %25 = OpConstant %2 20 + %26 = OpConstant %2 21 + %27 = OpConstant %2 22 + %28 = OpConstant %2 23 + %29 = OpConstant %2 24 + %30 = OpConstant %2 25 + %31 = OpConstant %2 26 + %32 = OpConstant %2 27 + %33 = OpConstant %2 28 + %34 = OpConstant %2 29 + %35 = OpConstant %2 30 + %36 = OpConstant %2 31 + %45 = OpConstant %200 32 + +; main function + %37 = OpFunction %3 None %4 + %38 = OpLabel + %39 = OpNot %200 %5 ; bit instruction + %40 = OpBitwiseOr %200 %6 %45 ; bit instruction + %41 = OpBitwiseAnd %2 %5 %6 ; bit instruction + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique<FactManager>(context.get()), validator_options); + + // Invalid because the sign of id 200 result is not equal to the sign of id 5 + // operand in OpNot. + auto transformation = TransformationAddBitInstructionSynonym( + 39, {300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, + 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, + 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, + 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, + 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, + 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, + 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, + 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, + 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, + 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427}); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Invalid because the sign of two operands not the same and the first operand + // sign not equal the result sign in OpBitwiseOr. + transformation = TransformationAddBitInstructionSynonym( + 40, {300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, + 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, + 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, + 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, + 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, + 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, + 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, + 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, + 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, + 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427}); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Successful transformation + { + // Instruction operands are the same and it's equal with the result sign in + // OpBitwiseAnd bitwise operation. + transformation = TransformationAddBitInstructionSynonym( + 41, {46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, + 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, + 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, + 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, + 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, + 163, 164, 165, 166, 167, 168, 169, 170, 171, 172}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } +} + } // namespace } // namespace fuzz } // namespace spvtools diff --git a/test/fuzz/transformation_add_constant_boolean_test.cpp b/test/fuzz/transformation_add_constant_boolean_test.cpp index 3506db6b..bd8d91c9 100644 --- a/test/fuzz/transformation_add_constant_boolean_test.cpp +++ b/test/fuzz/transformation_add_constant_boolean_test.cpp @@ -46,6 +46,7 @@ TEST(TransformationAddConstantBooleanTest, NeitherPresentInitiallyAddBoth) { spvtools::ValidatorOptions validator_options; ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); + TransformationContext transformation_context( MakeUnique<FactManager>(context.get()), validator_options); // True and false can both be added as neither is present. @@ -68,7 +69,14 @@ TEST(TransformationAddConstantBooleanTest, NeitherPresentInitiallyAddBoth) { auto add_false = TransformationAddConstantBoolean(8, false, false); ASSERT_TRUE(add_true.IsApplicable(context.get(), transformation_context)); + ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(7)); + ASSERT_EQ(nullptr, context->get_constant_mgr()->FindDeclaredConstant(7)); ApplyAndCheckFreshIds(add_true, context.get(), &transformation_context); + ASSERT_EQ(SpvOpConstantTrue, context->get_def_use_mgr()->GetDef(7)->opcode()); + ASSERT_TRUE(context->get_constant_mgr() + ->FindDeclaredConstant(7) + ->AsBoolConstant() + ->value()); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); diff --git a/test/fuzz/transformation_add_constant_composite_test.cpp b/test/fuzz/transformation_add_constant_composite_test.cpp index 2c296fbc..e5cbeec2 100644 --- a/test/fuzz/transformation_add_constant_composite_test.cpp +++ b/test/fuzz/transformation_add_constant_composite_test.cpp @@ -82,10 +82,30 @@ TEST(TransformationAddConstantCompositeTest, BasicTest) { ASSERT_FALSE(TransformationAddConstantComposite(100, 39, {11, 12}, false) .IsApplicable(context.get(), transformation_context)); - TransformationAddConstantComposite transformations[] = { - // %100 = OpConstantComposite %7 %11 %12 - TransformationAddConstantComposite(100, 7, {11, 12}, false), + { + // %100 = OpConstantComposite %7 %11 %12 + TransformationAddConstantComposite transformation(100, 7, {11, 12}, false); + ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(100)); + ASSERT_EQ(nullptr, context->get_constant_mgr()->FindDeclaredConstant(100)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_EQ(SpvOpConstantComposite, + context->get_def_use_mgr()->GetDef(100)->opcode()); + ASSERT_EQ(0.0F, context->get_constant_mgr() + ->FindDeclaredConstant(100) + ->AsVectorConstant() + ->GetComponents()[0] + ->GetFloat()); + ASSERT_EQ(1.0F, context->get_constant_mgr() + ->FindDeclaredConstant(100) + ->AsVectorConstant() + ->GetComponents()[1] + ->GetFloat()); + } + TransformationAddConstantComposite transformations[] = { // %101 = OpConstantComposite %7 %14 %15 TransformationAddConstantComposite(101, 7, {14, 15}, false), diff --git a/test/fuzz/transformation_add_constant_null_test.cpp b/test/fuzz/transformation_add_constant_null_test.cpp index ce20a677..1553e9f9 100644 --- a/test/fuzz/transformation_add_constant_null_test.cpp +++ b/test/fuzz/transformation_add_constant_null_test.cpp @@ -78,9 +78,23 @@ TEST(TransformationAddConstantNullTest, BasicTest) { ASSERT_FALSE(TransformationAddConstantNull(100, 22).IsApplicable( context.get(), transformation_context)); + { + // %100 = OpConstantNull %6 + TransformationAddConstantNull transformation(100, 6); + ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(100)); + ASSERT_EQ(nullptr, context->get_constant_mgr()->FindDeclaredConstant(100)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_EQ(SpvOpConstantNull, + context->get_def_use_mgr()->GetDef(100)->opcode()); + ASSERT_EQ( + 0.0F, + context->get_constant_mgr()->FindDeclaredConstant(100)->GetFloat()); + } + TransformationAddConstantNull transformations[] = { - // %100 = OpConstantNull %6 - TransformationAddConstantNull(100, 6), // %101 = OpConstantNull %7 TransformationAddConstantNull(101, 7), diff --git a/test/fuzz/transformation_add_constant_scalar_test.cpp b/test/fuzz/transformation_add_constant_scalar_test.cpp index a153fb1c..00c05416 100644 --- a/test/fuzz/transformation_add_constant_scalar_test.cpp +++ b/test/fuzz/transformation_add_constant_scalar_test.cpp @@ -114,6 +114,11 @@ TEST(TransformationAddConstantScalarTest, IsApplicable) { transformation = TransformationAddConstantScalar(19, 5, {0, 1, 2}, false); ASSERT_FALSE( transformation.IsApplicable(context.get(), transformation_context)); + + // Tests |words| having 2 words for a 32-bit float type. + transformation = TransformationAddConstantScalar(19, 4, {0, 1}, false); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); } TEST(TransformationAddConstantScalarTest, Apply) { @@ -171,7 +176,11 @@ TEST(TransformationAddConstantScalarTest, Apply) { MakeUnique<FactManager>(context.get()), validator_options); // Adds 32-bit unsigned integer (1 logical operand with 1 word). auto transformation = TransformationAddConstantScalar(19, 2, {4}, false); + ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(19)); + ASSERT_EQ(nullptr, context->get_constant_mgr()->FindDeclaredConstant(19)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_EQ(SpvOpConstant, context->get_def_use_mgr()->GetDef(19)->opcode()); + ASSERT_EQ(4, context->get_constant_mgr()->FindDeclaredConstant(19)->GetU32()); auto* constant_instruction = context->get_def_use_mgr()->GetDef(19); EXPECT_EQ(constant_instruction->NumInOperands(), 1); EXPECT_EQ(constant_instruction->NumInOperandWords(), 1); diff --git a/test/fuzz/transformation_add_dead_block_test.cpp b/test/fuzz/transformation_add_dead_block_test.cpp index 687f00c7..3c9e6b43 100644 --- a/test/fuzz/transformation_add_dead_block_test.cpp +++ b/test/fuzz/transformation_add_dead_block_test.cpp @@ -142,6 +142,99 @@ TEST(TransformationAddDeadBlockTest, BasicTest) { ASSERT_TRUE(IsEqual(env, variant_shader, context.get())); } +TEST(TransformationAddDeadBlockTest, ApplicableWithFalseCondition) { + std::string reference_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %6 "main" + OpExecutionMode %6 OriginUpperLeft + +; Types + %2 = OpTypeBool + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + +; Constants + %5 = OpConstantFalse %2 + +; main function + %6 = OpFunction %3 None %4 + %7 = OpLabel + OpSelectionMerge %11 None + OpBranchConditional %5 %8 %9 + %8 = OpLabel + OpBranch %10 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpBranch %11 + %11 = OpLabel + OpBranch %13 + %12 = OpLabel + OpBranch %13 + %13 = OpLabel + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique<FactManager>(context.get()), validator_options); + auto transformation = TransformationAddDeadBlock(14, 11, false); + + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + std::string variant_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %6 "main" + OpExecutionMode %6 OriginUpperLeft + +; Types + %2 = OpTypeBool + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + +; Constants + %5 = OpConstantFalse %2 + +; main function + %6 = OpFunction %3 None %4 + %7 = OpLabel + OpSelectionMerge %11 None + OpBranchConditional %5 %8 %9 + %8 = OpLabel + OpBranch %10 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpBranch %11 + %11 = OpLabel + OpSelectionMerge %13 None + OpBranchConditional %5 %14 %13 + %14 = OpLabel + OpBranch %13 + %12 = OpLabel + OpBranch %13 + %13 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(IsEqual(env, variant_shader, context.get())); +} + TEST(TransformationAddDeadBlockTest, TargetBlockMustNotBeSelectionMerge) { std::string shader = R"( OpCapability Shader diff --git a/test/fuzz/transformation_add_global_undef_test.cpp b/test/fuzz/transformation_add_global_undef_test.cpp index c3a49e47..03b91570 100644 --- a/test/fuzz/transformation_add_global_undef_test.cpp +++ b/test/fuzz/transformation_add_global_undef_test.cpp @@ -63,9 +63,18 @@ TEST(TransformationAddGlobalUndefTest, BasicTest) { ASSERT_FALSE(TransformationAddGlobalUndef(100, 3).IsApplicable( context.get(), transformation_context)); + { + // %100 = OpUndef %6 + TransformationAddGlobalUndef transformation(100, 6); + ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(100)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_EQ(SpvOpUndef, context->get_def_use_mgr()->GetDef(100)->opcode()); + } + TransformationAddGlobalUndef transformations[] = { - // %100 = OpUndef %6 - TransformationAddGlobalUndef(100, 6), // %101 = OpUndef %7 TransformationAddGlobalUndef(101, 7), diff --git a/test/fuzz/transformation_add_global_variable_test.cpp b/test/fuzz/transformation_add_global_variable_test.cpp index eb958a77..9531ade7 100644 --- a/test/fuzz/transformation_add_global_variable_test.cpp +++ b/test/fuzz/transformation_add_global_variable_test.cpp @@ -118,11 +118,24 @@ TEST(TransformationAddGlobalVariableTest, BasicTest) { 14, false) .IsApplicable(context.get(), transformation_context)); - TransformationAddGlobalVariable transformations[] = { - // %100 = OpVariable %12 Private - TransformationAddGlobalVariable(100, 12, SpvStorageClassPrivate, 16, - true), + { + // %100 = OpVariable %12 Private + ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(100)); + TransformationAddGlobalVariable transformation( + 100, 12, SpvStorageClassPrivate, 16, true); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_EQ(SpvOpVariable, context->get_def_use_mgr()->GetDef(100)->opcode()); + ASSERT_EQ( + SpvStorageClassPrivate, + static_cast<SpvStorageClass>( + context->get_def_use_mgr()->GetDef(100)->GetSingleWordInOperand( + 0))); + } + TransformationAddGlobalVariable transformations[] = { // %101 = OpVariable %10 Private TransformationAddGlobalVariable(101, 10, SpvStorageClassPrivate, 40, false), @@ -225,12 +238,10 @@ TEST(TransformationAddGlobalVariableTest, TestEntryPointInterfaceEnlargement) { %8 = OpTypeVector %6 2 %9 = OpTypePointer Function %6 %10 = OpTypePointer Private %6 - %20 = OpTypePointer Uniform %6 %11 = OpTypePointer Function %7 %12 = OpTypePointer Private %7 %13 = OpTypePointer Private %8 %14 = OpVariable %10 Private - %15 = OpVariable %20 Uniform %16 = OpConstant %7 1 %17 = OpTypePointer Private %10 %18 = OpTypeBool @@ -246,48 +257,96 @@ TEST(TransformationAddGlobalVariableTest, TestEntryPointInterfaceEnlargement) { OpFunctionEnd )"; - const auto env = SPV_ENV_UNIVERSAL_1_4; - const auto consumer = nullptr; - const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); - spvtools::ValidatorOptions validator_options; - ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, - kConsoleMessageConsumer)); - TransformationContext transformation_context( - MakeUnique<FactManager>(context.get()), validator_options); - TransformationAddGlobalVariable transformations[] = { - // %100 = OpVariable %12 Private - TransformationAddGlobalVariable(100, 12, SpvStorageClassPrivate, 16, - true), - - // %101 = OpVariable %12 Private %16 - TransformationAddGlobalVariable(101, 12, SpvStorageClassPrivate, 16, - false), - - // %102 = OpVariable %19 Private %21 - TransformationAddGlobalVariable(102, 19, SpvStorageClassPrivate, 21, - true)}; + for (auto env : {SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_5, + SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_VULKAN_1_2}) { + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique<FactManager>(context.get()), validator_options); + TransformationAddGlobalVariable transformations[] = { + // %100 = OpVariable %12 Private + TransformationAddGlobalVariable(100, 12, SpvStorageClassPrivate, 16, + true), + + // %101 = OpVariable %12 Private %16 + TransformationAddGlobalVariable(101, 12, SpvStorageClassPrivate, 16, + false), + + // %102 = OpVariable %19 Private %21 + TransformationAddGlobalVariable(102, 19, SpvStorageClassPrivate, 21, + true)}; + + for (auto& transformation : transformations) { + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(100)); + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(102)); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(101)); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + + std::string after_transformation_enlarged_interface = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "m1" %100 %101 %102 + OpEntryPoint Vertex %5 "m2" %100 %101 %102 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeInt 32 1 + %8 = OpTypeVector %6 2 + %9 = OpTypePointer Function %6 + %10 = OpTypePointer Private %6 + %11 = OpTypePointer Function %7 + %12 = OpTypePointer Private %7 + %13 = OpTypePointer Private %8 + %14 = OpVariable %10 Private + %16 = OpConstant %7 1 + %17 = OpTypePointer Private %10 + %18 = OpTypeBool + %19 = OpTypePointer Private %18 + %21 = OpConstantTrue %18 + %100 = OpVariable %12 Private %16 + %101 = OpVariable %12 Private %16 + %102 = OpVariable %19 Private %21 + %4 = OpFunction %2 None %3 + %30 = OpLabel + OpReturn + OpFunctionEnd + %5 = OpFunction %2 None %3 + %31 = OpLabel + OpReturn + OpFunctionEnd + )"; - for (auto& transformation : transformations) { ASSERT_TRUE( - transformation.IsApplicable(context.get(), transformation_context)); - ApplyAndCheckFreshIds(transformation, context.get(), - &transformation_context); + IsEqual(env, after_transformation_enlarged_interface, context.get())); } - ASSERT_TRUE( - transformation_context.GetFactManager()->PointeeValueIsIrrelevant(100)); - ASSERT_TRUE( - transformation_context.GetFactManager()->PointeeValueIsIrrelevant(102)); - ASSERT_FALSE( - transformation_context.GetFactManager()->PointeeValueIsIrrelevant(101)); - ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, - kConsoleMessageConsumer)); +} - std::string after_transformation = R"( +TEST(TransformationAddGlobalVariableTest, + TestEntryPointInterfaceNoEnlargement) { + // This checks that when global variables are added to a SPIR-V 1.3- module, + // they are not added to entry points of that module. + std::string shader = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 - OpEntryPoint Fragment %4 "m1" %100 %101 %102 - OpEntryPoint Vertex %5 "m2" %100 %101 %102 + OpEntryPoint Fragment %4 "m1" + OpEntryPoint Vertex %5 "m2" OpExecutionMode %4 OriginUpperLeft OpSource ESSL 310 %2 = OpTypeVoid @@ -297,20 +356,15 @@ TEST(TransformationAddGlobalVariableTest, TestEntryPointInterfaceEnlargement) { %8 = OpTypeVector %6 2 %9 = OpTypePointer Function %6 %10 = OpTypePointer Private %6 - %20 = OpTypePointer Uniform %6 %11 = OpTypePointer Function %7 %12 = OpTypePointer Private %7 %13 = OpTypePointer Private %8 %14 = OpVariable %10 Private - %15 = OpVariable %20 Uniform %16 = OpConstant %7 1 %17 = OpTypePointer Private %10 %18 = OpTypeBool %19 = OpTypePointer Private %18 %21 = OpConstantTrue %18 - %100 = OpVariable %12 Private %16 - %101 = OpVariable %12 Private %16 - %102 = OpVariable %19 Private %21 %4 = OpFunction %2 None %3 %30 = OpLabel OpReturn @@ -320,7 +374,86 @@ TEST(TransformationAddGlobalVariableTest, TestEntryPointInterfaceEnlargement) { OpReturn OpFunctionEnd )"; - ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); + + for (auto env : + {SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_2, + SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1}) { + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique<FactManager>(context.get()), validator_options); + TransformationAddGlobalVariable transformations[] = { + // %100 = OpVariable %12 Private + TransformationAddGlobalVariable(100, 12, SpvStorageClassPrivate, 16, + true), + + // %101 = OpVariable %12 Private %16 + TransformationAddGlobalVariable(101, 12, SpvStorageClassPrivate, 16, + false), + + // %102 = OpVariable %19 Private %21 + TransformationAddGlobalVariable(102, 19, SpvStorageClassPrivate, 21, + true)}; + + for (auto& transformation : transformations) { + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(100)); + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(102)); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(101)); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + + std::string after_transformation_fixed_interface = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "m1" + OpEntryPoint Vertex %5 "m2" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeInt 32 1 + %8 = OpTypeVector %6 2 + %9 = OpTypePointer Function %6 + %10 = OpTypePointer Private %6 + %11 = OpTypePointer Function %7 + %12 = OpTypePointer Private %7 + %13 = OpTypePointer Private %8 + %14 = OpVariable %10 Private + %16 = OpConstant %7 1 + %17 = OpTypePointer Private %10 + %18 = OpTypeBool + %19 = OpTypePointer Private %18 + %21 = OpConstantTrue %18 + %100 = OpVariable %12 Private %16 + %101 = OpVariable %12 Private %16 + %102 = OpVariable %19 Private %21 + %4 = OpFunction %2 None %3 + %30 = OpLabel + OpReturn + OpFunctionEnd + %5 = OpFunction %2 None %3 + %31 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE( + IsEqual(env, after_transformation_fixed_interface, context.get())); + } } TEST(TransformationAddGlobalVariableTest, TestAddingWorkgroupGlobals) { diff --git a/test/fuzz/transformation_add_local_variable_test.cpp b/test/fuzz/transformation_add_local_variable_test.cpp index ed57a28b..de88573a 100644 --- a/test/fuzz/transformation_add_local_variable_test.cpp +++ b/test/fuzz/transformation_add_local_variable_test.cpp @@ -98,10 +98,14 @@ TEST(TransformationAddLocalVariableTest, BasicTest) { // %105 = OpVariable %50 Function %51 { TransformationAddLocalVariable transformation(105, 50, 4, 51, true); + ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(105)); + ASSERT_EQ(nullptr, context->get_instr_block(105)); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_EQ(SpvOpVariable, context->get_def_use_mgr()->GetDef(105)->opcode()); + ASSERT_EQ(5, context->get_instr_block(105)->id()); } // %104 = OpVariable %41 Function %46 diff --git a/test/fuzz/transformation_add_parameter_test.cpp b/test/fuzz/transformation_add_parameter_test.cpp index 7b2a15f7..2ae60c9d 100644 --- a/test/fuzz/transformation_add_parameter_test.cpp +++ b/test/fuzz/transformation_add_parameter_test.cpp @@ -35,6 +35,10 @@ TEST(TransformationAddParameterTest, NonPointerBasicTest) { %7 = OpTypeBool %11 = OpTypeInt 32 1 %16 = OpTypeFloat 32 + %51 = OpConstant %11 2 + %52 = OpTypeArray %16 %51 + %53 = OpConstant %16 7 + %54 = OpConstantComposite %52 %53 %53 %3 = OpTypeFunction %2 %6 = OpTypeFunction %7 %7 %8 = OpConstant %11 23 @@ -141,6 +145,14 @@ TEST(TransformationAddParameterTest, NonPointerBasicTest) { ASSERT_TRUE(transformation_context.GetFactManager()->IdIsIrrelevant(60)); } { + TransformationAddParameter correct(9, 68, 52, {{{13, 54}}}, 69); + ASSERT_TRUE(correct.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(correct, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + ASSERT_TRUE(transformation_context.GetFactManager()->IdIsIrrelevant(68)); + } + { TransformationAddParameter correct(17, 62, 7, {{}}, 63); ASSERT_TRUE(correct.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(correct, context.get(), &transformation_context); @@ -177,6 +189,10 @@ TEST(TransformationAddParameterTest, NonPointerBasicTest) { %7 = OpTypeBool %11 = OpTypeInt 32 1 %16 = OpTypeFloat 32 + %51 = OpConstant %11 2 + %52 = OpTypeArray %16 %51 + %53 = OpConstant %16 7 + %54 = OpConstantComposite %52 %53 %53 %3 = OpTypeFunction %2 %8 = OpConstant %11 23 %12 = OpConstantTrue %7 @@ -188,11 +204,11 @@ TEST(TransformationAddParameterTest, NonPointerBasicTest) { %41 = OpTypeStruct %11 %16 %42 = OpConstantComposite %41 %8 %32 %44 = OpTypeFunction %2 %41 %7 - %6 = OpTypeFunction %7 %7 %11 + %6 = OpTypeFunction %7 %7 %11 %52 %65 = OpTypeFunction %2 %31 %4 = OpFunction %2 None %3 %5 = OpLabel - %13 = OpFunctionCall %7 %9 %12 %8 + %13 = OpFunctionCall %7 %9 %12 %8 %54 OpReturn OpFunctionEnd @@ -200,6 +216,7 @@ TEST(TransformationAddParameterTest, NonPointerBasicTest) { %9 = OpFunction %7 None %6 %14 = OpFunctionParameter %7 %60 = OpFunctionParameter %11 + %68 = OpFunctionParameter %52 %10 = OpLabel OpReturnValue %12 OpFunctionEnd diff --git a/test/fuzz/transformation_add_synonym_test.cpp b/test/fuzz/transformation_add_synonym_test.cpp index 3803fa3a..314b0039 100644 --- a/test/fuzz/transformation_add_synonym_test.cpp +++ b/test/fuzz/transformation_add_synonym_test.cpp @@ -197,6 +197,7 @@ TEST(TransformationAddSynonymTest, AddZeroSubZeroMulOne) { %37 = OpTypeVector %36 2 %38 = OpConstantTrue %36 %39 = OpConstantComposite %37 %38 %38 + %40 = OpConstant %6 37 %4 = OpFunction %2 None %3 %5 = OpLabel OpReturn @@ -249,6 +250,29 @@ TEST(TransformationAddSynonymTest, AddZeroSubZeroMulOne) { ++fresh_id; } } + { + TransformationAddSynonym transformation( + 40, protobufs::TransformationAddSynonym::BITWISE_OR, fresh_id, + insert_before); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(40, {}), MakeDataDescriptor(fresh_id, {}))); + ++fresh_id; + } + { + TransformationAddSynonym transformation( + 40, protobufs::TransformationAddSynonym::BITWISE_XOR, fresh_id, + insert_before); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(40, {}), MakeDataDescriptor(fresh_id, {}))); + } std::string expected_shader = R"( OpCapability Shader @@ -289,6 +313,7 @@ TEST(TransformationAddSynonymTest, AddZeroSubZeroMulOne) { %37 = OpTypeVector %36 2 %38 = OpConstantTrue %36 %39 = OpConstantComposite %37 %38 %38 + %40 = OpConstant %6 37 %4 = OpFunction %2 None %3 %5 = OpLabel %50 = OpIAdd %6 %9 %7 @@ -303,6 +328,8 @@ TEST(TransformationAddSynonymTest, AddZeroSubZeroMulOne) { %59 = OpFMul %14 %17 %16 %60 = OpFMul %18 %23 %20 %61 = OpIMul %24 %29 %26 + %62 = OpBitwiseOr %6 %40 %7 + %63 = OpBitwiseXor %6 %40 %7 OpReturn OpFunctionEnd )"; diff --git a/test/fuzz/transformation_add_type_array_test.cpp b/test/fuzz/transformation_add_type_array_test.cpp index ab4ed9a1..2ef8200e 100644 --- a/test/fuzz/transformation_add_type_array_test.cpp +++ b/test/fuzz/transformation_add_type_array_test.cpp @@ -90,19 +90,34 @@ TEST(TransformationAddTypeArrayTest, BasicTest) { ASSERT_FALSE(TransformationAddTypeArray(100, 11, 17) .IsApplicable(context.get(), transformation_context)); - TransformationAddTypeArray transformations[] = { - // %100 = OpTypeArray %10 %16 - TransformationAddTypeArray(100, 10, 16), - - // %101 = OpTypeArray %7 %12 - TransformationAddTypeArray(101, 7, 12)}; + { + // %100 = OpTypeArray %10 %16 + TransformationAddTypeArray transformation(100, 10, 16); + ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(100)); + ASSERT_EQ(nullptr, context->get_type_mgr()->GetType(100)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_EQ(SpvOpTypeArray, + context->get_def_use_mgr()->GetDef(100)->opcode()); + ASSERT_NE(nullptr, context->get_type_mgr()->GetType(100)->AsArray()); + } - for (auto& transformation : transformations) { + { + // %101 = OpTypeArray %7 %12 + TransformationAddTypeArray transformation(101, 7, 12); + ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(101)); + ASSERT_EQ(nullptr, context->get_type_mgr()->GetType(101)); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_EQ(SpvOpTypeArray, + context->get_def_use_mgr()->GetDef(100)->opcode()); + ASSERT_NE(nullptr, context->get_type_mgr()->GetType(100)->AsArray()); } + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); diff --git a/test/fuzz/transformation_add_type_boolean_test.cpp b/test/fuzz/transformation_add_type_boolean_test.cpp index 88d9f5b9..a8e657ba 100644 --- a/test/fuzz/transformation_add_type_boolean_test.cpp +++ b/test/fuzz/transformation_add_type_boolean_test.cpp @@ -52,9 +52,13 @@ TEST(TransformationAddTypeBooleanTest, BasicTest) { context.get(), transformation_context)); auto add_type_bool = TransformationAddTypeBoolean(100); + ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(100)); + ASSERT_EQ(nullptr, context->get_type_mgr()->GetType(100)); ASSERT_TRUE( add_type_bool.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(add_type_bool, context.get(), &transformation_context); + ASSERT_EQ(SpvOpTypeBool, context->get_def_use_mgr()->GetDef(100)->opcode()); + ASSERT_NE(nullptr, context->get_type_mgr()->GetType(100)->AsBool()); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); diff --git a/test/fuzz/transformation_add_type_float_test.cpp b/test/fuzz/transformation_add_type_float_test.cpp index 235d61b6..9275bec1 100644 --- a/test/fuzz/transformation_add_type_float_test.cpp +++ b/test/fuzz/transformation_add_type_float_test.cpp @@ -61,7 +61,8 @@ TEST(TransformationAddTypeFloatTest, IsApplicable) { ASSERT_FALSE( transformation.IsApplicable(context.get(), transformation_context)); - // Tests existing 16-bit float type. + // The transformation is not applicable because there is already a 16-bit + // float type declared in the module. transformation = TransformationAddTypeFloat(7, 16); ASSERT_FALSE( transformation.IsApplicable(context.get(), transformation_context)); @@ -70,6 +71,19 @@ TEST(TransformationAddTypeFloatTest, IsApplicable) { transformation = TransformationAddTypeFloat(7, 32); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); + + // By default, SPIR-V does not support 64-bit float types. + // Below we add such capability, so the test should now pass. + context.get()->get_feature_mgr()->AddCapability(SpvCapabilityFloat64); + ASSERT_TRUE(TransformationAddTypeFloat(7, 64).IsApplicable( + context.get(), transformation_context)); + +#ifndef NDEBUG + // Should not be able to add float type of width different from 16/32/64 + ASSERT_DEATH(TransformationAddTypeFloat(7, 20).IsApplicable( + context.get(), transformation_context), + "Unexpected float type width"); +#endif } TEST(TransformationAddTypeFloatTest, Apply) { @@ -103,15 +117,27 @@ TEST(TransformationAddTypeFloatTest, Apply) { MakeUnique<FactManager>(context.get()), validator_options); // Adds 16-bit float type. auto transformation = TransformationAddTypeFloat(6, 16); + ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(6)); + ASSERT_EQ(nullptr, context->get_type_mgr()->GetType(6)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_EQ(SpvOpTypeFloat, context->get_def_use_mgr()->GetDef(6)->opcode()); + ASSERT_NE(nullptr, context->get_type_mgr()->GetType(6)->AsFloat()); // Adds 32-bit float type. transformation = TransformationAddTypeFloat(7, 32); + ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(7)); + ASSERT_EQ(nullptr, context->get_type_mgr()->GetType(7)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_EQ(SpvOpTypeFloat, context->get_def_use_mgr()->GetDef(7)->opcode()); + ASSERT_NE(nullptr, context->get_type_mgr()->GetType(7)->AsFloat()); // Adds 64-bit float type. transformation = TransformationAddTypeFloat(8, 64); + ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(8)); + ASSERT_EQ(nullptr, context->get_type_mgr()->GetType(8)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_EQ(SpvOpTypeFloat, context->get_def_use_mgr()->GetDef(8)->opcode()); + ASSERT_NE(nullptr, context->get_type_mgr()->GetType(8)->AsFloat()); std::string variant_shader = R"( OpCapability Shader diff --git a/test/fuzz/transformation_add_type_int_test.cpp b/test/fuzz/transformation_add_type_int_test.cpp index ee4e7996..ed8e00a6 100644 --- a/test/fuzz/transformation_add_type_int_test.cpp +++ b/test/fuzz/transformation_add_type_int_test.cpp @@ -85,6 +85,29 @@ TEST(TransformationAddTypeIntTest, IsApplicable) { transformation = TransformationAddTypeInt(7, 32, true); ASSERT_TRUE( 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. + context.get()->get_feature_mgr()->AddCapability(SpvCapabilityInt16); + ASSERT_TRUE(TransformationAddTypeInt(7, 16, true) + .IsApplicable(context.get(), transformation_context)); + + // By default SPIR-V does not support 64-bit integers. + // Below we add such capability, so the test should now pass. + context.get()->get_feature_mgr()->AddCapability(SpvCapabilityInt64); + ASSERT_TRUE(TransformationAddTypeInt(7, 64, true) + .IsApplicable(context.get(), transformation_context)); + +#ifndef NDEBUG + // Should not be able to add signed/unsigned integers of width different from + // 16/32/64 bits. + ASSERT_DEATH(TransformationAddTypeInt(7, 20, false) + .IsApplicable(context.get(), transformation_context), + "Unexpected integer type width"); + ASSERT_DEATH(TransformationAddTypeInt(12, 15, false) + .IsApplicable(context.get(), transformation_context), + "Unexpected integer type width"); +#endif } TEST(TransformationAddTypeIntTest, Apply) { @@ -118,8 +141,14 @@ TEST(TransformationAddTypeIntTest, Apply) { TransformationContext transformation_context( MakeUnique<FactManager>(context.get()), validator_options); // Adds signed 8-bit integer type. + // For this transformation we also check that the def-use manager and type + // manager are updated appropriately. auto transformation = TransformationAddTypeInt(6, 8, true); + ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(6)); + ASSERT_EQ(nullptr, context->get_type_mgr()->GetType(6)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_EQ(SpvOpTypeInt, context->get_def_use_mgr()->GetDef(6)->opcode()); + ASSERT_NE(nullptr, context->get_type_mgr()->GetType(6)->AsInteger()); // Adds signed 16-bit integer type. transformation = TransformationAddTypeInt(7, 16, true); diff --git a/test/fuzz/transformation_add_type_matrix_test.cpp b/test/fuzz/transformation_add_type_matrix_test.cpp index 926e983e..df0111e2 100644 --- a/test/fuzz/transformation_add_type_matrix_test.cpp +++ b/test/fuzz/transformation_add_type_matrix_test.cpp @@ -63,10 +63,21 @@ TEST(TransformationAddTypeMatrixTest, BasicTest) { ASSERT_FALSE(TransformationAddTypeMatrix(100, 11, 2) .IsApplicable(context.get(), transformation_context)); - TransformationAddTypeMatrix transformations[] = { - // %100 = OpTypeMatrix %8 2 - TransformationAddTypeMatrix(100, 8, 2), + { + // %100 = OpTypeMatrix %8 2 + TransformationAddTypeMatrix transformation(100, 8, 2); + ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(100)); + ASSERT_EQ(nullptr, context->get_type_mgr()->GetType(100)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_EQ(SpvOpTypeMatrix, + context->get_def_use_mgr()->GetDef(100)->opcode()); + ASSERT_NE(nullptr, context->get_type_mgr()->GetType(100)->AsMatrix()); + } + TransformationAddTypeMatrix transformations[] = { // %101 = OpTypeMatrix %8 3 TransformationAddTypeMatrix(101, 8, 3), diff --git a/test/fuzz/transformation_add_type_pointer_test.cpp b/test/fuzz/transformation_add_type_pointer_test.cpp index 985e9044..b9072e3c 100644 --- a/test/fuzz/transformation_add_type_pointer_test.cpp +++ b/test/fuzz/transformation_add_type_pointer_test.cpp @@ -133,10 +133,24 @@ TEST(TransformationAddTypePointerTest, BasicTest) { ASSERT_FALSE(bad_result_id_is_not_fresh.IsApplicable(context.get(), transformation_context)); + { + auto& transformation = good_new_private_pointer_to_t; + ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(101)); + ASSERT_EQ(nullptr, context->get_type_mgr()->GetType(101)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + ASSERT_EQ(SpvOpTypePointer, + context->get_def_use_mgr()->GetDef(101)->opcode()); + ASSERT_NE(nullptr, context->get_type_mgr()->GetType(101)->AsPointer()); + } + for (auto& transformation : - {good_new_private_pointer_to_t, good_new_uniform_pointer_to_t, - good_another_function_pointer_to_s, good_new_uniform_pointer_to_s, - good_another_private_pointer_to_float, + {good_new_uniform_pointer_to_t, good_another_function_pointer_to_s, + good_new_uniform_pointer_to_s, good_another_private_pointer_to_float, good_new_private_pointer_to_private_pointer_to_float, good_new_uniform_pointer_to_vec2, good_new_private_pointer_to_uniform_pointer_to_vec2}) { diff --git a/test/fuzz/transformation_add_type_struct_test.cpp b/test/fuzz/transformation_add_type_struct_test.cpp index b57bab25..7fb91ab4 100644 --- a/test/fuzz/transformation_add_type_struct_test.cpp +++ b/test/fuzz/transformation_add_type_struct_test.cpp @@ -63,10 +63,21 @@ TEST(TransformationAddTypeStructTest, BasicTest) { ASSERT_FALSE(TransformationAddTypeStruct(100, {3}).IsApplicable( context.get(), transformation_context)); - TransformationAddTypeStruct transformations[] = { - // %100 = OpTypeStruct %6 %7 %8 %9 %10 %11 - TransformationAddTypeStruct(100, {6, 7, 8, 9, 10, 11}), + { + // %100 = OpTypeStruct %6 %7 %8 %9 %10 %11 + TransformationAddTypeStruct transformation(100, {6, 7, 8, 9, 10, 11}); + ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(100)); + ASSERT_EQ(nullptr, context->get_type_mgr()->GetType(100)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_EQ(SpvOpTypeStruct, + context->get_def_use_mgr()->GetDef(100)->opcode()); + ASSERT_NE(nullptr, context->get_type_mgr()->GetType(100)->AsStruct()); + } + TransformationAddTypeStruct transformations[] = { // %101 = OpTypeStruct TransformationAddTypeStruct(101, {}), diff --git a/test/fuzz/transformation_add_type_vector_test.cpp b/test/fuzz/transformation_add_type_vector_test.cpp index a49ba6e6..755bc4a8 100644 --- a/test/fuzz/transformation_add_type_vector_test.cpp +++ b/test/fuzz/transformation_add_type_vector_test.cpp @@ -57,10 +57,21 @@ TEST(TransformationAddTypeVectorTest, BasicTest) { ASSERT_FALSE(TransformationAddTypeVector(100, 1, 2).IsApplicable( context.get(), transformation_context)); - TransformationAddTypeVector transformations[] = { - // %100 = OpTypeVector %6 2 - TransformationAddTypeVector(100, 6, 2), + { + // %100 = OpTypeVector %6 2 + TransformationAddTypeVector transformation(100, 6, 2); + ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(100)); + ASSERT_EQ(nullptr, context->get_type_mgr()->GetType(100)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_EQ(SpvOpTypeVector, + context->get_def_use_mgr()->GetDef(100)->opcode()); + ASSERT_NE(nullptr, context->get_type_mgr()->GetType(100)->AsVector()); + } + TransformationAddTypeVector transformations[] = { // %101 = OpTypeVector %7 3 TransformationAddTypeVector(101, 7, 3), diff --git a/test/fuzz/transformation_composite_construct_test.cpp b/test/fuzz/transformation_composite_construct_test.cpp index edbfe3b6..3c5f7310 100644 --- a/test/fuzz/transformation_composite_construct_test.cpp +++ b/test/fuzz/transformation_composite_construct_test.cpp @@ -142,12 +142,29 @@ TEST(TransformationCompositeConstructTest, ConstructArrays) { TransformationCompositeConstruct make_vec2_array_length_3_bad( 37, {41, 45, 27, 27}, MakeInstructionDescriptor(46, SpvOpAccessChain, 0), 200); + // The first component does not correspond to an instruction with a result + // type so this check should return false. + TransformationCompositeConstruct make_vec2_array_length_3_nores( + 37, {2, 45, 27}, MakeInstructionDescriptor(46, SpvOpAccessChain, 0), 200); ASSERT_TRUE(make_vec2_array_length_3.IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(make_vec2_array_length_3_bad.IsApplicable( context.get(), transformation_context)); + ASSERT_FALSE(make_vec2_array_length_3_nores.IsApplicable( + context.get(), transformation_context)); + ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(200)); + ASSERT_EQ(nullptr, context->get_instr_block(200)); + uint32_t num_uses_of_41_before = context->get_def_use_mgr()->NumUses(41); + uint32_t num_uses_of_45_before = context->get_def_use_mgr()->NumUses(45); + uint32_t num_uses_of_27_before = context->get_def_use_mgr()->NumUses(27); ApplyAndCheckFreshIds(make_vec2_array_length_3, context.get(), &transformation_context); + ASSERT_EQ(SpvOpCompositeConstruct, + context->get_def_use_mgr()->GetDef(200)->opcode()); + ASSERT_EQ(34, context->get_instr_block(200)->id()); + ASSERT_EQ(num_uses_of_41_before + 1, context->get_def_use_mgr()->NumUses(41)); + ASSERT_EQ(num_uses_of_45_before + 1, context->get_def_use_mgr()->NumUses(45)); + ASSERT_EQ(num_uses_of_27_before + 1, context->get_def_use_mgr()->NumUses(27)); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( @@ -412,9 +429,15 @@ TEST(TransformationCompositeConstructTest, ConstructMatrices) { // Bad: %35 is mat4x3, not mat3x4. TransformationCompositeConstruct make_mat34_bad( 35, {25, 28, 31}, MakeInstructionDescriptor(31, SpvOpReturn, 0), 200); + // The first component does not correspond to an instruction with a result + // type so this check should return false. + TransformationCompositeConstruct make_mat34_nores( + 32, {2, 28, 31}, MakeInstructionDescriptor(31, SpvOpReturn, 0), 200); ASSERT_TRUE(make_mat34.IsApplicable(context.get(), transformation_context)); ASSERT_FALSE( make_mat34_bad.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + make_mat34_nores.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(make_mat34, context.get(), &transformation_context); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); @@ -625,9 +648,15 @@ TEST(TransformationCompositeConstructTest, ConstructStructs) { // Bad: Too few fields to make the struct. TransformationCompositeConstruct make_inner_bad( 9, {25}, MakeInstructionDescriptor(57, SpvOpAccessChain, 0), 200); + // The first component does not correspond to an instruction with a result + // type so this check should return false. + TransformationCompositeConstruct make_inner_nores( + 9, {2, 19}, MakeInstructionDescriptor(57, SpvOpAccessChain, 0), 200); ASSERT_TRUE(make_inner.IsApplicable(context.get(), transformation_context)); ASSERT_FALSE( make_inner_bad.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + make_inner_nores.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(make_inner, context.get(), &transformation_context); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); diff --git a/test/fuzz/transformation_composite_extract_test.cpp b/test/fuzz/transformation_composite_extract_test.cpp index 383a4db1..1df55918 100644 --- a/test/fuzz/transformation_composite_extract_test.cpp +++ b/test/fuzz/transformation_composite_extract_test.cpp @@ -143,10 +143,18 @@ TEST(TransformationCompositeExtractTest, BasicTest) { TransformationCompositeExtract transformation_1( MakeInstructionDescriptor(36, SpvOpConvertFToS, 0), 201, 100, {2}); + ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(201)); + ASSERT_EQ(nullptr, context->get_instr_block(201)); + uint32_t num_uses_of_100_before = context->get_def_use_mgr()->NumUses(100); ASSERT_TRUE( transformation_1.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation_1, context.get(), &transformation_context); + ASSERT_EQ(SpvOpCompositeExtract, + context->get_def_use_mgr()->GetDef(201)->opcode()); + ASSERT_EQ(15, context->get_instr_block(201)->id()); + ASSERT_EQ(num_uses_of_100_before + 1, + context->get_def_use_mgr()->NumUses(100)); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); diff --git a/test/fuzz/transformation_equation_instruction_test.cpp b/test/fuzz/transformation_equation_instruction_test.cpp index 654fffcb..5b5033d2 100644 --- a/test/fuzz/transformation_equation_instruction_test.cpp +++ b/test/fuzz/transformation_equation_instruction_test.cpp @@ -102,8 +102,12 @@ TEST(TransformationEquationInstructionTest, SignedNegate) { 14, SpvOpSNegate, {7}, return_instruction); ASSERT_TRUE( transformation1.IsApplicable(context.get(), transformation_context)); + ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(14)); + ASSERT_EQ(nullptr, context->get_instr_block(14)); ApplyAndCheckFreshIds(transformation1, context.get(), &transformation_context); + ASSERT_EQ(SpvOpSNegate, context->get_def_use_mgr()->GetDef(14)->opcode()); + ASSERT_EQ(13, context->get_instr_block(14)->id()); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); diff --git a/test/fuzz/transformation_flatten_conditional_branch_test.cpp b/test/fuzz/transformation_flatten_conditional_branch_test.cpp index 1a0ff6aa..800af0ea 100644 --- a/test/fuzz/transformation_flatten_conditional_branch_test.cpp +++ b/test/fuzz/transformation_flatten_conditional_branch_test.cpp @@ -1334,20 +1334,24 @@ TEST(TransformationFlattenConditionalBranchTest, OpFunctionEnd )"; - const auto env = SPV_ENV_UNIVERSAL_1_3; - const auto consumer = nullptr; - const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); - spvtools::ValidatorOptions validator_options; - ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, - kConsoleMessageConsumer)); - - TransformationContext transformation_context( - MakeUnique<FactManager>(context.get()), validator_options); - - auto transformation = - TransformationFlattenConditionalBranch(5, true, 0, 0, 0, {}); - ASSERT_FALSE( - transformation.IsApplicable(context.get(), transformation_context)); + for (auto env : + {SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_2, + SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1}) { + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique<FactManager>(context.get()), validator_options); + + auto transformation = + TransformationFlattenConditionalBranch(5, true, 0, 0, 0, {}); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + } } TEST(TransformationFlattenConditionalBranchTest, diff --git a/test/fuzz/transformation_merge_function_returns_test.cpp b/test/fuzz/transformation_merge_function_returns_test.cpp index e60d3458..400d49ad 100644 --- a/test/fuzz/transformation_merge_function_returns_test.cpp +++ b/test/fuzz/transformation_merge_function_returns_test.cpp @@ -147,12 +147,12 @@ TEST(TransformationMergeFunctionReturnsTest, SimpleInapplicable) { MakeUnique<FactManager>(context.get()), validator_options); // Function %1 does not exist. - ASSERT_FALSE(TransformationMergeFunctionReturns(1, 100, 101, 0, 0, {{}}) + ASSERT_FALSE(TransformationMergeFunctionReturns(1, 100, 200, 101, 0, 0, {{}}) .IsApplicable(context.get(), transformation_context)); // The entry block (%22) of function %15 does not branch unconditionally to // the following block. - ASSERT_FALSE(TransformationMergeFunctionReturns(16, 100, 101, 0, 0, {{}}) + ASSERT_FALSE(TransformationMergeFunctionReturns(16, 100, 200, 101, 0, 0, {{}}) .IsApplicable(context.get(), transformation_context)); // Block %28 is the merge block of a loop containing a return instruction, but @@ -160,7 +160,7 @@ TEST(TransformationMergeFunctionReturnsTest, SimpleInapplicable) { // not OpLabel, OpPhi or OpBranch). ASSERT_FALSE( TransformationMergeFunctionReturns( - 18, 100, 101, 0, 0, {{MakeReturnMergingInfo(29, 102, 0, {{}})}}) + 18, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(29, 102, 0, {{}})}}) .IsApplicable(context.get(), transformation_context)); // Block %34 is the merge block of a loop containing a return instruction, but @@ -168,21 +168,24 @@ TEST(TransformationMergeFunctionReturnsTest, SimpleInapplicable) { // that are not OpLabel, OpPhi or OpBranch). ASSERT_FALSE( TransformationMergeFunctionReturns( - 20, 100, 101, 0, 0, {{MakeReturnMergingInfo(35, 102, 0, {{}})}}) + 20, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(35, 102, 0, {{}})}}) .IsApplicable(context.get(), transformation_context)); // Id %1000 cannot be found in the module and there is no id of the correct // type (float) available at the end of the entry block of function %21. - ASSERT_FALSE(TransformationMergeFunctionReturns(22, 100, 101, 102, 1000, {{}}) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationMergeFunctionReturns(22, 100, 200, 101, 102, 1000, {{}}) + .IsApplicable(context.get(), transformation_context)); // Id %47 is of type float, while function %45 has return type int. - ASSERT_FALSE(TransformationMergeFunctionReturns(45, 100, 101, 102, 47, {{}}) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationMergeFunctionReturns(45, 100, 200, 101, 102, 47, {{}}) + .IsApplicable(context.get(), transformation_context)); // Id %50 is not available at the end of the entry block of function %45. - ASSERT_FALSE(TransformationMergeFunctionReturns(45, 100, 101, 102, 50, {{}}) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationMergeFunctionReturns(45, 100, 200, 101, 102, 50, {{}}) + .IsApplicable(context.get(), transformation_context)); } TEST(TransformationMergeFunctionReturnsTest, MissingBooleans) { @@ -235,8 +238,9 @@ TEST(TransformationMergeFunctionReturnsTest, MissingBooleans) { TransformationContext transformation_context( MakeUnique<FactManager>(context.get()), validator_options); - ASSERT_FALSE(TransformationMergeFunctionReturns(3, 100, 101, 0, 0, {{}}) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationMergeFunctionReturns(3, 100, 200, 101, 0, 0, {{}}) + .IsApplicable(context.get(), transformation_context)); } { // OpConstantFalse is missing. @@ -287,8 +291,9 @@ TEST(TransformationMergeFunctionReturnsTest, MissingBooleans) { TransformationContext transformation_context( MakeUnique<FactManager>(context.get()), validator_options); - ASSERT_FALSE(TransformationMergeFunctionReturns(3, 100, 101, 0, 0, {{}}) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationMergeFunctionReturns(3, 100, 200, 101, 0, 0, {{}}) + .IsApplicable(context.get(), transformation_context)); } } @@ -386,59 +391,65 @@ TEST(TransformationMergeFunctionReturnsTest, InvalidIds) { // Fresh id %100 is used twice. ASSERT_FALSE( TransformationMergeFunctionReturns( - 17, 100, 100, 0, 0, {{MakeReturnMergingInfo(20, 101, 0, {{}})}}) + 17, 100, 200, 100, 0, 0, {{MakeReturnMergingInfo(20, 101, 0, {{}})}}) .IsApplicable(context.get(), transformation_context)); // Fresh id %100 is used twice. ASSERT_FALSE( TransformationMergeFunctionReturns( - 17, 100, 101, 0, 0, {{MakeReturnMergingInfo(20, 100, 0, {{}})}}) + 17, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(20, 100, 0, {{}})}}) .IsApplicable(context.get(), transformation_context)); // %0 cannot be a fresh id for the new merge block. ASSERT_FALSE( TransformationMergeFunctionReturns( - 17, 100, 0, 0, 0, {{MakeReturnMergingInfo(20, 101, 0, {{}})}}) + 17, 100, 200, 0, 0, 0, {{MakeReturnMergingInfo(20, 101, 0, {{}})}}) + .IsApplicable(context.get(), transformation_context)); + + // %0 cannot be a fresh id for the new continue block. + ASSERT_FALSE( + TransformationMergeFunctionReturns( + 17, 100, 0, 200, 0, 0, {{MakeReturnMergingInfo(20, 101, 0, {{}})}}) .IsApplicable(context.get(), transformation_context)); // %0 cannot be a fresh id for the new header block. ASSERT_FALSE( TransformationMergeFunctionReturns( - 17, 0, 100, 0, 0, {{MakeReturnMergingInfo(20, 101, 0, {{}})}}) + 17, 0, 200, 100, 0, 0, {{MakeReturnMergingInfo(20, 101, 0, {{}})}}) .IsApplicable(context.get(), transformation_context)); // %0 cannot be a fresh id for the new |is_returning| instruction in an // existing merge block. ASSERT_FALSE( TransformationMergeFunctionReturns( - 17, 100, 101, 0, 0, {{MakeReturnMergingInfo(20, 0, 0, {{}})}}) + 17, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(20, 0, 0, {{}})}}) .IsApplicable(context.get(), transformation_context)); // %0 cannot be a fresh id for the new |return_val| instruction in the new // return block. - ASSERT_FALSE( - TransformationMergeFunctionReturns( - 14, 100, 101, 0, 10, {{MakeReturnMergingInfo(27, 102, 103, {{}})}}) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationMergeFunctionReturns( + 14, 100, 200, 101, 0, 10, + {{MakeReturnMergingInfo(27, 102, 103, {{}})}}) + .IsApplicable(context.get(), transformation_context)); // %0 cannot be a fresh id for the new |maybe_return_val| instruction in an // existing merge block, inside a non-void function. - ASSERT_FALSE( - TransformationMergeFunctionReturns( - 14, 100, 101, 102, 10, {{MakeReturnMergingInfo(27, 103, 0, {{}})}}) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationMergeFunctionReturns( + 14, 100, 200, 101, 102, 10, + {{MakeReturnMergingInfo(27, 103, 0, {{}})}}) + .IsApplicable(context.get(), transformation_context)); // Fresh id %102 is repeated. - ASSERT_FALSE( - TransformationMergeFunctionReturns( - 14, 100, 101, 102, 10, {{MakeReturnMergingInfo(27, 102, 104, {{}})}}) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationMergeFunctionReturns( + 14, 100, 200, 101, 102, 10, + {{MakeReturnMergingInfo(27, 102, 104, {{}})}}) + .IsApplicable(context.get(), transformation_context)); // Id %11 (type int) does not have the correct type (float) for OpPhi // instruction %31. ASSERT_FALSE( TransformationMergeFunctionReturns( - 16, 100, 101, 0, 0, + 16, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(36, 103, 104, {{{31, 11}, {32, 11}}})}}) .IsApplicable(context.get(), transformation_context)); @@ -446,21 +457,21 @@ TEST(TransformationMergeFunctionReturnsTest, InvalidIds) { // instruction %31. ASSERT_FALSE( TransformationMergeFunctionReturns( - 16, 100, 101, 0, 0, + 16, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(36, 102, 0, {{{31, 11}, {32, 11}}})}}) .IsApplicable(context.get(), transformation_context)); // Id %43 is not available at the end of the entry block. ASSERT_FALSE( TransformationMergeFunctionReturns( - 16, 100, 101, 0, 0, + 16, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(36, 102, 0, {{{31, 44}, {32, 11}}})}}) .IsApplicable(context.get(), transformation_context)); // There is not a mapping for id %31 (float OpPhi instruction in a loop merge // block) and no suitable id is available at the end of the entry block. ASSERT_FALSE(TransformationMergeFunctionReturns( - 16, 100, 101, 0, 0, + 16, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(36, 102, 0, {{{32, 11}}})}}) .IsApplicable(context.get(), transformation_context)); @@ -468,7 +479,7 @@ TEST(TransformationMergeFunctionReturnsTest, InvalidIds) { // available at the end of the entry block. ASSERT_FALSE( TransformationMergeFunctionReturns( - 16, 100, 101, 0, 0, + 16, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(36, 102, 0, {{{31, 1000}, {32, 11}}})}}) .IsApplicable(context.get(), transformation_context)); } @@ -560,7 +571,7 @@ TEST(TransformationMergeFunctionReturnsTest, Simple) { // The 0s are allowed because the function's return type is void. auto transformation1 = - TransformationMergeFunctionReturns(14, 100, 101, 0, 0, {{}}); + TransformationMergeFunctionReturns(14, 100, 200, 101, 0, 0, {{}}); ASSERT_TRUE( transformation1.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation1, context.get(), @@ -570,13 +581,14 @@ TEST(TransformationMergeFunctionReturnsTest, Simple) { // %12 is available at the end of the entry block of %19 (it is a global // variable). - ASSERT_TRUE(TransformationMergeFunctionReturns(19, 110, 111, 112, 12, {{}}) - .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + TransformationMergeFunctionReturns(19, 110, 210, 111, 112, 12, {{}}) + .IsApplicable(context.get(), transformation_context)); // %1000 cannot be found in the module, but there is a suitable id available // at the end of the entry block (%12). auto transformation2 = - TransformationMergeFunctionReturns(19, 110, 111, 112, 1000, {{}}); + TransformationMergeFunctionReturns(19, 110, 210, 111, 112, 1000, {{}}); ASSERT_TRUE( transformation2.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation2, context.get(), @@ -586,13 +598,14 @@ TEST(TransformationMergeFunctionReturnsTest, Simple) { // %27 is available at the end of the entry block of %26 (it is a function // parameter). - ASSERT_TRUE(TransformationMergeFunctionReturns(26, 120, 121, 122, 27, {{}}) - .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + TransformationMergeFunctionReturns(26, 120, 220, 121, 122, 27, {{}}) + .IsApplicable(context.get(), transformation_context)); // %1000 cannot be found in the module, but there is a suitable id available // at the end of the entry block (%27). auto transformation3 = - TransformationMergeFunctionReturns(26, 120, 121, 122, 1000, {{}}); + TransformationMergeFunctionReturns(26, 120, 220, 121, 122, 1000, {{}}); ASSERT_TRUE( transformation3.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation3, context.get(), @@ -602,13 +615,14 @@ TEST(TransformationMergeFunctionReturnsTest, Simple) { // %35 is available at the end of the entry block of %33 (it is in the entry // block). - ASSERT_TRUE(TransformationMergeFunctionReturns(26, 130, 131, 132, 27, {{}}) - .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + TransformationMergeFunctionReturns(26, 130, 230, 131, 132, 27, {{}}) + .IsApplicable(context.get(), transformation_context)); // %1000 cannot be found in the module, but there is a suitable id available // at the end of the entry block (%35). auto transformation4 = - TransformationMergeFunctionReturns(33, 130, 131, 132, 1000, {{}}); + TransformationMergeFunctionReturns(33, 130, 230, 131, 132, 1000, {{}}); ASSERT_TRUE( transformation4.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation4, context.get(), @@ -642,8 +656,8 @@ TEST(TransformationMergeFunctionReturnsTest, Simple) { %15 = OpLabel OpBranch %100 %100 = OpLabel - OpLoopMerge %101 %100 None - OpBranchConditional %11 %16 %100 + OpLoopMerge %101 %200 None + OpBranch %16 %16 = OpLabel OpSelectionMerge %17 None OpBranchConditional %11 %18 %17 @@ -653,13 +667,15 @@ TEST(TransformationMergeFunctionReturnsTest, Simple) { OpBranch %101 %101 = OpLabel OpReturn + %200 = OpLabel + OpBranch %100 OpFunctionEnd %19 = OpFunction %5 None %6 %20 = OpLabel OpBranch %110 %110 = OpLabel - OpLoopMerge %111 %110 None - OpBranchConditional %11 %21 %110 + OpLoopMerge %111 %210 None + OpBranch %21 %21 = OpLabel OpSelectionMerge %22 None OpBranchConditional %11 %23 %24 @@ -673,14 +689,16 @@ TEST(TransformationMergeFunctionReturnsTest, Simple) { %111 = OpLabel %112 = OpPhi %5 %12 %23 %25 %24 OpReturnValue %112 + %210 = OpLabel + OpBranch %110 OpFunctionEnd %26 = OpFunction %7 None %8 %27 = OpFunctionParameter %7 %28 = OpLabel OpBranch %120 %120 = OpLabel - OpLoopMerge %121 %120 None - OpBranchConditional %11 %29 %120 + OpLoopMerge %121 %220 None + OpBranch %29 %29 = OpLabel OpSelectionMerge %30 None OpBranchConditional %11 %31 %30 @@ -692,14 +710,16 @@ TEST(TransformationMergeFunctionReturnsTest, Simple) { %121 = OpLabel %122 = OpPhi %7 %27 %30 %32 %31 OpReturnValue %122 + %220 = OpLabel + OpBranch %120 OpFunctionEnd %33 = OpFunction %7 None %9 %34 = OpLabel %35 = OpConvertSToF %7 %12 OpBranch %130 %130 = OpLabel - OpLoopMerge %131 %130 None - OpBranchConditional %11 %36 %130 + OpLoopMerge %131 %230 None + OpBranch %36 %36 = OpLabel OpSelectionMerge %37 None OpBranchConditional %11 %38 %37 @@ -711,6 +731,8 @@ TEST(TransformationMergeFunctionReturnsTest, Simple) { %131 = OpLabel %132 = OpPhi %7 %35 %37 %39 %38 OpReturnValue %132 + %230 = OpLabel + OpBranch %130 OpFunctionEnd )"; @@ -743,8 +765,8 @@ TEST(TransformationMergeFunctionReturnsTest, NestedLoops) { %15 = OpLabel OpBranch %16 %16 = OpLabel - OpLoopMerge %17 %16 None - OpBranchConditional %8 %18 %16 + OpLoopMerge %17 %916 None + OpBranch %18 %18 = OpLabel OpLoopMerge %19 %20 None OpBranchConditional %8 %19 %21 @@ -767,8 +789,8 @@ TEST(TransformationMergeFunctionReturnsTest, NestedLoops) { %28 = OpLabel OpBranch %29 %29 = OpLabel - OpLoopMerge %30 %29 None - OpBranchConditional %8 %30 %29 + OpLoopMerge %30 %929 None + OpBranch %30 %30 = OpLabel OpLoopMerge %31 %32 None OpBranch %33 @@ -792,6 +814,10 @@ TEST(TransformationMergeFunctionReturnsTest, NestedLoops) { OpBranch %38 %38 = OpLabel OpReturnValue %12 + %916 = OpLabel + OpBranch %16 + %929 = OpLabel + OpBranch %29 OpFunctionEnd )"; @@ -805,7 +831,7 @@ TEST(TransformationMergeFunctionReturnsTest, NestedLoops) { MakeUnique<FactManager>(context.get()), validator_options); auto transformation = TransformationMergeFunctionReturns( - 14, 100, 101, 102, 11, + 14, 100, 200, 101, 102, 11, {{MakeReturnMergingInfo(19, 103, 104, {{}}), MakeReturnMergingInfo(17, 105, 106, {{}}), MakeReturnMergingInfo(31, 107, 108, {{{35, 10}, {36, 12}}}), @@ -841,11 +867,11 @@ TEST(TransformationMergeFunctionReturnsTest, NestedLoops) { %15 = OpLabel OpBranch %100 %100 = OpLabel - OpLoopMerge %101 %100 None - OpBranchConditional %8 %16 %100 + OpLoopMerge %101 %200 None + OpBranch %16 %16 = OpLabel - OpLoopMerge %17 %16 None - OpBranchConditional %8 %18 %16 + OpLoopMerge %17 %916 None + OpBranch %18 %18 = OpLabel OpLoopMerge %19 %20 None OpBranchConditional %8 %19 %21 @@ -872,8 +898,8 @@ TEST(TransformationMergeFunctionReturnsTest, NestedLoops) { %28 = OpLabel OpBranch %29 %29 = OpLabel - OpLoopMerge %30 %29 None - OpBranchConditional %8 %30 %29 + OpLoopMerge %30 %929 None + OpBranch %30 %30 = OpLabel OpLoopMerge %31 %32 None OpBranch %33 @@ -901,9 +927,15 @@ TEST(TransformationMergeFunctionReturnsTest, NestedLoops) { OpBranchConditional %109 %101 %38 %38 = OpLabel OpBranch %101 + %916 = OpLabel + OpBranch %16 + %929 = OpLabel + OpBranch %29 %101 = OpLabel %102 = OpPhi %5 %106 %17 %110 %23 %12 %38 OpReturnValue %102 + %200 = OpLabel + OpBranch %100 OpFunctionEnd )"; @@ -997,7 +1029,7 @@ TEST(TransformationMergeFunctionReturnsTest, OverflowIds) { // No mapping from merge block %16 to fresh ids is given, so overflow ids are // needed. auto transformation1 = - TransformationMergeFunctionReturns(12, 100, 101, 102, 10, {{}}); + TransformationMergeFunctionReturns(12, 100, 200, 101, 102, 10, {{}}); #ifndef NDEBUG ASSERT_DEATH( @@ -1016,7 +1048,7 @@ TEST(TransformationMergeFunctionReturnsTest, OverflowIds) { // No mapping from merge block %27 to fresh ids is given, so overflow ids are // needed. auto transformation2 = - TransformationMergeFunctionReturns(24, 110, 111, 0, 0, {{}}); + TransformationMergeFunctionReturns(24, 110, 210, 111, 0, 0, {{}}); #ifndef NDEBUG ASSERT_DEATH( @@ -1054,8 +1086,8 @@ TEST(TransformationMergeFunctionReturnsTest, OverflowIds) { %13 = OpLabel OpBranch %100 %100 = OpLabel - OpLoopMerge %101 %100 None - OpBranchConditional %8 %14 %100 + OpLoopMerge %101 %200 None + OpBranch %14 %14 = OpLabel %15 = OpIAdd %5 %10 %10 OpLoopMerge %16 %17 None @@ -1081,13 +1113,15 @@ TEST(TransformationMergeFunctionReturnsTest, OverflowIds) { %101 = OpLabel %102 = OpPhi %5 %1001 %16 %22 %23 OpReturnValue %102 + %200 = OpLabel + OpBranch %100 OpFunctionEnd %24 = OpFunction %3 None %4 %25 = OpLabel OpBranch %110 %110 = OpLabel - OpLoopMerge %111 %110 None - OpBranchConditional %8 %26 %110 + OpLoopMerge %111 %210 None + OpBranch %26 %26 = OpLabel OpLoopMerge %27 %28 None OpBranch %29 @@ -1110,6 +1144,8 @@ TEST(TransformationMergeFunctionReturnsTest, OverflowIds) { OpBranch %111 %111 = OpLabel OpReturn + %210 = OpLabel + OpBranch %110 OpFunctionEnd )"; @@ -1179,7 +1215,7 @@ TEST(TransformationMergeFunctionReturnsTest, MissingIdsForOpPhi) { // corresponding mapping. auto transformation = TransformationMergeFunctionReturns( - 12, 101, 102, 0, 0, + 12, 101, 200, 102, 0, 0, {{MakeReturnMergingInfo(17, 103, 0, {{{25, 7}, {35, 8}}})}}); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); @@ -1212,8 +1248,8 @@ TEST(TransformationMergeFunctionReturnsTest, MissingIdsForOpPhi) { %15 = OpConvertSToF %10 %13 OpBranch %101 %101 = OpLabel - OpLoopMerge %102 %101 None - OpBranchConditional %6 %16 %101 + OpLoopMerge %102 %200 None + OpBranch %16 %16 = OpLabel OpLoopMerge %17 %18 None OpBranch %19 @@ -1238,6 +1274,8 @@ TEST(TransformationMergeFunctionReturnsTest, MissingIdsForOpPhi) { OpBranch %102 %102 = OpLabel OpReturn + %200 = OpLabel + OpBranch %101 OpFunctionEnd )"; @@ -1324,14 +1362,14 @@ TEST(TransformationMergeFunctionReturnsTest, RespectDominanceRules1) { // block %14 is added. ASSERT_FALSE( TransformationMergeFunctionReturns( - 2, 100, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}}) + 2, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}}) .IsApplicable(context.get(), transformation_context)); // In function %18, The definition of id %26 will still dominate its use in // instruction %27 (inside merge block %21), because %27 is an OpPhi // instruction. auto transformation = TransformationMergeFunctionReturns( - 18, 100, 101, 0, 0, {{MakeReturnMergingInfo(21, 102, 103, {{}})}}); + 18, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(21, 102, 103, {{}})}}); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); @@ -1376,8 +1414,8 @@ TEST(TransformationMergeFunctionReturnsTest, RespectDominanceRules1) { %19 = OpLabel OpBranch %100 %100 = OpLabel - OpLoopMerge %101 %100 None - OpBranchConditional %6 %20 %100 + OpLoopMerge %101 %200 None + OpBranch %20 %20 = OpLabel OpLoopMerge %21 %22 None OpBranch %23 @@ -1399,6 +1437,8 @@ TEST(TransformationMergeFunctionReturnsTest, RespectDominanceRules1) { OpBranch %101 %101 = OpLabel OpReturn + %200 = OpLabel + OpBranch %100 OpFunctionEnd )"; @@ -1482,17 +1522,17 @@ TEST(TransformationMergeFunctionReturnsTest, RespectDominanceRules2) { // block %14 to merge block %10 is added. ASSERT_FALSE( TransformationMergeFunctionReturns( - 2, 100, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}}) + 2, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}}) .IsApplicable(context.get(), transformation_context)); // In function %18, the definition of id %26 will not dominate its use in // instruction %28 (inside block %27) after a new branch from return // block %25 to merge block %21 is added. - ASSERT_FALSE(TransformationMergeFunctionReturns( - 2, 100, 101, 0, 0, - {{MakeReturnMergingInfo(10, 102, 0, {{}}), - MakeReturnMergingInfo(21, 103, 0, {{}})}}) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationMergeFunctionReturns( + 2, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 0, {{}}), + MakeReturnMergingInfo(21, 103, 0, {{}})}}) + .IsApplicable(context.get(), transformation_context)); } TEST(TransformationMergeFunctionReturnsTest, RespectDominanceRules3) { @@ -1556,7 +1596,7 @@ TEST(TransformationMergeFunctionReturnsTest, RespectDominanceRules3) { // fact that the id definition dominates the uses does not depend on it // dominating the merge block. auto transformation = TransformationMergeFunctionReturns( - 2, 100, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}}); + 2, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}}); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); @@ -1579,8 +1619,8 @@ TEST(TransformationMergeFunctionReturnsTest, RespectDominanceRules3) { %8 = OpLabel OpBranch %100 %100 = OpLabel - OpLoopMerge %101 %100 None - OpBranchConditional %6 %9 %100 + OpLoopMerge %101 %200 None + OpBranch %9 %9 = OpLabel OpLoopMerge %10 %11 None OpBranch %12 @@ -1608,6 +1648,8 @@ TEST(TransformationMergeFunctionReturnsTest, RespectDominanceRules3) { OpBranch %101 %101 = OpLabel OpReturn + %200 = OpLabel + OpBranch %100 OpFunctionEnd )"; @@ -1700,7 +1742,7 @@ TEST(TransformationMergeFunctionReturnsTest, RespectDominanceRules4) { // instruction %19 after the transformation is applied, because %13 dominates // all of the return blocks. auto transformation = TransformationMergeFunctionReturns( - 2, 100, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}}); + 2, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}}); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); @@ -1710,10 +1752,10 @@ TEST(TransformationMergeFunctionReturnsTest, RespectDominanceRules4) { // In function %20, the definition of id %28 will not dominate its use in // instruction %32 after the transformation is applied, because %28 dominates // only one of the return blocks. - ASSERT_FALSE( - TransformationMergeFunctionReturns( - 20, 100, 101, 0, 0, {{MakeReturnMergingInfo(23, 102, 103, {{}})}}) - .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationMergeFunctionReturns( + 20, 100, 200, 101, 0, 0, + {{MakeReturnMergingInfo(23, 102, 103, {{}})}}) + .IsApplicable(context.get(), transformation_context)); std::string after_transformation = R"( OpCapability Shader @@ -1731,8 +1773,8 @@ TEST(TransformationMergeFunctionReturnsTest, RespectDominanceRules4) { %8 = OpLabel OpBranch %100 %100 = OpLabel - OpLoopMerge %101 %100 None - OpBranchConditional %6 %9 %100 + OpLoopMerge %101 %200 None + OpBranch %9 %9 = OpLabel OpLoopMerge %10 %11 None OpBranch %12 @@ -1759,6 +1801,8 @@ TEST(TransformationMergeFunctionReturnsTest, RespectDominanceRules4) { OpBranch %101 %101 = OpLabel OpReturn + %200 = OpLabel + OpBranch %100 OpFunctionEnd %20 = OpFunction %3 None %4 %21 = OpLabel @@ -1830,7 +1874,7 @@ TEST(TransformationMergeFunctionReturnsTest, OpPhiAfterFirstBlock) { MakeUnique<FactManager>(context.get()), validator_options); auto transformation = - TransformationMergeFunctionReturns(2, 100, 101, 0, 0, {}); + TransformationMergeFunctionReturns(2, 100, 200, 101, 0, 0, {}); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); @@ -1863,8 +1907,8 @@ TEST(TransformationMergeFunctionReturnsTest, OpPhiAfterFirstBlock) { %8 = OpLabel OpBranch %100 %100 = OpLabel - OpLoopMerge %101 %100 None - OpBranchConditional %6 %9 %100 + OpLoopMerge %101 %200 None + OpBranch %9 %9 = OpLabel %10 = OpPhi %5 %6 %100 OpSelectionMerge %11 None @@ -1875,6 +1919,8 @@ TEST(TransformationMergeFunctionReturnsTest, OpPhiAfterFirstBlock) { OpBranch %101 %101 = OpLabel OpReturn + %200 = OpLabel + OpBranch %100 OpFunctionEnd )"; diff --git a/test/fuzz/transformation_outline_function_test.cpp b/test/fuzz/transformation_outline_function_test.cpp index c567680c..cc62049d 100644 --- a/test/fuzz/transformation_outline_function_test.cpp +++ b/test/fuzz/transformation_outline_function_test.cpp @@ -3207,6 +3207,45 @@ TEST(TransformationOutlineFunctionTest, Miscellaneous4) { ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); } +TEST(TransformationOutlineFunctionTest, NoOutlineWithUnreachableBlocks) { + // This checks that outlining will not be performed if a node in the region + // has an unreachable predecessor. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %7 = OpLabel + OpBranch %5 + %5 = OpLabel + OpReturn + %6 = OpLabel + OpBranch %5 + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique<FactManager>(context.get()), validator_options); + TransformationOutlineFunction transformation(5, 5, /* not relevant */ 200, + 100, 101, 102, 103, + /* not relevant */ 201, {}, {}); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); +} + } // namespace } // namespace fuzz } // namespace spvtools diff --git a/test/fuzz/transformation_permute_phi_operands_test.cpp b/test/fuzz/transformation_permute_phi_operands_test.cpp index 2843cfc5..0774dcf3 100644 --- a/test/fuzz/transformation_permute_phi_operands_test.cpp +++ b/test/fuzz/transformation_permute_phi_operands_test.cpp @@ -60,6 +60,7 @@ TEST(TransformationPermutePhiOperandsTest, BasicTest) { OpBranch %17 %17 = OpLabel %25 = OpPhi %6 %20 %16 %24 %21 + %30 = OpIAdd %6 %25 %25 OpStore %8 %25 OpReturn OpFunctionEnd @@ -105,6 +106,47 @@ TEST(TransformationPermutePhiOperandsTest, BasicTest) { transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + // Check that the def-use manager knows that the phi instruction's ids have + // been permuted. + std::vector<std::pair<uint32_t, uint32_t>> phi_operand_to_new_operand_index = + {{20, 4}, {16, 5}, {24, 2}, {21, 3}}; + for (std::pair<uint32_t, uint32_t>& entry : + phi_operand_to_new_operand_index) { + context->get_def_use_mgr()->WhileEachUse( + entry.first, + [&entry](opt::Instruction* inst, uint32_t operand_index) -> bool { + if (inst->result_id() == 25) { + EXPECT_EQ(entry.second, operand_index); + return false; + } + return true; + }); + } + bool found_use_in_store = false; + bool found_use_in_add_lhs = false; + bool found_use_in_add_rhs = false; + context->get_def_use_mgr()->ForEachUse( + 25, [&found_use_in_store, &found_use_in_add_lhs, &found_use_in_add_rhs]( + opt::Instruction* inst, uint32_t operand_index) { + if (inst->opcode() == SpvOpStore) { + ASSERT_FALSE(found_use_in_store); + found_use_in_store = true; + } else { + ASSERT_EQ(SpvOpIAdd, inst->opcode()); + if (operand_index == 2) { + ASSERT_FALSE(found_use_in_add_lhs); + found_use_in_add_lhs = true; + } else { + ASSERT_EQ(3, operand_index); + ASSERT_FALSE(found_use_in_add_rhs); + found_use_in_add_rhs = true; + } + } + }); + ASSERT_TRUE(found_use_in_store); + ASSERT_TRUE(found_use_in_add_lhs); + ASSERT_TRUE(found_use_in_add_rhs); + std::string after_transformation = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" @@ -142,6 +184,7 @@ TEST(TransformationPermutePhiOperandsTest, BasicTest) { OpBranch %17 %17 = OpLabel %25 = OpPhi %6 %24 %21 %20 %16 + %30 = OpIAdd %6 %25 %25 OpStore %8 %25 OpReturn OpFunctionEnd diff --git a/test/fuzz/transformation_push_id_through_variable_test.cpp b/test/fuzz/transformation_push_id_through_variable_test.cpp index cd45c4ce..7487cab9 100644 --- a/test/fuzz/transformation_push_id_through_variable_test.cpp +++ b/test/fuzz/transformation_push_id_through_variable_test.cpp @@ -341,7 +341,26 @@ TEST(TransformationPushIdThroughVariableTest, Apply) { auto transformation = TransformationPushIdThroughVariable( value_id, value_synonym_id, variable_id, variable_storage_class, initializer_id, instruction_descriptor); + ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(value_synonym_id)); + ASSERT_EQ(nullptr, context->get_instr_block(value_synonym_id)); + ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(variable_id)); + ASSERT_EQ(nullptr, context->get_instr_block(variable_id)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_EQ(SpvOpLoad, + context->get_def_use_mgr()->GetDef(value_synonym_id)->opcode()); + ASSERT_EQ(36, context->get_instr_block(value_synonym_id)->id()); + ASSERT_EQ(SpvOpVariable, + context->get_def_use_mgr()->GetDef(variable_id)->opcode()); + ASSERT_EQ(5, context->get_instr_block(variable_id)->id()); + uint32_t variable_use_count = 0; + context->get_def_use_mgr()->ForEachUse( + variable_id, + [&variable_use_count](opt::Instruction* inst, uint32_t /*unused*/) { + ASSERT_TRUE(inst->opcode() == SpvOpLoad || + inst->opcode() == SpvOpStore); + variable_use_count++; + }); + ASSERT_EQ(2, variable_use_count); value_id = 21; value_synonym_id = 102; diff --git a/test/fuzz/transformation_replace_branch_from_dead_block_with_exit_test.cpp b/test/fuzz/transformation_replace_branch_from_dead_block_with_exit_test.cpp index 45325038..6bba14f6 100644 --- a/test/fuzz/transformation_replace_branch_from_dead_block_with_exit_test.cpp +++ b/test/fuzz/transformation_replace_branch_from_dead_block_with_exit_test.cpp @@ -566,6 +566,302 @@ TEST(TransformationReplaceBranchFromDeadBlockWithExitTest, OpPhi) { ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); } +TEST(TransformationReplaceBranchFromDeadBlockWithExitTest, + DominatorAfterDeadBlockSuccessor) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantFalse %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %8 None + OpBranchConditional %7 %9 %10 + %9 = OpLabel + OpBranch %11 + %11 = OpLabel + %12 = OpCopyObject %6 %7 + OpBranch %8 + %10 = OpLabel + OpBranch %13 + %8 = OpLabel + OpReturn + %13 = OpLabel + OpBranch %8 + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique<FactManager>(context.get()), validator_options); + + transformation_context.GetFactManager()->AddFactBlockIsDead(9); + transformation_context.GetFactManager()->AddFactBlockIsDead(11); + + ASSERT_FALSE( + TransformationReplaceBranchFromDeadBlockWithExit(11, SpvOpUnreachable, 0) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationReplaceBranchFromDeadBlockWithExitTest, + UnreachableSuccessor) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantFalse %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %8 None + OpBranchConditional %7 %9 %10 + %9 = OpLabel + OpBranch %11 + %11 = OpLabel + %12 = OpCopyObject %6 %7 + OpBranch %8 + %10 = OpLabel + OpReturn + %8 = OpLabel + OpReturn + %13 = OpLabel + OpBranch %8 + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique<FactManager>(context.get()), validator_options); + + transformation_context.GetFactManager()->AddFactBlockIsDead(9); + transformation_context.GetFactManager()->AddFactBlockIsDead(11); + + TransformationReplaceBranchFromDeadBlockWithExit transformation( + 11, SpvOpUnreachable, 0); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantFalse %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %8 None + OpBranchConditional %7 %9 %10 + %9 = OpLabel + OpBranch %11 + %11 = OpLabel + %12 = OpCopyObject %6 %7 + OpUnreachable + %10 = OpLabel + OpReturn + %8 = OpLabel + OpReturn + %13 = OpLabel + OpBranch %8 + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationReplaceBranchFromDeadBlockWithExitTest, + DeadBlockAfterItsSuccessor) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %8 None + OpBranchConditional %7 %9 %10 + %9 = OpLabel + OpBranch %11 + %11 = OpLabel + %12 = OpCopyObject %6 %7 + OpBranch %8 + %10 = OpLabel + OpBranch %13 + %8 = OpLabel + OpReturn + %13 = OpLabel + OpBranch %8 + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique<FactManager>(context.get()), validator_options); + + transformation_context.GetFactManager()->AddFactBlockIsDead(10); + transformation_context.GetFactManager()->AddFactBlockIsDead(13); + + TransformationReplaceBranchFromDeadBlockWithExit transformation( + 13, SpvOpUnreachable, 0); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %8 None + OpBranchConditional %7 %9 %10 + %9 = OpLabel + OpBranch %11 + %11 = OpLabel + %12 = OpCopyObject %6 %7 + OpBranch %8 + %10 = OpLabel + OpBranch %13 + %8 = OpLabel + OpReturn + %13 = OpLabel + OpUnreachable + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationReplaceBranchFromDeadBlockWithExitTest, + BranchToOuterMergeBlock) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %15 = OpTypeInt 32 0 + %14 = OpUndef %15 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %8 None + OpSwitch %14 %9 1 %8 + %9 = OpLabel + OpSelectionMerge %10 None + OpBranchConditional %7 %11 %10 + %8 = OpLabel + OpReturn + %11 = OpLabel + OpBranch %8 + %10 = OpLabel + OpBranch %8 + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique<FactManager>(context.get()), validator_options); + + transformation_context.GetFactManager()->AddFactBlockIsDead(10); + + TransformationReplaceBranchFromDeadBlockWithExit transformation( + 10, SpvOpUnreachable, 0); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %15 = OpTypeInt 32 0 + %14 = OpUndef %15 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %8 None + OpSwitch %14 %9 1 %8 + %9 = OpLabel + OpSelectionMerge %10 None + OpBranchConditional %7 %11 %10 + %8 = OpLabel + OpReturn + %11 = OpLabel + OpBranch %8 + %10 = OpLabel + OpUnreachable + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + } // namespace } // namespace fuzz } // namespace spvtools diff --git a/test/fuzz/transformation_replace_id_with_synonym_test.cpp b/test/fuzz/transformation_replace_id_with_synonym_test.cpp index 5c10fc59..629a00ee 100644 --- a/test/fuzz/transformation_replace_id_with_synonym_test.cpp +++ b/test/fuzz/transformation_replace_id_with_synonym_test.cpp @@ -303,10 +303,18 @@ TEST(TransformationReplaceIdWithSynonymTest, LegalTransformations) { auto global_constant_synonym = TransformationReplaceIdWithSynonym( MakeIdUseDescriptor(19, MakeInstructionDescriptor(47, SpvOpStore, 0), 1), 210); + uint32_t num_uses_of_original_id_before_replacement = + context->get_def_use_mgr()->NumUses(19); + uint32_t num_uses_of_synonym_before_replacement = + context->get_def_use_mgr()->NumUses(210); ASSERT_TRUE(global_constant_synonym.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(global_constant_synonym, context.get(), &transformation_context); + ASSERT_EQ(num_uses_of_original_id_before_replacement - 1, + context->get_def_use_mgr()->NumUses(19)); + ASSERT_EQ(num_uses_of_synonym_before_replacement + 1, + context->get_def_use_mgr()->NumUses(210)); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); diff --git a/test/fuzz/transformation_set_loop_control_test.cpp b/test/fuzz/transformation_set_loop_control_test.cpp index 3312a679..88b4aab5 100644 --- a/test/fuzz/transformation_set_loop_control_test.cpp +++ b/test/fuzz/transformation_set_loop_control_test.cpp @@ -947,7 +947,9 @@ TEST(TransformationSetLoopControlTest, CheckSPIRVVersionsRespected) { for (auto env : {SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_2, - SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_5}) { + SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_5, + SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_1_SPIRV_1_4, + SPV_ENV_VULKAN_1_2}) { const auto consumer = nullptr; const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); @@ -956,23 +958,33 @@ TEST(TransformationSetLoopControlTest, CheckSPIRVVersionsRespected) { context.get(), validator_options, kConsoleMessageConsumer)); TransformationContext transformation_context( MakeUnique<FactManager>(context.get()), validator_options); - TransformationSetLoopControl transformation( - 10, SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask, 4, 4); + TransformationSetLoopControl peel_count(10, SpvLoopControlPeelCountMask, 4, + 0); + TransformationSetLoopControl partial_count( + 10, SpvLoopControlPartialCountMask, 0, 4); switch (env) { 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_VULKAN_1_0: + case SPV_ENV_VULKAN_1_1: // PeelCount and PartialCount were introduced in SPIRV 1.4, so are not // valid in the context of older versions. ASSERT_FALSE( - transformation.IsApplicable(context.get(), transformation_context)); + peel_count.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + partial_count.IsApplicable(context.get(), transformation_context)); break; case SPV_ENV_UNIVERSAL_1_4: case SPV_ENV_UNIVERSAL_1_5: + case SPV_ENV_VULKAN_1_1_SPIRV_1_4: + case SPV_ENV_VULKAN_1_2: + ASSERT_TRUE( + peel_count.IsApplicable(context.get(), transformation_context)); ASSERT_TRUE( - transformation.IsApplicable(context.get(), transformation_context)); + partial_count.IsApplicable(context.get(), transformation_context)); break; default: assert(false && "Unhandled environment"); diff --git a/test/fuzz/transformation_set_memory_operands_mask_test.cpp b/test/fuzz/transformation_set_memory_operands_mask_test.cpp index 4763d7ad..29e57f42 100644 --- a/test/fuzz/transformation_set_memory_operands_mask_test.cpp +++ b/test/fuzz/transformation_set_memory_operands_mask_test.cpp @@ -90,187 +90,205 @@ TEST(TransformationSetMemoryOperandsMaskTest, PreSpirv14) { OpFunctionEnd )"; - const auto env = SPV_ENV_UNIVERSAL_1_3; - const auto consumer = nullptr; - const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); - spvtools::ValidatorOptions validator_options; - ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, - kConsoleMessageConsumer)); - TransformationContext transformation_context( - MakeUnique<FactManager>(context.get()), validator_options); - // Not OK: the instruction is not a memory access. - ASSERT_FALSE(TransformationSetMemoryOperandsMask( - MakeInstructionDescriptor(21, SpvOpAccessChain, 0), - SpvMemoryAccessMaskNone, 0) - .IsApplicable(context.get(), transformation_context)); + for (auto env : + {SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_2, + SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1}) { + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique<FactManager>(context.get()), validator_options); - // Not OK to remove Aligned - ASSERT_FALSE(TransformationSetMemoryOperandsMask( - MakeInstructionDescriptor(147, SpvOpLoad, 0), - SpvMemoryAccessVolatileMask | SpvMemoryAccessNontemporalMask, - 0) - .IsApplicable(context.get(), transformation_context)); +#ifndef NDEBUG + { + // Not OK: multiple operands are not supported pre SPIR-V 1.4. + TransformationSetMemoryOperandsMask transformation( + MakeInstructionDescriptor(21, SpvOpCopyMemory, 3), + SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 1); + ASSERT_DEATH( + transformation.IsApplicable(context.get(), transformation_context), + "Multiple memory operand masks are not supported"); + } +#endif - { - TransformationSetMemoryOperandsMask transformation( - MakeInstructionDescriptor(147, SpvOpLoad, 0), - SpvMemoryAccessAlignedMask | SpvMemoryAccessVolatileMask, 0); - ASSERT_TRUE( - transformation.IsApplicable(context.get(), transformation_context)); - ApplyAndCheckFreshIds(transformation, context.get(), - &transformation_context); - } + // Not OK: the instruction is not a memory access. + ASSERT_FALSE(TransformationSetMemoryOperandsMask( + MakeInstructionDescriptor(21, SpvOpAccessChain, 0), + SpvMemoryAccessMaskNone, 0) + .IsApplicable(context.get(), transformation_context)); - // Not OK to remove Aligned - ASSERT_FALSE(TransformationSetMemoryOperandsMask( - MakeInstructionDescriptor(21, SpvOpCopyMemory, 0), - SpvMemoryAccessMaskNone, 0) - .IsApplicable(context.get(), transformation_context)); + // Not OK to remove Aligned + ASSERT_FALSE( + TransformationSetMemoryOperandsMask( + MakeInstructionDescriptor(147, SpvOpLoad, 0), + SpvMemoryAccessVolatileMask | SpvMemoryAccessNontemporalMask, 0) + .IsApplicable(context.get(), transformation_context)); - // OK: leaves the mask as is - ASSERT_TRUE(TransformationSetMemoryOperandsMask( - MakeInstructionDescriptor(21, SpvOpCopyMemory, 0), - SpvMemoryAccessAlignedMask, 0) - .IsApplicable(context.get(), transformation_context)); + { + TransformationSetMemoryOperandsMask transformation( + MakeInstructionDescriptor(147, SpvOpLoad, 0), + SpvMemoryAccessAlignedMask | SpvMemoryAccessVolatileMask, 0); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } - { - // OK: adds Nontemporal and Volatile - TransformationSetMemoryOperandsMask transformation( - MakeInstructionDescriptor(21, SpvOpCopyMemory, 0), - SpvMemoryAccessAlignedMask | SpvMemoryAccessNontemporalMask | - SpvMemoryAccessVolatileMask, - 0); - ASSERT_TRUE( - transformation.IsApplicable(context.get(), transformation_context)); - ApplyAndCheckFreshIds(transformation, context.get(), - &transformation_context); - } + // Not OK to remove Aligned + ASSERT_FALSE(TransformationSetMemoryOperandsMask( + MakeInstructionDescriptor(21, SpvOpCopyMemory, 0), + SpvMemoryAccessMaskNone, 0) + .IsApplicable(context.get(), transformation_context)); - // Not OK to remove Volatile - ASSERT_FALSE(TransformationSetMemoryOperandsMask( - MakeInstructionDescriptor(21, SpvOpCopyMemory, 1), - SpvMemoryAccessNontemporalMask, 0) - .IsApplicable(context.get(), transformation_context)); + // OK: leaves the mask as is + ASSERT_TRUE(TransformationSetMemoryOperandsMask( + MakeInstructionDescriptor(21, SpvOpCopyMemory, 0), + SpvMemoryAccessAlignedMask, 0) + .IsApplicable(context.get(), transformation_context)); - // Not OK to add Aligned - ASSERT_FALSE(TransformationSetMemoryOperandsMask( - MakeInstructionDescriptor(21, SpvOpCopyMemory, 1), - SpvMemoryAccessAlignedMask | SpvMemoryAccessVolatileMask, 0) - .IsApplicable(context.get(), transformation_context)); + { + // OK: adds Nontemporal and Volatile + TransformationSetMemoryOperandsMask transformation( + MakeInstructionDescriptor(21, SpvOpCopyMemory, 0), + SpvMemoryAccessAlignedMask | SpvMemoryAccessNontemporalMask | + SpvMemoryAccessVolatileMask, + 0); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } - { - // OK: adds Nontemporal - TransformationSetMemoryOperandsMask transformation( - MakeInstructionDescriptor(21, SpvOpCopyMemory, 1), - SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0); - ASSERT_TRUE( - transformation.IsApplicable(context.get(), transformation_context)); - ApplyAndCheckFreshIds(transformation, context.get(), - &transformation_context); - } + // Not OK to remove Volatile + ASSERT_FALSE(TransformationSetMemoryOperandsMask( + MakeInstructionDescriptor(21, SpvOpCopyMemory, 1), + SpvMemoryAccessNontemporalMask, 0) + .IsApplicable(context.get(), transformation_context)); - { - // OK: adds Nontemporal (creates new operand) - TransformationSetMemoryOperandsMask transformation( - MakeInstructionDescriptor(21, SpvOpCopyMemory, 2), - SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0); - ASSERT_TRUE( - transformation.IsApplicable(context.get(), transformation_context)); - ApplyAndCheckFreshIds(transformation, context.get(), - &transformation_context); - } + // Not OK to add Aligned + ASSERT_FALSE(TransformationSetMemoryOperandsMask( + MakeInstructionDescriptor(21, SpvOpCopyMemory, 1), + SpvMemoryAccessAlignedMask | SpvMemoryAccessVolatileMask, + 0) + .IsApplicable(context.get(), transformation_context)); - { - // OK: adds Nontemporal and Volatile - TransformationSetMemoryOperandsMask transformation( - MakeInstructionDescriptor(138, SpvOpCopyMemory, 0), - SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0); - ASSERT_TRUE( - transformation.IsApplicable(context.get(), transformation_context)); - ApplyAndCheckFreshIds(transformation, context.get(), - &transformation_context); - } + { + // OK: adds Nontemporal + TransformationSetMemoryOperandsMask transformation( + MakeInstructionDescriptor(21, SpvOpCopyMemory, 1), + SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } - { - // OK: removes Nontemporal, adds Volatile - TransformationSetMemoryOperandsMask transformation( - MakeInstructionDescriptor(148, SpvOpStore, 0), - SpvMemoryAccessVolatileMask, 0); - ASSERT_TRUE( - transformation.IsApplicable(context.get(), transformation_context)); - ApplyAndCheckFreshIds(transformation, context.get(), - &transformation_context); - } + { + // OK: adds Nontemporal (creates new operand) + TransformationSetMemoryOperandsMask transformation( + MakeInstructionDescriptor(21, SpvOpCopyMemory, 2), + SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } - std::string after_transformation = R"( - OpCapability Shader - %1 = OpExtInstImport "GLSL.std.450" - OpMemoryModel Logical GLSL450 - OpEntryPoint Fragment %4 "main" - OpExecutionMode %4 OriginUpperLeft - OpSource ESSL 310 - OpName %4 "main" - OpName %7 "Point3D" - OpMemberName %7 0 "x" - OpMemberName %7 1 "y" - OpMemberName %7 2 "z" - OpName %12 "global_points" - OpName %15 "block" - OpMemberName %15 0 "in_points" - OpMemberName %15 1 "in_point" - OpName %17 "" - OpName %133 "local_points" - OpMemberDecorate %7 0 Offset 0 - OpMemberDecorate %7 1 Offset 4 - OpMemberDecorate %7 2 Offset 8 - OpDecorate %10 ArrayStride 16 - OpMemberDecorate %15 0 Offset 0 - OpMemberDecorate %15 1 Offset 192 - OpDecorate %15 Block - OpDecorate %17 DescriptorSet 0 - OpDecorate %17 Binding 0 - %2 = OpTypeVoid - %3 = OpTypeFunction %2 - %6 = OpTypeFloat 32 - %7 = OpTypeStruct %6 %6 %6 - %8 = OpTypeInt 32 0 - %9 = OpConstant %8 12 - %10 = OpTypeArray %7 %9 - %11 = OpTypePointer Private %10 - %12 = OpVariable %11 Private - %15 = OpTypeStruct %10 %7 - %16 = OpTypePointer Uniform %15 - %17 = OpVariable %16 Uniform - %18 = OpTypeInt 32 1 - %19 = OpConstant %18 0 - %20 = OpTypePointer Uniform %10 - %24 = OpTypePointer Private %7 - %27 = OpTypePointer Private %6 - %30 = OpConstant %18 1 - %132 = OpTypePointer Function %10 - %135 = OpTypePointer Uniform %7 - %145 = OpTypePointer Function %7 - %4 = OpFunction %2 None %3 - %5 = OpLabel - %133 = OpVariable %132 Function - %21 = OpAccessChain %20 %17 %19 - OpCopyMemory %12 %21 Aligned|Nontemporal|Volatile 16 - OpCopyMemory %133 %12 Nontemporal|Volatile - OpCopyMemory %133 %12 Nontemporal|Volatile - %136 = OpAccessChain %135 %17 %30 - %138 = OpAccessChain %24 %12 %19 - OpCopyMemory %138 %136 Nontemporal|Volatile - %146 = OpAccessChain %145 %133 %30 - %147 = OpLoad %7 %146 Aligned|Volatile 16 - %148 = OpAccessChain %24 %12 %19 - OpStore %148 %147 Volatile - OpReturn - OpFunctionEnd - )"; - ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); + { + // OK: adds Nontemporal and Volatile + TransformationSetMemoryOperandsMask transformation( + MakeInstructionDescriptor(138, SpvOpCopyMemory, 0), + SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + + { + // OK: removes Nontemporal, adds Volatile + TransformationSetMemoryOperandsMask transformation( + MakeInstructionDescriptor(148, SpvOpStore, 0), + SpvMemoryAccessVolatileMask, 0); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %7 "Point3D" + OpMemberName %7 0 "x" + OpMemberName %7 1 "y" + OpMemberName %7 2 "z" + OpName %12 "global_points" + OpName %15 "block" + OpMemberName %15 0 "in_points" + OpMemberName %15 1 "in_point" + OpName %17 "" + OpName %133 "local_points" + OpMemberDecorate %7 0 Offset 0 + OpMemberDecorate %7 1 Offset 4 + OpMemberDecorate %7 2 Offset 8 + OpDecorate %10 ArrayStride 16 + OpMemberDecorate %15 0 Offset 0 + OpMemberDecorate %15 1 Offset 192 + OpDecorate %15 Block + OpDecorate %17 DescriptorSet 0 + OpDecorate %17 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeStruct %6 %6 %6 + %8 = OpTypeInt 32 0 + %9 = OpConstant %8 12 + %10 = OpTypeArray %7 %9 + %11 = OpTypePointer Private %10 + %12 = OpVariable %11 Private + %15 = OpTypeStruct %10 %7 + %16 = OpTypePointer Uniform %15 + %17 = OpVariable %16 Uniform + %18 = OpTypeInt 32 1 + %19 = OpConstant %18 0 + %20 = OpTypePointer Uniform %10 + %24 = OpTypePointer Private %7 + %27 = OpTypePointer Private %6 + %30 = OpConstant %18 1 + %132 = OpTypePointer Function %10 + %135 = OpTypePointer Uniform %7 + %145 = OpTypePointer Function %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %133 = OpVariable %132 Function + %21 = OpAccessChain %20 %17 %19 + OpCopyMemory %12 %21 Aligned|Nontemporal|Volatile 16 + OpCopyMemory %133 %12 Nontemporal|Volatile + OpCopyMemory %133 %12 Nontemporal|Volatile + %136 = OpAccessChain %135 %17 %30 + %138 = OpAccessChain %24 %12 %19 + OpCopyMemory %138 %136 Nontemporal|Volatile + %146 = OpAccessChain %145 %133 %30 + %147 = OpLoad %7 %146 Aligned|Volatile 16 + %148 = OpAccessChain %24 %12 %19 + OpStore %148 %147 Volatile + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); + } } -TEST(TransformationSetMemoryOperandsMaskTest, Spirv14) { +TEST(TransformationSetMemoryOperandsMaskTest, Spirv14OrHigher) { std::string shader = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" @@ -339,180 +357,183 @@ TEST(TransformationSetMemoryOperandsMaskTest, Spirv14) { OpFunctionEnd )"; - const auto env = SPV_ENV_UNIVERSAL_1_4; - const auto consumer = nullptr; - const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); - spvtools::ValidatorOptions validator_options; - ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, - kConsoleMessageConsumer)); - TransformationContext transformation_context( - MakeUnique<FactManager>(context.get()), validator_options); - { - TransformationSetMemoryOperandsMask transformation( - MakeInstructionDescriptor(21, SpvOpCopyMemory, 0), - SpvMemoryAccessAlignedMask | SpvMemoryAccessVolatileMask, 1); - // Bad: cannot remove aligned - ASSERT_FALSE(TransformationSetMemoryOperandsMask( - MakeInstructionDescriptor(21, SpvOpCopyMemory, 0), - SpvMemoryAccessVolatileMask, 1) - .IsApplicable(context.get(), transformation_context)); - ASSERT_TRUE( - transformation.IsApplicable(context.get(), transformation_context)); - ApplyAndCheckFreshIds(transformation, context.get(), - &transformation_context); - } + for (auto env : {SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_5, + SPV_ENV_VULKAN_1_1_SPIRV_1_4, SPV_ENV_VULKAN_1_2}) { + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique<FactManager>(context.get()), validator_options); + { + TransformationSetMemoryOperandsMask transformation( + MakeInstructionDescriptor(21, SpvOpCopyMemory, 0), + SpvMemoryAccessAlignedMask | SpvMemoryAccessVolatileMask, 1); + // Bad: cannot remove aligned + ASSERT_FALSE(TransformationSetMemoryOperandsMask( + MakeInstructionDescriptor(21, SpvOpCopyMemory, 0), + SpvMemoryAccessVolatileMask, 1) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } - { - TransformationSetMemoryOperandsMask transformation( - MakeInstructionDescriptor(21, SpvOpCopyMemory, 1), - SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 1); - // Bad: cannot remove volatile - ASSERT_FALSE(TransformationSetMemoryOperandsMask( - MakeInstructionDescriptor(21, SpvOpCopyMemory, 1), - SpvMemoryAccessNontemporalMask, 0) - .IsApplicable(context.get(), transformation_context)); - ASSERT_TRUE( - transformation.IsApplicable(context.get(), transformation_context)); - ApplyAndCheckFreshIds(transformation, context.get(), - &transformation_context); - } + { + TransformationSetMemoryOperandsMask transformation( + MakeInstructionDescriptor(21, SpvOpCopyMemory, 1), + SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 1); + // Bad: cannot remove volatile + ASSERT_FALSE(TransformationSetMemoryOperandsMask( + MakeInstructionDescriptor(21, SpvOpCopyMemory, 1), + SpvMemoryAccessNontemporalMask, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } - { - // Creates the first operand. - TransformationSetMemoryOperandsMask transformation( - MakeInstructionDescriptor(21, SpvOpCopyMemory, 2), - SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0); - ASSERT_TRUE( - transformation.IsApplicable(context.get(), transformation_context)); - ApplyAndCheckFreshIds(transformation, context.get(), - &transformation_context); - } + { + // Creates the first operand. + TransformationSetMemoryOperandsMask transformation( + MakeInstructionDescriptor(21, SpvOpCopyMemory, 2), + SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } - { - // Creates both operands. - TransformationSetMemoryOperandsMask transformation( - MakeInstructionDescriptor(21, SpvOpCopyMemory, 3), - SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 1); - ASSERT_TRUE( - transformation.IsApplicable(context.get(), transformation_context)); - ApplyAndCheckFreshIds(transformation, context.get(), - &transformation_context); - } + { + // Creates both operands. + TransformationSetMemoryOperandsMask transformation( + MakeInstructionDescriptor(21, SpvOpCopyMemory, 3), + SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 1); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } - { - TransformationSetMemoryOperandsMask transformation( - MakeInstructionDescriptor(138, SpvOpCopyMemory, 0), - SpvMemoryAccessAlignedMask | SpvMemoryAccessNontemporalMask, 1); - // Bad: the first mask is None, so Aligned cannot be added to it. - ASSERT_FALSE( - TransformationSetMemoryOperandsMask( - MakeInstructionDescriptor(138, SpvOpCopyMemory, 0), - SpvMemoryAccessAlignedMask | SpvMemoryAccessNontemporalMask, 0) - .IsApplicable(context.get(), transformation_context)); - ASSERT_TRUE( - transformation.IsApplicable(context.get(), transformation_context)); - ApplyAndCheckFreshIds(transformation, context.get(), - &transformation_context); - } + { + TransformationSetMemoryOperandsMask transformation( + MakeInstructionDescriptor(138, SpvOpCopyMemory, 0), + SpvMemoryAccessAlignedMask | SpvMemoryAccessNontemporalMask, 1); + // Bad: the first mask is None, so Aligned cannot be added to it. + ASSERT_FALSE( + TransformationSetMemoryOperandsMask( + MakeInstructionDescriptor(138, SpvOpCopyMemory, 0), + SpvMemoryAccessAlignedMask | SpvMemoryAccessNontemporalMask, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } - { - TransformationSetMemoryOperandsMask transformation( - MakeInstructionDescriptor(138, SpvOpCopyMemory, 1), - SpvMemoryAccessVolatileMask, 1); - ASSERT_TRUE( - transformation.IsApplicable(context.get(), transformation_context)); - ApplyAndCheckFreshIds(transformation, context.get(), - &transformation_context); - } + { + TransformationSetMemoryOperandsMask transformation( + MakeInstructionDescriptor(138, SpvOpCopyMemory, 1), + SpvMemoryAccessVolatileMask, 1); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } - { - TransformationSetMemoryOperandsMask transformation( - MakeInstructionDescriptor(147, SpvOpLoad, 0), - SpvMemoryAccessVolatileMask | SpvMemoryAccessAlignedMask, 0); - ASSERT_TRUE( - transformation.IsApplicable(context.get(), transformation_context)); - ApplyAndCheckFreshIds(transformation, context.get(), - &transformation_context); - } + { + TransformationSetMemoryOperandsMask transformation( + MakeInstructionDescriptor(147, SpvOpLoad, 0), + SpvMemoryAccessVolatileMask | SpvMemoryAccessAlignedMask, 0); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } - { - TransformationSetMemoryOperandsMask transformation( - MakeInstructionDescriptor(148, SpvOpStore, 0), SpvMemoryAccessMaskNone, - 0); - ASSERT_TRUE( - transformation.IsApplicable(context.get(), transformation_context)); - ApplyAndCheckFreshIds(transformation, context.get(), - &transformation_context); - } + { + TransformationSetMemoryOperandsMask transformation( + MakeInstructionDescriptor(148, SpvOpStore, 0), + SpvMemoryAccessMaskNone, 0); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } - std::string after_transformation = R"( - OpCapability Shader - %1 = OpExtInstImport "GLSL.std.450" - OpMemoryModel Logical GLSL450 - OpEntryPoint Fragment %4 "main" %12 %17 - OpExecutionMode %4 OriginUpperLeft - OpSource ESSL 310 - OpName %4 "main" - OpName %7 "Point3D" - OpMemberName %7 0 "x" - OpMemberName %7 1 "y" - OpMemberName %7 2 "z" - OpName %12 "global_points" - OpName %15 "block" - OpMemberName %15 0 "in_points" - OpMemberName %15 1 "in_point" - OpName %17 "" - OpName %133 "local_points" - OpMemberDecorate %7 0 Offset 0 - OpMemberDecorate %7 1 Offset 4 - OpMemberDecorate %7 2 Offset 8 - OpDecorate %10 ArrayStride 16 - OpMemberDecorate %15 0 Offset 0 - OpMemberDecorate %15 1 Offset 192 - OpDecorate %15 Block - OpDecorate %17 DescriptorSet 0 - OpDecorate %17 Binding 0 - %2 = OpTypeVoid - %3 = OpTypeFunction %2 - %6 = OpTypeFloat 32 - %7 = OpTypeStruct %6 %6 %6 - %8 = OpTypeInt 32 0 - %9 = OpConstant %8 12 - %10 = OpTypeArray %7 %9 - %11 = OpTypePointer Private %10 - %12 = OpVariable %11 Private - %15 = OpTypeStruct %10 %7 - %16 = OpTypePointer Uniform %15 - %17 = OpVariable %16 Uniform - %18 = OpTypeInt 32 1 - %19 = OpConstant %18 0 - %20 = OpTypePointer Uniform %10 - %24 = OpTypePointer Private %7 - %27 = OpTypePointer Private %6 - %30 = OpConstant %18 1 - %132 = OpTypePointer Function %10 - %135 = OpTypePointer Uniform %7 - %145 = OpTypePointer Function %7 - %4 = OpFunction %2 None %3 - %5 = OpLabel - %133 = OpVariable %132 Function - %21 = OpAccessChain %20 %17 %19 - OpCopyMemory %12 %21 Aligned 16 Aligned|Volatile 16 - OpCopyMemory %133 %12 Volatile Nontemporal|Volatile - OpCopyMemory %133 %12 Nontemporal|Volatile - OpCopyMemory %133 %12 None Nontemporal|Volatile - %136 = OpAccessChain %135 %17 %30 - %138 = OpAccessChain %24 %12 %19 - OpCopyMemory %138 %136 None Aligned|Nontemporal 16 - OpCopyMemory %138 %136 Aligned 16 Volatile - %146 = OpAccessChain %145 %133 %30 - %147 = OpLoad %7 %146 Volatile|Aligned 16 - %148 = OpAccessChain %24 %12 %19 - OpStore %148 %147 None - OpReturn - OpFunctionEnd - )"; - ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %12 %17 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %7 "Point3D" + OpMemberName %7 0 "x" + OpMemberName %7 1 "y" + OpMemberName %7 2 "z" + OpName %12 "global_points" + OpName %15 "block" + OpMemberName %15 0 "in_points" + OpMemberName %15 1 "in_point" + OpName %17 "" + OpName %133 "local_points" + OpMemberDecorate %7 0 Offset 0 + OpMemberDecorate %7 1 Offset 4 + OpMemberDecorate %7 2 Offset 8 + OpDecorate %10 ArrayStride 16 + OpMemberDecorate %15 0 Offset 0 + OpMemberDecorate %15 1 Offset 192 + OpDecorate %15 Block + OpDecorate %17 DescriptorSet 0 + OpDecorate %17 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeStruct %6 %6 %6 + %8 = OpTypeInt 32 0 + %9 = OpConstant %8 12 + %10 = OpTypeArray %7 %9 + %11 = OpTypePointer Private %10 + %12 = OpVariable %11 Private + %15 = OpTypeStruct %10 %7 + %16 = OpTypePointer Uniform %15 + %17 = OpVariable %16 Uniform + %18 = OpTypeInt 32 1 + %19 = OpConstant %18 0 + %20 = OpTypePointer Uniform %10 + %24 = OpTypePointer Private %7 + %27 = OpTypePointer Private %6 + %30 = OpConstant %18 1 + %132 = OpTypePointer Function %10 + %135 = OpTypePointer Uniform %7 + %145 = OpTypePointer Function %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %133 = OpVariable %132 Function + %21 = OpAccessChain %20 %17 %19 + OpCopyMemory %12 %21 Aligned 16 Aligned|Volatile 16 + OpCopyMemory %133 %12 Volatile Nontemporal|Volatile + OpCopyMemory %133 %12 Nontemporal|Volatile + OpCopyMemory %133 %12 None Nontemporal|Volatile + %136 = OpAccessChain %135 %17 %30 + %138 = OpAccessChain %24 %12 %19 + OpCopyMemory %138 %136 None Aligned|Nontemporal 16 + OpCopyMemory %138 %136 Aligned 16 Volatile + %146 = OpAccessChain %145 %133 %30 + %147 = OpLoad %7 %146 Volatile|Aligned 16 + %148 = OpAccessChain %24 %12 %19 + OpStore %148 %147 None + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); + } } } // namespace diff --git a/test/fuzz/transformation_swap_conditional_branch_operands_test.cpp b/test/fuzz/transformation_swap_conditional_branch_operands_test.cpp index e7a8732e..6133a7a8 100644 --- a/test/fuzz/transformation_swap_conditional_branch_operands_test.cpp +++ b/test/fuzz/transformation_swap_conditional_branch_operands_test.cpp @@ -91,9 +91,31 @@ TEST(TransformationSwapConditionalBranchOperandsTest, BasicTest) { TransformationSwapConditionalBranchOperands transformation( MakeInstructionDescriptor(15, SpvOpBranchConditional, 0), 26); + ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(26)); + ASSERT_EQ(nullptr, context->get_instr_block(26)); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_EQ(SpvOpLogicalNot, context->get_def_use_mgr()->GetDef(26)->opcode()); + ASSERT_EQ(5, context->get_instr_block(26)->id()); + ASSERT_EQ(1, context->get_def_use_mgr()->NumUses(26)); + + // Check that the def-use manager knows that the conditional branch operands + // have been swapped. + std::vector<std::pair<uint32_t, uint32_t>> phi_operand_to_new_operand_index = + {{16, 2}, {21, 1}}; + for (std::pair<uint32_t, uint32_t>& entry : + phi_operand_to_new_operand_index) { + context->get_def_use_mgr()->WhileEachUse( + entry.first, + [&entry](opt::Instruction* inst, uint32_t operand_index) -> bool { + if (inst->opcode() == SpvOpBranchConditional) { + EXPECT_EQ(entry.second, operand_index); + return false; + } + return true; + }); + } std::string after_transformation = R"( OpCapability Shader diff --git a/test/fuzz/transformation_swap_function_variables_test.cpp b/test/fuzz/transformation_swap_function_variables_test.cpp new file mode 100644 index 00000000..8132aa4b --- /dev/null +++ b/test/fuzz/transformation_swap_function_variables_test.cpp @@ -0,0 +1,288 @@ +// Copyright (c) 2021 Mostafa Ashraf +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_swap_function_variables.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationSwapFunctionVariables, NotApplicable) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFloat 32 + %9 = OpTypePointer Function %8 + %10 = OpTypeVector %8 2 + %11 = OpTypePointer Function %10 + %12 = OpTypeVector %8 3 + %13 = OpTypeMatrix %12 3 + %14 = OpTypePointer Function %13 + %15 = OpTypeFunction %2 %7 %9 %11 %14 %7 %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %24 = OpVariable %7 Function + %25 = OpVariable %9 Function + %26 = OpVariable %11 Function + %27 = OpVariable %14 Function + %28 = OpVariable %7 Function + %29 = OpVariable %7 Function + %30 = OpVariable %7 Function + %32 = OpVariable %9 Function + %34 = OpVariable %11 Function + %36 = OpVariable %14 Function + %38 = OpVariable %7 Function + %40 = OpVariable %7 Function + %31 = OpLoad %6 %24 + OpStore %30 %31 + %33 = OpLoad %8 %25 + OpStore %32 %33 + %35 = OpLoad %10 %26 + OpStore %34 %35 + %37 = OpLoad %13 %27 + OpStore %36 %37 + %39 = OpLoad %6 %28 + OpStore %38 %39 + %41 = OpLoad %6 %29 + OpStore %40 %41 + %42 = OpFunctionCall %2 %22 %30 %32 %34 %36 %38 %40 + OpReturn + OpFunctionEnd + %22 = OpFunction %2 None %15 + %16 = OpFunctionParameter %7 + %17 = OpFunctionParameter %9 + %18 = OpFunctionParameter %11 + %19 = OpFunctionParameter %14 + %20 = OpFunctionParameter %7 + %21 = OpFunctionParameter %7 + %23 = OpLabel + OpReturn + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique<FactManager>(context.get()), validator_options); + +#ifndef NDEBUG + // Can't swap variable with itself. + ASSERT_DEATH(TransformationSwapFunctionVariables(7, 7).IsApplicable( + context.get(), transformation_context), + "Two results ids are equal"); +#endif + + // Invalid because 200 is not the id of an instruction. + ASSERT_FALSE(TransformationSwapFunctionVariables(1, 200).IsApplicable( + context.get(), transformation_context)); + // Invalid because 5 is not the id of an instruction. + ASSERT_FALSE(TransformationSwapFunctionVariables(5, 24).IsApplicable( + context.get(), transformation_context)); + // Can't swap two instructions from two different blocks. + ASSERT_FALSE(TransformationSwapFunctionVariables(16, 26).IsApplicable( + context.get(), transformation_context)); +} + +TEST(TransformationSwapFunctionVariables, IsApplicable) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFloat 32 + %9 = OpTypePointer Function %8 + %10 = OpTypeVector %8 2 + %11 = OpTypePointer Function %10 + %12 = OpTypeVector %8 3 + %13 = OpTypeMatrix %12 3 + %14 = OpTypePointer Function %13 + %15 = OpTypeFunction %2 %7 %9 %11 %14 %7 %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %24 = OpVariable %7 Function + %25 = OpVariable %9 Function + %26 = OpVariable %11 Function + %27 = OpVariable %14 Function + %28 = OpVariable %7 Function + %29 = OpVariable %7 Function + %30 = OpVariable %7 Function + %32 = OpVariable %9 Function + %34 = OpVariable %11 Function + %36 = OpVariable %14 Function + %38 = OpVariable %7 Function + %40 = OpVariable %7 Function + %31 = OpLoad %6 %24 + OpStore %30 %31 + %33 = OpLoad %8 %25 + OpStore %32 %33 + %35 = OpLoad %10 %26 + OpStore %34 %35 + %37 = OpLoad %13 %27 + OpStore %36 %37 + %39 = OpLoad %6 %28 + OpStore %38 %39 + %41 = OpLoad %6 %29 + OpStore %40 %41 + %42 = OpFunctionCall %2 %22 %30 %32 %34 %36 %38 %40 + OpReturn + OpFunctionEnd + %22 = OpFunction %2 None %15 + %16 = OpFunctionParameter %7 + %17 = OpFunctionParameter %9 + %18 = OpFunctionParameter %11 + %19 = OpFunctionParameter %14 + %20 = OpFunctionParameter %7 + %21 = OpFunctionParameter %7 + %23 = OpLabel + OpReturn + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + // Get Unique pointer of IRContext. + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique<FactManager>(context.get()), validator_options); + + // Successful transformations + { + auto first_instruction = context->get_def_use_mgr()->GetDef(24); + auto second_instruction = context->get_def_use_mgr()->GetDef(28); + // Swap two OpVariable instructions in the same function. + TransformationSwapFunctionVariables transformation(24, 28); + + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + + ASSERT_EQ(first_instruction, context->get_def_use_mgr()->GetDef(24)); + ASSERT_EQ(second_instruction, context->get_def_use_mgr()->GetDef(28)); + } + { + auto first_instruction = context->get_def_use_mgr()->GetDef(38); + auto second_instruction = context->get_def_use_mgr()->GetDef(40); + // Swap two OpVariable instructions in the same function. + TransformationSwapFunctionVariables transformation(38, 40); + + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + + ASSERT_EQ(first_instruction, context->get_def_use_mgr()->GetDef(38)); + ASSERT_EQ(second_instruction, context->get_def_use_mgr()->GetDef(40)); + } + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFloat 32 + %9 = OpTypePointer Function %8 + %10 = OpTypeVector %8 2 + %11 = OpTypePointer Function %10 + %12 = OpTypeVector %8 3 + %13 = OpTypeMatrix %12 3 + %14 = OpTypePointer Function %13 + %15 = OpTypeFunction %2 %7 %9 %11 %14 %7 %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %28 = OpVariable %7 Function + %25 = OpVariable %9 Function + %26 = OpVariable %11 Function + %27 = OpVariable %14 Function + %24 = OpVariable %7 Function + %29 = OpVariable %7 Function + %30 = OpVariable %7 Function + %32 = OpVariable %9 Function + %34 = OpVariable %11 Function + %36 = OpVariable %14 Function + %40 = OpVariable %7 Function + %38 = OpVariable %7 Function + %31 = OpLoad %6 %24 + OpStore %30 %31 + %33 = OpLoad %8 %25 + OpStore %32 %33 + %35 = OpLoad %10 %26 + OpStore %34 %35 + %37 = OpLoad %13 %27 + OpStore %36 %37 + %39 = OpLoad %6 %28 + OpStore %38 %39 + %41 = OpLoad %6 %29 + OpStore %40 %41 + %42 = OpFunctionCall %2 %22 %30 %32 %34 %36 %38 %40 + OpReturn + OpFunctionEnd + %22 = OpFunction %2 None %15 + %16 = OpFunctionParameter %7 + %17 = OpFunctionParameter %9 + %18 = OpFunctionParameter %11 + %19 = OpFunctionParameter %14 + %20 = OpFunctionParameter %7 + %21 = OpFunctionParameter %7 + %23 = OpLabel + OpReturn + OpFunctionEnd +)"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/test/fuzz/transformation_swap_two_functions_test.cpp b/test/fuzz/transformation_swap_two_functions_test.cpp new file mode 100644 index 00000000..38d6a076 --- /dev/null +++ b/test/fuzz/transformation_swap_two_functions_test.cpp @@ -0,0 +1,244 @@ +// Copyright (c) 2021 Shiyu Liu +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_swap_two_functions.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationSwapTwoFunctionsTest, SimpleTest) { + // float multiplyBy2(in float value) { + // return value*2.0; + // } + + // float multiplyBy4(in float value) { + // return multiplyBy2(value)*2.0; + // } + + // float multiplyBy8(in float value) { + // return multiplyBy2(value)*multiplyBy4(value); + // } + + // layout(location=0) in float value; + // void main() { //4 + // multiplyBy2(3.7); //10 + // multiplyBy4(3.9); //13 + // multiplyBy8(5.0); //16 + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %48 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %10 "multiplyBy2(f1;" + OpName %9 "value" + OpName %13 "multiplyBy4(f1;" + OpName %12 "value" + OpName %16 "multiplyBy8(f1;" + OpName %15 "value" + OpName %23 "param" + OpName %29 "param" + OpName %32 "param" + OpName %39 "param" + OpName %42 "param" + OpName %45 "param" + OpName %48 "value" + OpDecorate %48 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %6 %7 + %19 = OpConstant %6 2 + %38 = OpConstant %6 3.70000005 + %41 = OpConstant %6 3.9000001 + %44 = OpConstant %6 5 + %47 = OpTypePointer Input %6 + %48 = OpVariable %47 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %39 = OpVariable %7 Function + %42 = OpVariable %7 Function + %45 = OpVariable %7 Function + OpStore %39 %38 + %40 = OpFunctionCall %6 %10 %39 + OpStore %42 %41 + %43 = OpFunctionCall %6 %13 %42 + OpStore %45 %44 + %46 = OpFunctionCall %6 %16 %45 + OpReturn + OpFunctionEnd + %10 = OpFunction %6 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %18 = OpLoad %6 %9 + %20 = OpFMul %6 %18 %19 + OpReturnValue %20 + OpFunctionEnd + %13 = OpFunction %6 None %8 + %12 = OpFunctionParameter %7 + %14 = OpLabel + %23 = OpVariable %7 Function + %24 = OpLoad %6 %12 + OpStore %23 %24 + %25 = OpFunctionCall %6 %10 %23 + %26 = OpFMul %6 %25 %19 + OpReturnValue %26 + OpFunctionEnd + %16 = OpFunction %6 None %8 + %15 = OpFunctionParameter %7 + %17 = OpLabel + %29 = OpVariable %7 Function + %32 = OpVariable %7 Function + %30 = OpLoad %6 %15 + OpStore %29 %30 + %31 = OpFunctionCall %6 %10 %29 + %33 = OpLoad %6 %15 + OpStore %32 %33 + %34 = OpFunctionCall %6 %13 %32 + %35 = OpFMul %6 %31 %34 + OpReturnValue %35 + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + + // Check context validity. + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique<FactManager>(context.get()), validator_options); + +#ifndef NDEBUG + // Function should not swap with itself. + ASSERT_DEATH(TransformationSwapTwoFunctions(4, 4).IsApplicable( + context.get(), transformation_context), + "The two function ids cannot be the same."); +#endif + + // Function with id 29 does not exist. + ASSERT_FALSE(TransformationSwapTwoFunctions(10, 29).IsApplicable( + context.get(), transformation_context)); + + // Function with id 30 does not exist. + ASSERT_FALSE(TransformationSwapTwoFunctions(30, 13).IsApplicable( + context.get(), transformation_context)); + + // Both functions with id 5 and 6 do not exist. + ASSERT_FALSE(TransformationSwapTwoFunctions(5, 6).IsApplicable( + context.get(), transformation_context)); + + // Function with result_id 10 and 13 should swap successfully. + auto swap_test5 = TransformationSwapTwoFunctions(10, 13); + ASSERT_TRUE(swap_test5.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(swap_test5, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %48 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %10 "multiplyBy2(f1;" + OpName %9 "value" + OpName %13 "multiplyBy4(f1;" + OpName %12 "value" + OpName %16 "multiplyBy8(f1;" + OpName %15 "value" + OpName %23 "param" + OpName %29 "param" + OpName %32 "param" + OpName %39 "param" + OpName %42 "param" + OpName %45 "param" + OpName %48 "value" + OpDecorate %48 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %6 %7 + %19 = OpConstant %6 2 + %38 = OpConstant %6 3.70000005 + %41 = OpConstant %6 3.9000001 + %44 = OpConstant %6 5 + %47 = OpTypePointer Input %6 + %48 = OpVariable %47 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %39 = OpVariable %7 Function + %42 = OpVariable %7 Function + %45 = OpVariable %7 Function + OpStore %39 %38 + %40 = OpFunctionCall %6 %10 %39 + OpStore %42 %41 + %43 = OpFunctionCall %6 %13 %42 + OpStore %45 %44 + %46 = OpFunctionCall %6 %16 %45 + OpReturn + OpFunctionEnd + %13 = OpFunction %6 None %8 + %12 = OpFunctionParameter %7 + %14 = OpLabel + %23 = OpVariable %7 Function + %24 = OpLoad %6 %12 + OpStore %23 %24 + %25 = OpFunctionCall %6 %10 %23 + %26 = OpFMul %6 %25 %19 + OpReturnValue %26 + OpFunctionEnd + %10 = OpFunction %6 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %18 = OpLoad %6 %9 + %20 = OpFMul %6 %18 %19 + OpReturnValue %20 + OpFunctionEnd + %16 = OpFunction %6 None %8 + %15 = OpFunctionParameter %7 + %17 = OpLabel + %29 = OpVariable %7 Function + %32 = OpVariable %7 Function + %30 = OpLoad %6 %15 + OpStore %29 %30 + %31 = OpFunctionCall %6 %10 %29 + %33 = OpLoad %6 %15 + OpStore %32 %33 + %34 = OpFunctionCall %6 %13 %32 + %35 = OpFMul %6 %31 %34 + OpReturnValue %35 + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/test/operand_capabilities_test.cpp b/test/operand_capabilities_test.cpp index 0aec7918..6f83dfeb 100644 --- a/test/operand_capabilities_test.cpp +++ b/test/operand_capabilities_test.cpp @@ -199,7 +199,8 @@ INSTANTIATE_TEST_SUITE_P( CASE1(STORAGE_CLASS, StorageClassOutput, Shader), CASE0(STORAGE_CLASS, StorageClassWorkgroup), CASE0(STORAGE_CLASS, StorageClassCrossWorkgroup), - CASE1(STORAGE_CLASS, StorageClassPrivate, Shader), + CASE2(STORAGE_CLASS, StorageClassPrivate, Shader, + VectorComputeINTEL), CASE0(STORAGE_CLASS, StorageClassFunction), CASE1(STORAGE_CLASS, StorageClassGeneric, GenericPointer), // Bug 14287 diff --git a/test/opt/CMakeLists.txt b/test/opt/CMakeLists.txt index 79cb3fc4..f65d2ff3 100644 --- a/test/opt/CMakeLists.txt +++ b/test/opt/CMakeLists.txt @@ -57,6 +57,7 @@ add_spvtools_unittest(TARGET opt inst_debug_printf_test.cpp instruction_list_test.cpp instruction_test.cpp + interp_fixup_test.cpp ir_builder.cpp ir_context_test.cpp ir_loader_test.cpp diff --git a/test/opt/aggressive_dead_code_elim_test.cpp b/test/opt/aggressive_dead_code_elim_test.cpp index 972e6e57..5b4291da 100644 --- a/test/opt/aggressive_dead_code_elim_test.cpp +++ b/test/opt/aggressive_dead_code_elim_test.cpp @@ -3948,12 +3948,15 @@ OpBranch %12 OpLoopMerge %14 %15 None OpBranch %16 %16 = OpLabel -OpSwitch %13 %14 0 %17 1 %15 +OpSelectionMerge %18 None +OpSwitch %13 %18 0 %17 1 %15 %17 = OpLabel OpStore %3 %uint_1 OpBranch %15 %15 = OpLabel OpBranch %12 +%18 = OpLabel +OpBranch %14 %14 = OpLabel OpStore %3 %uint_0 OpReturn diff --git a/test/opt/block_merge_test.cpp b/test/opt/block_merge_test.cpp index 7381908e..140a5c09 100644 --- a/test/opt/block_merge_test.cpp +++ b/test/opt/block_merge_test.cpp @@ -744,6 +744,7 @@ TEST_F(BlockMergeTest, DontMergeSwitch) { ; CHECK: OpLoopMerge [[merge:%\w+]] [[cont:%\w+]] None ; CHECK-NEXT: OpBranch [[ret:%\w+]] ; CHECK: [[ret:%\w+]] = OpLabel +; CHECK-NEXT: OpSelectionMerge ; CHECK-NEXT: OpSwitch ; CHECK-DAG: [[cont]] = OpLabel ; CHECK-DAG: [[merge]] = OpLabel @@ -763,6 +764,7 @@ OpBranch %2 OpLoopMerge %3 %4 None OpBranch %5 %5 = OpLabel +OpSelectionMerge %6 None OpSwitch %int_0 %6 %6 = OpLabel OpReturn diff --git a/test/opt/dead_branch_elim_test.cpp b/test/opt/dead_branch_elim_test.cpp index 41ce31da..f89befb4 100644 --- a/test/opt/dead_branch_elim_test.cpp +++ b/test/opt/dead_branch_elim_test.cpp @@ -2722,6 +2722,7 @@ OpSource GLSL 140 ; CHECK: [[bb1]] = OpLabel ; CHECK-NEXT: OpBranch [[bb2:%\w+]] ; CHECK: [[bb2]] = OpLabel +; CHECK-NEXT: OpSelectionMerge ; CHECK-NEXT: OpSwitch {{%\w+}} [[bb3:%\w+]] 0 [[loop_merge]] 1 [[bb3:%\w+]] ; CHECK: [[bb3]] = OpLabel ; CHECK-NEXT: OpBranch [[sel_merge:%\w+]] @@ -2739,6 +2740,7 @@ OpBranch %bb1 OpSelectionMerge %sel_merge None OpBranchConditional %true %bb2 %bb4 %bb2 = OpLabel +OpSelectionMerge %bb3 None OpSwitch %undef_int %bb3 0 %loop_merge 1 %bb3 %bb3 = OpLabel OpBranch %sel_merge @@ -2782,6 +2784,7 @@ OpSource GLSL 140 ; CHECK: [[bb1]] = OpLabel ; CHECK-NEXT: OpBranch [[bb2:%\w+]] ; CHECK: [[bb2]] = OpLabel +; CHECK-NEXT: OpSelectionMerge ; CHECK-NEXT: OpSwitch {{%\w+}} [[bb3:%\w+]] 0 [[loop_cont]] 1 [[bb3:%\w+]] ; CHECK: [[bb3]] = OpLabel ; CHECK-NEXT: OpBranch [[sel_merge:%\w+]] @@ -2799,6 +2802,7 @@ OpBranch %bb1 OpSelectionMerge %sel_merge None OpBranchConditional %true %bb2 %bb4 %bb2 = OpLabel +OpSelectionMerge %bb3 None OpSwitch %undef_int %bb3 0 %cont 1 %bb3 %bb3 = OpLabel OpBranch %sel_merge diff --git a/test/opt/eliminate_dead_functions_test.cpp b/test/opt/eliminate_dead_functions_test.cpp index 96ecdc60..96deb2a6 100644 --- a/test/opt/eliminate_dead_functions_test.cpp +++ b/test/opt/eliminate_dead_functions_test.cpp @@ -439,6 +439,84 @@ OpFunctionEnd SinglePassRunAndMatch<EliminateDeadFunctionsPass>(text, true); } +TEST_F(EliminateDeadFunctionsBasicTest, NonSemanticInfoRemoveDebugPrintf) { + const std::string text = R"( +; CHECK-NOT: %foo_ = OpFunction %void None % 3 +; CHECK-NOT: % 7 = OpLabel +; CHECK-NOT: %c = OpVariable %_ptr_Function_v4float Function +; CHECK-NOT: % 22 = OpAccessChain %_ptr_UniformConstant_13 %samplers %int_0 +; CHECK-NOT: % 23 = OpLoad % 13 % 22 +; CHECK-NOT: % 27 = OpImageSampleExplicitLod %v4float % 23 % 26 Lod %float_0 +; CHECK-NOT: OpStore %c % 27 +; CHECK-NOT: % 31 = OpAccessChain %_ptr_Function_float %c %uint_0 +; CHECK-NOT: % 32 = OpLoad %float %31 +; CHECK-NOT: % 34 = OpExtInst %void %33 1 % 28 % 32 +OpCapability RayTracingKHR +OpExtension "SPV_KHR_non_semantic_info" +OpExtension "SPV_KHR_ray_tracing" +%1 = OpExtInstImport "GLSL.std.450" +%33 = OpExtInstImport "NonSemantic.DebugPrintf" +OpMemoryModel Logical GLSL450 +OpEntryPoint ClosestHitNV %main "main" %samplers +%28 = OpString "%f" +OpSource GLSL 460 +OpSourceExtension "GL_EXT_debug_printf" +OpName %main "main" +OpName %foo_ "foo(" +OpName %c "c" +OpName %samplers "samplers" +OpDecorate %samplers DescriptorSet 0 +OpDecorate %samplers Binding 0 +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%12 = OpTypeImage %float 3D 0 0 0 1 Unknown +%13 = OpTypeSampledImage %12 +%uint = OpTypeInt 32 0 +%uint_1 = OpConstant %uint 1 +%_arr_13_uint_1 = OpTypeArray %13 %uint_1 +%_ptr_UniformConstant__arr_13_uint_1 = OpTypePointer UniformConstant %_arr_13_uint_1 +%samplers = OpVariable %_ptr_UniformConstant__arr_13_uint_1 UniformConstant +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v3float = OpTypeVector %float 3 +%float_0 = OpConstant %float 0 +%26 = OpConstantComposite %v3float %float_0 %float_0 %float_0 +%uint_0 = OpConstant %uint 0 +%_ptr_Function_float = OpTypePointer Function %float +%main = OpFunction %void None %3 +%5 = OpLabel +%36 = OpVariable %_ptr_Function_v4float Function +%38 = OpAccessChain %_ptr_UniformConstant_13 %samplers %int_0 +%39 = OpLoad %13 %38 +%40 = OpImageSampleExplicitLod %v4float %39 %26 Lod %float_0 +OpStore %36 %40 +%41 = OpAccessChain %_ptr_Function_float %36 %uint_0 +%42 = OpLoad %float %41 +%43 = OpExtInst %void %33 1 %28 %42 +OpReturn +OpFunctionEnd +%foo_ = OpFunction %void None %3 +%7 = OpLabel +%c = OpVariable %_ptr_Function_v4float Function +%22 = OpAccessChain %_ptr_UniformConstant_13 %samplers %int_0 +%23 = OpLoad %13 %22 +%27 = OpImageSampleExplicitLod %v4float %23 %26 Lod %float_0 +OpStore %c %27 +%31 = OpAccessChain %_ptr_Function_float %c %uint_0 +%32 = OpLoad %float %31 +%34 = OpExtInst %void %33 1 %28 %32 +OpReturn +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_2); + SinglePassRunAndMatch<EliminateDeadFunctionsPass>(text, true); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/test/opt/fix_storage_class_test.cpp b/test/opt/fix_storage_class_test.cpp index 4c8504ae..1c0101a0 100644 --- a/test/opt/fix_storage_class_test.cpp +++ b/test/opt/fix_storage_class_test.cpp @@ -528,6 +528,48 @@ TEST_F(FixStorageClassTest, FixLinkedAccessChain2) { SinglePassRunAndMatch<FixStorageClass>(text, false); } +TEST_F(FixStorageClassTest, AllowImageFormatMismatch) { + const std::string text = R"(OpCapability Shader +OpCapability SampledBuffer +OpCapability ImageBuffer +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpSource HLSL 600 +OpName %type_buffer_image "type.buffer.image" +OpName %Buf "Buf" +OpName %main "main" +OpName %src_main "src.main" +OpName %bb_entry "bb.entry" +OpName %type_buffer_image_0 "type.buffer.image" +OpName %b "b" +OpDecorate %Buf DescriptorSet 0 +OpDecorate %Buf Binding 0 +%float = OpTypeFloat 32 +%type_buffer_image = OpTypeImage %float Buffer 2 0 0 2 Rgba16f +%_ptr_UniformConstant_type_buffer_image = OpTypePointer UniformConstant %type_buffer_image +%void = OpTypeVoid +%11 = OpTypeFunction %void +%type_buffer_image_0 = OpTypeImage %float Buffer 2 0 0 2 Rgba32f +%_ptr_Function_type_buffer_image_0 = OpTypePointer Function %type_buffer_image_0 +%Buf = OpVariable %_ptr_UniformConstant_type_buffer_image UniformConstant +%main = OpFunction %void None %11 +%13 = OpLabel +%14 = OpFunctionCall %void %src_main +OpReturn +OpFunctionEnd +%src_main = OpFunction %void None %11 +%bb_entry = OpLabel +%b = OpVariable %_ptr_Function_type_buffer_image_0 Function +%15 = OpLoad %type_buffer_image %Buf +OpStore %b %15 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck<FixStorageClass>(text, text, false, false); +} + using FixTypeTest = PassTest<::testing::Test>; TEST_F(FixTypeTest, FixAccessChain) { diff --git a/test/opt/fold_test.cpp b/test/opt/fold_test.cpp index bb6098cc..8457bbfe 100644 --- a/test/opt/fold_test.cpp +++ b/test/opt/fold_test.cpp @@ -141,6 +141,7 @@ OpName %main "main" %v4int = OpTypeVector %int 4 %v4float = OpTypeVector %float 4 %v4double = OpTypeVector %double 4 +%v2uint = OpTypeVector %uint 2 %v2float = OpTypeVector %float 2 %v2double = OpTypeVector %double 2 %v2half = OpTypeVector %half 2 @@ -191,6 +192,7 @@ OpName %main "main" %v2int_2_3 = OpConstantComposite %v2int %int_2 %int_3 %v2int_3_2 = OpConstantComposite %v2int %int_3 %int_2 %v2int_4_4 = OpConstantComposite %v2int %int_4 %int_4 +%v2int_min_max = OpConstantComposite %v2int %int_min %int_max %v2bool_null = OpConstantNull %v2bool %v2bool_true_false = OpConstantComposite %v2bool %true %false %v2bool_false_true = OpConstantComposite %v2bool %false %true @@ -258,6 +260,15 @@ OpName %main "main" %v4double_1_1_1_0p5 = OpConstantComposite %v4double %double_1 %double_1 %double_1 %double_0p5 %v4double_null = OpConstantNull %v4double %v4float_n1_2_1_3 = OpConstantComposite %v4float %float_n1 %float_2 %float_1 %float_3 +%uint_0x3f800000 = OpConstant %uint 0x3f800000 +%uint_0xbf800000 = OpConstant %uint 0xbf800000 +%v2uint_0x3f800000_0xbf800000 = OpConstantComposite %v2uint %uint_0x3f800000 %uint_0xbf800000 +%long_0xbf8000003f800000 = OpConstant %long 0xbf8000003f800000 +%int_0x3FF00000 = OpConstant %int 0x3FF00000 +%int_0x00000000 = OpConstant %int 0x00000000 +%int_0xC05FD666 = OpConstant %int 0xC05FD666 +%int_0x66666666 = OpConstant %int 0x66666666 +%v4int_0x3FF00000_0x00000000_0xC05FD666_0x66666666 = OpConstantComposite %v4int %int_0x00000000 %int_0x3FF00000 %int_0x66666666 %int_0xC05FD666 )"; return header; @@ -708,7 +719,31 @@ INSTANTIATE_TEST_SUITE_P(TestCase, IntegerInstructionFoldingTest, "%2 = OpExtInst %uint %1 UClamp %uint_2 %undef %uint_1\n" + "OpReturn\n" + "OpFunctionEnd", - 2, 1) + 2, 1), + // Test case 46: Bit-cast int 0 to unsigned int + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpBitcast %uint %int_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 47: Bit-cast int -24 to unsigned int + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpBitcast %uint %int_n24\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, static_cast<uint32_t>(-24)), + // Test case 48: Bit-cast float 1.0f to unsigned int + InstructionFoldingCase<uint32_t>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpBitcast %uint %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, static_cast<uint32_t>(0x3f800000)) )); // clang-format on @@ -790,10 +825,72 @@ INSTANTIATE_TEST_SUITE_P(TestCase, IntVectorInstructionFoldingTest, "%2 = OpVectorShuffle %v2int %v2int_null %v2int_2_3 0 4294967295 \n" + "OpReturn\n" + "OpFunctionEnd", - 2, {0,0}) + 2, {0,0}), + // Test case 4: fold bit-cast int -24 to unsigned int + InstructionFoldingCase<std::vector<uint32_t>>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%load = OpLoad %int %n\n" + + "%2 = OpBitcast %v2uint %v2int_min_max\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, {2147483648, 2147483647}) )); // clang-format on +using DoubleVectorInstructionFoldingTest = + ::testing::TestWithParam<InstructionFoldingCase<std::vector<double>>>; + +TEST_P(DoubleVectorInstructionFoldingTest, Case) { + const auto& tc = GetParam(); + + // Build module. + std::unique_ptr<IRContext> context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.test_body, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(nullptr, context); + + // Fold the instruction to test. + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold); + bool succeeded = context->get_instruction_folder().FoldInstruction(inst); + + // Make sure the instruction folded as expected. + EXPECT_TRUE(succeeded); + if (succeeded && inst != nullptr) { + EXPECT_EQ(inst->opcode(), SpvOpCopyObject); + inst = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0)); + std::vector<SpvOp> opcodes = {SpvOpConstantComposite}; + EXPECT_THAT(opcodes, Contains(inst->opcode())); + analysis::ConstantManager* const_mrg = context->get_constant_mgr(); + const analysis::Constant* result = const_mrg->GetConstantFromInst(inst); + EXPECT_NE(result, nullptr); + if (result != nullptr) { + const std::vector<const analysis::Constant*>& componenets = + result->AsVectorConstant()->GetComponents(); + EXPECT_EQ(componenets.size(), tc.expected_result.size()); + for (size_t i = 0; i < componenets.size(); i++) { + EXPECT_EQ(tc.expected_result[i], componenets[i]->GetDouble()); + } + } + } +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P(TestCase, DoubleVectorInstructionFoldingTest, +::testing::Values( + // Test case 0: bit-cast int {0x3FF00000,0x00000000,0xC05FD666,0x66666666} + // to double vector + InstructionFoldingCase<std::vector<double>>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpBitcast %v2double %v4int_0x3FF00000_0x00000000_0xC05FD666_0x66666666\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, {1.0,-127.35}) +)); + using FloatVectorInstructionFoldingTest = ::testing::TestWithParam<InstructionFoldingCase<std::vector<float>>>; @@ -843,7 +940,24 @@ INSTANTIATE_TEST_SUITE_P(TestCase, FloatVectorInstructionFoldingTest, "%2 = OpExtInst %v2float %1 FMix %v2float_2_3 %v2float_0_0 %v2float_0p2_0p5\n" + "OpReturn\n" + "OpFunctionEnd", - 2, {1.6f,1.5f}) + 2, {1.6f,1.5f}), + // Test case 1: bit-cast unsigned int vector {0x3f800000, 0xbf800000} to + // float vector + InstructionFoldingCase<std::vector<float>>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpBitcast %v2float %v2uint_0x3f800000_0xbf800000\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, {1.0f,-1.0f}), + // Test case 2: bit-cast long int 0xbf8000003f800000 to float vector + InstructionFoldingCase<std::vector<float>>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpBitcast %v2float %long_0xbf8000003f800000\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, {1.0f,-1.0f}) )); // clang-format on using BooleanInstructionFoldingTest = diff --git a/test/opt/graphics_robust_access_test.cpp b/test/opt/graphics_robust_access_test.cpp index 4b2cd447..3c23347d 100644 --- a/test/opt/graphics_robust_access_test.cpp +++ b/test/opt/graphics_robust_access_test.cpp @@ -1548,6 +1548,105 @@ OpFunctionEnd true); } +TEST_F(GraphicsRobustAccessTest, ReplaceIndexReportsChanged) { + // A ClusterFuzz generated shader that triggered a + // "Binary size unexpectedly changed despite the optimizer saying there was no + // change" assertion. + // See https://github.com/KhronosGroup/SPIRV-Tools/issues/4166. + std::string shader = R"( +; SPIR-V +; Version: 1.0 +; Generator: Google Shaderc over Glslang; 245 +; Bound: 41 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "else" %gl_GlobalInvocationID + OpExecutionMode %main LocalSize 1 1 3338665985 + OpSource GLSL 450 + OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" + OpSourceExtension "GL_GOOGLE_include_directive" + OpName %main "main" + OpName %index "index" + OpName %gl_GlobalInvocationID "gl_GlobalInvocationID" + OpName %S "S" + OpMemberName %_struct_24 0 "" + OpMemberName %_struct_24 1 "" + OpName %Dst "Dst" + OpMemberName %Dst 0 "s" + OpName %dst "dst" + OpName %Src "Src" + OpMemberName %Src 0 "s" + OpName %src "src" + OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId + OpMemberDecorate %_struct_24 0 Offset 64 + OpMemberDecorate %_struct_24 1 Offset 8 + OpDecorate %_arr__struct_24_uint_1 ArrayStride 16 + OpMemberDecorate %Dst 0 Offset 0 + OpDecorate %Dst BufferBlock + OpDecorate %dst DescriptorSet 0 + OpDecorate %dst Binding 1 + OpDecorate %_arr__struct_24_uint_1_0 ArrayStride 16 + OpMemberDecorate %Src 0 Offset 0 + OpDecorate %Src Block + OpDecorate %src DescriptorSet 0 + OpDecorate %src Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_Function_uint = OpTypePointer Function %uint + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input + %uint_4864 = OpConstant %uint 4864 +%_ptr_Input_uint = OpTypePointer Input %uint + %uint_1 = OpConstant %uint 1 + %bool = OpTypeBool + %v2uint = OpTypeVector %uint 2 + %_struct_24 = OpTypeStruct %_ptr_Input_uint %v2uint +%_arr__struct_24_uint_1 = OpTypeArray %_struct_24 %uint_1 + %Dst = OpTypeStruct %_arr__struct_24_uint_1 +%_ptr_Uniform_Dst = OpTypePointer Uniform %Dst + %dst = OpVariable %_ptr_Uniform_Dst Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_arr__struct_24_uint_1_0 = OpTypeArray %_struct_24 %uint_1 + %Src = OpTypeStruct %_arr__struct_24_uint_1_0 +%_ptr_Uniform_Src = OpTypePointer Uniform %Src + %src = OpVariable %_ptr_Uniform_Src Uniform +%_ptr_Uniform__struct_24 = OpTypePointer Uniform %_struct_24 + %main = OpFunction %void None %3 + %5 = OpLabel + %index = OpVariable %_ptr_Function_uint Function + %14 = OpAccessChain %_ptr_Input_uint %gl_GlobalInvocationID %uint_4864 + %15 = OpLoad %uint %14 + OpStore %index %15 + %16 = OpLoad %uint %index + %S = OpUGreaterThanEqual %bool %16 %uint_1 + OpSelectionMerge %21 None + OpBranchConditional %S %20 %21 + %20 = OpLabel + OpReturn + %21 = OpLabel + %31 = OpLoad %uint %index + %36 = OpLoad %uint %index + %38 = OpAccessChain %_ptr_Uniform__struct_24 %src %int_0 %36 + %39 = OpLoad %_struct_24 %38 + %40 = OpAccessChain %_ptr_Uniform__struct_24 %dst %int_0 %31 + OpStore %40 %39 + OpReturn + OpFunctionEnd +)"; + + std::vector<uint32_t> optimized_bin; + auto status = spvtools::opt::Pass::Status::Failure; + std::tie(optimized_bin, status) = + SinglePassRunToBinary<GraphicsRobustAccessPass>(shader, false); + // Check whether the pass returns the correct modification indication. + EXPECT_EQ(status, spvtools::opt::Pass::Status::SuccessWithChange); +} + // TODO(dneto): Test access chain index wider than 64 bits? // TODO(dneto): Test struct access chain index wider than 64 bits? // TODO(dneto): OpImageTexelPointer diff --git a/test/opt/inst_bindless_check_test.cpp b/test/opt/inst_bindless_check_test.cpp index f1899627..1a42329b 100644 --- a/test/opt/inst_bindless_check_test.cpp +++ b/test/opt/inst_bindless_check_test.cpp @@ -7308,15 +7308,15 @@ TEST_F(InstBindlessTest, MultipleUniformNonAggregateRefsNoDescInit) { ;CHECK: %_ptr_StorageBuffer__struct_128 = OpTypePointer StorageBuffer %_struct_128 ;CHECK: %130 = OpVariable %_ptr_StorageBuffer__struct_128 StorageBuffer ;CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint - ;CHECK: %uint_3 = OpConstant %uint 3 + ;CHECK: %uint_4 = OpConstant %uint 4 ;CHECK: %148 = OpTypeFunction %void %uint %uint %uint %uint %uint ;CHECK: %_struct_155 = OpTypeStruct %uint %_runtimearr_uint ;CHECK: %_ptr_StorageBuffer__struct_155 = OpTypePointer StorageBuffer %_struct_155 ;CHECK: %157 = OpVariable %_ptr_StorageBuffer__struct_155 StorageBuffer ;CHECK: %uint_11 = OpConstant %uint 11 - ;CHECK: %uint_4 = OpConstant %uint 4 ;CHECK: %uint_23 = OpConstant %uint 23 ;CHECK: %uint_2 = OpConstant %uint 2 + ;CHECK: %uint_3 = OpConstant %uint 3 ;CHECK:%_ptr_Input_v4float = OpTypePointer Input %v4float ;CHECK:%gl_FragCoord = OpVariable %_ptr_Input_v4float Input ;CHECK: %v4uint = OpTypeVector %uint 4 @@ -7352,7 +7352,7 @@ TEST_F(InstBindlessTest, MultipleUniformNonAggregateRefsNoDescInit) { ;CHECK: %146 = OpLoad %v2float %86 ;CHECK: OpBranch %143 ;CHECK: %145 = OpLabel - ;CHECK: %201 = OpFunctionCall %void %147 %uint_71 %uint_3 %uint_0 %119 %140 + ;CHECK: %201 = OpFunctionCall %void %147 %uint_71 %uint_4 %uint_0 %119 %140 ;CHECK: OpBranch %143 ;CHECK: %143 = OpLabel ;CHECK: %203 = OpPhi %v2float %146 %144 %202 %145 @@ -7369,7 +7369,7 @@ TEST_F(InstBindlessTest, MultipleUniformNonAggregateRefsNoDescInit) { ;CHECK: %209 = OpLoad %v2float %89 ;CHECK: OpBranch %206 ;CHECK: %208 = OpLabel - ;CHECK: %211 = OpFunctionCall %void %147 %uint_75 %uint_3 %uint_0 %204 %140 + ;CHECK: %211 = OpFunctionCall %void %147 %uint_75 %uint_4 %uint_0 %204 %140 ;CHECK: OpBranch %206 ;CHECK: %206 = OpLabel ;CHECK: %212 = OpPhi %v2float %209 %207 %202 %208 @@ -7409,49 +7409,49 @@ TEST_F(InstBindlessTest, MultipleUniformNonAggregateRefsNoDescInit) { ;CHECK: %153 = OpFunctionParameter %uint ;CHECK: %154 = OpLabel ;CHECK: %158 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_0 - ;CHECK: %161 = OpAtomicIAdd %uint %158 %uint_4 %uint_0 %uint_11 - ;CHECK: %162 = OpIAdd %uint %161 %uint_11 - ;CHECK: %163 = OpArrayLength %uint %157 1 - ;CHECK: %164 = OpULessThanEqual %bool %162 %163 - ;CHECK: OpSelectionMerge %165 None - ;CHECK: OpBranchConditional %164 %166 %165 - ;CHECK: %166 = OpLabel - ;CHECK: %167 = OpIAdd %uint %161 %uint_0 - ;CHECK: %168 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %167 - ;CHECK: OpStore %168 %uint_11 - ;CHECK: %170 = OpIAdd %uint %161 %uint_1 - ;CHECK: %171 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %170 - ;CHECK: OpStore %171 %uint_23 - ;CHECK: %173 = OpIAdd %uint %161 %uint_2 - ;CHECK: %174 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %173 - ;CHECK: OpStore %174 %149 - ;CHECK: %175 = OpIAdd %uint %161 %uint_3 + ;CHECK: %160 = OpAtomicIAdd %uint %158 %uint_4 %uint_0 %uint_11 + ;CHECK: %161 = OpIAdd %uint %160 %uint_11 + ;CHECK: %162 = OpArrayLength %uint %157 1 + ;CHECK: %163 = OpULessThanEqual %bool %161 %162 + ;CHECK: OpSelectionMerge %164 None + ;CHECK: OpBranchConditional %163 %165 %164 + ;CHECK: %165 = OpLabel + ;CHECK: %166 = OpIAdd %uint %160 %uint_0 + ;CHECK: %167 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %166 + ;CHECK: OpStore %167 %uint_11 + ;CHECK: %169 = OpIAdd %uint %160 %uint_1 + ;CHECK: %170 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %169 + ;CHECK: OpStore %170 %uint_23 + ;CHECK: %172 = OpIAdd %uint %160 %uint_2 + ;CHECK: %173 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %172 + ;CHECK: OpStore %173 %149 + ;CHECK: %175 = OpIAdd %uint %160 %uint_3 ;CHECK: %176 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %175 ;CHECK: OpStore %176 %uint_4 ;CHECK: %179 = OpLoad %v4float %gl_FragCoord ;CHECK: %181 = OpBitcast %v4uint %179 ;CHECK: %182 = OpCompositeExtract %uint %181 0 - ;CHECK: %183 = OpIAdd %uint %161 %uint_4 + ;CHECK: %183 = OpIAdd %uint %160 %uint_4 ;CHECK: %184 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %183 ;CHECK: OpStore %184 %182 ;CHECK: %185 = OpCompositeExtract %uint %181 1 - ;CHECK: %187 = OpIAdd %uint %161 %uint_5 + ;CHECK: %187 = OpIAdd %uint %160 %uint_5 ;CHECK: %188 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %187 ;CHECK: OpStore %188 %185 - ;CHECK: %189 = OpIAdd %uint %161 %uint_7 + ;CHECK: %189 = OpIAdd %uint %160 %uint_7 ;CHECK: %190 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %189 ;CHECK: OpStore %190 %150 - ;CHECK: %192 = OpIAdd %uint %161 %uint_8 + ;CHECK: %192 = OpIAdd %uint %160 %uint_8 ;CHECK: %193 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %192 ;CHECK: OpStore %193 %151 - ;CHECK: %195 = OpIAdd %uint %161 %uint_9 + ;CHECK: %195 = OpIAdd %uint %160 %uint_9 ;CHECK: %196 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %195 ;CHECK: OpStore %196 %152 - ;CHECK: %198 = OpIAdd %uint %161 %uint_10 + ;CHECK: %198 = OpIAdd %uint %160 %uint_10 ;CHECK: %199 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %198 ;CHECK: OpStore %199 %153 - ;CHECK: OpBranch %165 - ;CHECK: %165 = OpLabel + ;CHECK: OpBranch %164 + ;CHECK: %164 = OpLabel ;CHECK: OpReturn ;CHECK: OpFunctionEnd )"; @@ -7596,14 +7596,14 @@ TEST_F(InstBindlessTest, UniformArrayRefNoDescInit) { ;CHECK: %113 = OpVariable %_ptr_StorageBuffer__struct_111 StorageBuffer ;CHECK:%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint ;CHECK: %bool = OpTypeBool -;CHECK: %uint_3 = OpConstant %uint 3 +;CHECK: %uint_4 = OpConstant %uint 4 ;CHECK: %132 = OpTypeFunction %void %uint %uint %uint %uint %uint ;CHECK:%_struct_139 = OpTypeStruct %uint %_runtimearr_uint ;CHECK:%_ptr_StorageBuffer__struct_139 = OpTypePointer StorageBuffer %_struct_139 ;CHECK: %141 = OpVariable %_ptr_StorageBuffer__struct_139 StorageBuffer ;CHECK: %uint_11 = OpConstant %uint 11 -;CHECK: %uint_4 = OpConstant %uint 4 ;CHECK: %uint_23 = OpConstant %uint 23 +;CHECK: %uint_3 = OpConstant %uint 3 ;CHECK:%_ptr_Input_v4float = OpTypePointer Input %v4float ;CHECK:%gl_FragCoord = OpVariable %_ptr_Input_v4float Input ;CHECK: %v4uint = OpTypeVector %uint 4 @@ -7637,7 +7637,7 @@ TEST_F(InstBindlessTest, UniformArrayRefNoDescInit) { ;CHECK: %130 = OpLoad %v2float %81 ;CHECK: OpBranch %127 ;CHECK: %129 = OpLabel -;CHECK: %184 = OpFunctionCall %void %131 %uint_78 %uint_3 %uint_0 %101 %123 +;CHECK: %184 = OpFunctionCall %void %131 %uint_78 %uint_4 %uint_0 %101 %123 ;CHECK: OpBranch %127 ;CHECK: %127 = OpLabel ;CHECK: %186 = OpPhi %v2float %130 %128 %185 %129 @@ -7674,49 +7674,49 @@ TEST_F(InstBindlessTest, UniformArrayRefNoDescInit) { ;CHECK: %137 = OpFunctionParameter %uint ;CHECK: %138 = OpLabel ;CHECK: %142 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_0 -;CHECK: %145 = OpAtomicIAdd %uint %142 %uint_4 %uint_0 %uint_11 -;CHECK: %146 = OpIAdd %uint %145 %uint_11 -;CHECK: %147 = OpArrayLength %uint %141 1 -;CHECK: %148 = OpULessThanEqual %bool %146 %147 -;CHECK: OpSelectionMerge %149 None -;CHECK: OpBranchConditional %148 %150 %149 -;CHECK: %150 = OpLabel -;CHECK: %151 = OpIAdd %uint %145 %uint_0 -;CHECK: %152 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %151 -;CHECK: OpStore %152 %uint_11 -;CHECK: %154 = OpIAdd %uint %145 %uint_1 -;CHECK: %155 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %154 -;CHECK: OpStore %155 %uint_23 -;CHECK: %156 = OpIAdd %uint %145 %uint_2 -;CHECK: %157 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %156 -;CHECK: OpStore %157 %133 -;CHECK: %158 = OpIAdd %uint %145 %uint_3 +;CHECK: %144 = OpAtomicIAdd %uint %142 %uint_4 %uint_0 %uint_11 +;CHECK: %145 = OpIAdd %uint %144 %uint_11 +;CHECK: %146 = OpArrayLength %uint %141 1 +;CHECK: %147 = OpULessThanEqual %bool %145 %146 +;CHECK: OpSelectionMerge %148 None +;CHECK: OpBranchConditional %147 %149 %148 +;CHECK: %149 = OpLabel +;CHECK: %150 = OpIAdd %uint %144 %uint_0 +;CHECK: %151 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %150 +;CHECK: OpStore %151 %uint_11 +;CHECK: %153 = OpIAdd %uint %144 %uint_1 +;CHECK: %154 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %153 +;CHECK: OpStore %154 %uint_23 +;CHECK: %155 = OpIAdd %uint %144 %uint_2 +;CHECK: %156 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %155 +;CHECK: OpStore %156 %133 +;CHECK: %158 = OpIAdd %uint %144 %uint_3 ;CHECK: %159 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %158 ;CHECK: OpStore %159 %uint_4 ;CHECK: %162 = OpLoad %v4float %gl_FragCoord ;CHECK: %164 = OpBitcast %v4uint %162 ;CHECK: %165 = OpCompositeExtract %uint %164 0 -;CHECK: %166 = OpIAdd %uint %145 %uint_4 +;CHECK: %166 = OpIAdd %uint %144 %uint_4 ;CHECK: %167 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %166 ;CHECK: OpStore %167 %165 ;CHECK: %168 = OpCompositeExtract %uint %164 1 -;CHECK: %170 = OpIAdd %uint %145 %uint_5 +;CHECK: %170 = OpIAdd %uint %144 %uint_5 ;CHECK: %171 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %170 ;CHECK: OpStore %171 %168 -;CHECK: %172 = OpIAdd %uint %145 %uint_7 +;CHECK: %172 = OpIAdd %uint %144 %uint_7 ;CHECK: %173 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %172 ;CHECK: OpStore %173 %134 -;CHECK: %175 = OpIAdd %uint %145 %uint_8 +;CHECK: %175 = OpIAdd %uint %144 %uint_8 ;CHECK: %176 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %175 ;CHECK: OpStore %176 %135 -;CHECK: %178 = OpIAdd %uint %145 %uint_9 +;CHECK: %178 = OpIAdd %uint %144 %uint_9 ;CHECK: %179 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %178 ;CHECK: OpStore %179 %136 -;CHECK: %181 = OpIAdd %uint %145 %uint_10 +;CHECK: %181 = OpIAdd %uint %144 %uint_10 ;CHECK: %182 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %181 ;CHECK: OpStore %182 %137 -;CHECK: OpBranch %149 -;CHECK: %149 = OpLabel +;CHECK: OpBranch %148 +;CHECK: %148 = OpLabel ;CHECK: OpReturn ;CHECK: OpFunctionEnd )"; @@ -7743,11 +7743,11 @@ TEST_F(InstBindlessTest, UniformArrayRefWithDescInit) { OpExecutionMode %MainPs OriginUpperLeft OpSource HLSL 500 OpName %MainPs "MainPs" - OpName %PerBatchEnvMapConstantBuffer_t -"PerBatchEnvMapConstantBuffer_t" OpMemberName %PerBatchEnvMapConstantBuffer_t 0 -"g_matEnvMapWorldToLocal" OpMemberName %PerBatchEnvMapConstantBuffer_t 1 -"g_vEnvironmentMapBoxMins" OpMemberName %PerBatchEnvMapConstantBuffer_t 2 -"g_TexOff" OpName %_BindlessFastEnvMapCB_PS_t "_BindlessFastEnvMapCB_PS_t" + OpName %PerBatchEnvMapConstantBuffer_t "PerBatchEnvMapConstantBuffer_t" + OpMemberName %PerBatchEnvMapConstantBuffer_t 0 "g_matEnvMapWorldToLocal" + OpMemberName %PerBatchEnvMapConstantBuffer_t 1 "g_vEnvironmentMapBoxMins" + OpMemberName %PerBatchEnvMapConstantBuffer_t 2 "g_TexOff" + OpName %_BindlessFastEnvMapCB_PS_t "_BindlessFastEnvMapCB_PS_t" OpMemberName %_BindlessFastEnvMapCB_PS_t 0 "g_envMapConstants" OpName %_ "" OpName %PerViewPushConst_t "PerViewPushConst_t" @@ -7831,15 +7831,15 @@ TEST_F(InstBindlessTest, UniformArrayRefWithDescInit) { ;CHECK: %113 = OpVariable %_ptr_StorageBuffer__struct_111 StorageBuffer ;CHECK:%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint ;CHECK: %bool = OpTypeBool -;CHECK: %uint_3 = OpConstant %uint 3 +;CHECK: %uint_4 = OpConstant %uint 4 ;CHECK: %135 = OpTypeFunction %void %uint %uint %uint %uint %uint ;CHECK:%_struct_142 = OpTypeStruct %uint %_runtimearr_uint ;CHECK:%_ptr_StorageBuffer__struct_142 = OpTypePointer StorageBuffer %_struct_142 ;CHECK: %144 = OpVariable %_ptr_StorageBuffer__struct_142 StorageBuffer ;CHECK: %uint_11 = OpConstant %uint 11 -;CHECK: %uint_4 = OpConstant %uint 4 ;CHECK: %uint_1 = OpConstant %uint 1 ;CHECK: %uint_23 = OpConstant %uint 23 +;CHECK: %uint_3 = OpConstant %uint 3 ;CHECK:%_ptr_Input_v4float = OpTypePointer Input %v4float ;CHECK:%gl_FragCoord = OpVariable %_ptr_Input_v4float Input ;CHECK: %v4uint = OpTypeVector %uint 4 @@ -7878,13 +7878,15 @@ TEST_F(InstBindlessTest, UniformArrayRefWithDescInit) { ;CHECK: %133 = OpLoad %v2float %81 ;CHECK: OpBranch %130 ;CHECK: %132 = OpLabel -;CHECK: %188 = OpFunctionCall %void %134 %uint_78 %uint_3 %uint_0 %101 %126 +;CHECK: %188 = OpFunctionCall %void %134 %uint_78 %uint_4 %uint_0 %101 %126 ;CHECK: OpBranch %130 ;CHECK: %130 = OpLabel ;CHECK: %190 = OpPhi %v2float %133 %131 %189 %132 ;CHECK: %86 = OpFAdd %v2float %66 %190 - %87 = OpLoad %46 %g_tColor %88 = OpLoad %50 %g_sAniso %89 = - OpSampledImage %54 %87 %88 %91 = OpImageSampleImplicitLod %v4float %89 %86 + %87 = OpLoad %46 %g_tColor + %88 = OpLoad %50 %g_sAniso + %89 = OpSampledImage %54 %87 %88 + %91 = OpImageSampleImplicitLod %v4float %89 %86 OpStore %_entryPointOutput_vColor %91 ;CHECK-NOT: %91 = OpImageSampleImplicitLod %v4float %89 %86 ;CHECK-NOT: OpStore %_entryPointOutput_vColor %91 @@ -7931,49 +7933,49 @@ TEST_F(InstBindlessTest, UniformArrayRefWithDescInit) { ;CHECK: %140 = OpFunctionParameter %uint ;CHECK: %141 = OpLabel ;CHECK: %145 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_0 -;CHECK: %148 = OpAtomicIAdd %uint %145 %uint_4 %uint_0 %uint_11 -;CHECK: %149 = OpIAdd %uint %148 %uint_11 -;CHECK: %150 = OpArrayLength %uint %144 1 -;CHECK: %151 = OpULessThanEqual %bool %149 %150 -;CHECK: OpSelectionMerge %152 None -;CHECK: OpBranchConditional %151 %153 %152 -;CHECK: %153 = OpLabel -;CHECK: %154 = OpIAdd %uint %148 %uint_0 -;CHECK: %156 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %154 -;CHECK: OpStore %156 %uint_11 -;CHECK: %158 = OpIAdd %uint %148 %uint_1 -;CHECK: %159 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %158 -;CHECK: OpStore %159 %uint_23 -;CHECK: %160 = OpIAdd %uint %148 %uint_2 -;CHECK: %161 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %160 -;CHECK: OpStore %161 %136 -;CHECK: %162 = OpIAdd %uint %148 %uint_3 +;CHECK: %147 = OpAtomicIAdd %uint %145 %uint_4 %uint_0 %uint_11 +;CHECK: %148 = OpIAdd %uint %147 %uint_11 +;CHECK: %149 = OpArrayLength %uint %144 1 +;CHECK: %150 = OpULessThanEqual %bool %148 %149 +;CHECK: OpSelectionMerge %151 None +;CHECK: OpBranchConditional %150 %152 %151 +;CHECK: %152 = OpLabel +;CHECK: %153 = OpIAdd %uint %147 %uint_0 +;CHECK: %155 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %153 +;CHECK: OpStore %155 %uint_11 +;CHECK: %157 = OpIAdd %uint %147 %uint_1 +;CHECK: %158 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %157 +;CHECK: OpStore %158 %uint_23 +;CHECK: %159 = OpIAdd %uint %147 %uint_2 +;CHECK: %160 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %159 +;CHECK: OpStore %160 %136 +;CHECK: %162 = OpIAdd %uint %147 %uint_3 ;CHECK: %163 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %162 ;CHECK: OpStore %163 %uint_4 ;CHECK: %166 = OpLoad %v4float %gl_FragCoord ;CHECK: %168 = OpBitcast %v4uint %166 ;CHECK: %169 = OpCompositeExtract %uint %168 0 -;CHECK: %170 = OpIAdd %uint %148 %uint_4 +;CHECK: %170 = OpIAdd %uint %147 %uint_4 ;CHECK: %171 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %170 ;CHECK: OpStore %171 %169 ;CHECK: %172 = OpCompositeExtract %uint %168 1 -;CHECK: %174 = OpIAdd %uint %148 %uint_5 +;CHECK: %174 = OpIAdd %uint %147 %uint_5 ;CHECK: %175 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %174 ;CHECK: OpStore %175 %172 -;CHECK: %176 = OpIAdd %uint %148 %uint_7 +;CHECK: %176 = OpIAdd %uint %147 %uint_7 ;CHECK: %177 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %176 ;CHECK: OpStore %177 %137 -;CHECK: %179 = OpIAdd %uint %148 %uint_8 +;CHECK: %179 = OpIAdd %uint %147 %uint_8 ;CHECK: %180 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %179 ;CHECK: OpStore %180 %138 -;CHECK: %182 = OpIAdd %uint %148 %uint_9 +;CHECK: %182 = OpIAdd %uint %147 %uint_9 ;CHECK: %183 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %182 ;CHECK: OpStore %183 %139 -;CHECK: %185 = OpIAdd %uint %148 %uint_10 +;CHECK: %185 = OpIAdd %uint %147 %uint_10 ;CHECK: %186 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %185 ;CHECK: OpStore %186 %140 -;CHECK: OpBranch %152 -;CHECK: %152 = OpLabel +;CHECK: OpBranch %151 +;CHECK: %151 = OpLabel ;CHECK: OpReturn ;CHECK: OpFunctionEnd )"; @@ -8344,15 +8346,15 @@ TEST_F(InstBindlessTest, UniformArray16bitIdxRef) { ;CHECK: %69 = OpVariable %_ptr_StorageBuffer__struct_67 StorageBuffer ;CHECK:%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint ;CHECK: %bool = OpTypeBool -;CHECK: %uint_3 = OpConstant %uint 3 +;CHECK: %uint_4 = OpConstant %uint 4 ;CHECK: %88 = OpTypeFunction %void %uint %uint %uint %uint %uint ;CHECK: %_struct_95 = OpTypeStruct %uint %_runtimearr_uint ;CHECK:%_ptr_StorageBuffer__struct_95 = OpTypePointer StorageBuffer %_struct_95 ;CHECK: %97 = OpVariable %_ptr_StorageBuffer__struct_95 StorageBuffer ;CHECK: %uint_11 = OpConstant %uint 11 -;CHECK: %uint_4 = OpConstant %uint 4 ;CHECK: %uint_23 = OpConstant %uint 23 ;CHECK: %uint_2 = OpConstant %uint 2 +;CHECK: %uint_3 = OpConstant %uint 3 ;CHECK:%_ptr_Input_v4float = OpTypePointer Input %v4float ;CHECK:%gl_FragCoord = OpVariable %_ptr_Input_v4float Input ;CHECK: %v4uint = OpTypeVector %uint 4 @@ -8389,7 +8391,7 @@ TEST_F(InstBindlessTest, UniformArray16bitIdxRef) { ;CHECK: %86 = OpLoad %v2float %41 ;CHECK: OpBranch %83 ;CHECK: %85 = OpLabel -;CHECK: %141 = OpFunctionCall %void %87 %uint_81 %uint_3 %uint_0 %58 %79 +;CHECK: %141 = OpFunctionCall %void %87 %uint_81 %uint_4 %uint_0 %58 %79 ;CHECK: OpBranch %83 ;CHECK: %83 = OpLabel ;CHECK: %143 = OpPhi %v2float %86 %84 %142 %85 @@ -8424,49 +8426,49 @@ TEST_F(InstBindlessTest, UniformArray16bitIdxRef) { ;CHECK: %93 = OpFunctionParameter %uint ;CHECK: %94 = OpLabel ;CHECK: %98 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_0 -;CHECK: %101 = OpAtomicIAdd %uint %98 %uint_4 %uint_0 %uint_11 -;CHECK: %102 = OpIAdd %uint %101 %uint_11 -;CHECK: %103 = OpArrayLength %uint %97 1 -;CHECK: %104 = OpULessThanEqual %bool %102 %103 -;CHECK: OpSelectionMerge %105 None -;CHECK: OpBranchConditional %104 %106 %105 -;CHECK: %106 = OpLabel -;CHECK: %107 = OpIAdd %uint %101 %uint_0 -;CHECK: %108 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %107 -;CHECK: OpStore %108 %uint_11 -;CHECK: %110 = OpIAdd %uint %101 %uint_1 -;CHECK: %111 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %110 -;CHECK: OpStore %111 %uint_23 -;CHECK: %113 = OpIAdd %uint %101 %uint_2 -;CHECK: %114 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %113 -;CHECK: OpStore %114 %89 -;CHECK: %115 = OpIAdd %uint %101 %uint_3 +;CHECK: %100 = OpAtomicIAdd %uint %98 %uint_4 %uint_0 %uint_11 +;CHECK: %101 = OpIAdd %uint %100 %uint_11 +;CHECK: %102 = OpArrayLength %uint %97 1 +;CHECK: %103 = OpULessThanEqual %bool %101 %102 +;CHECK: OpSelectionMerge %104 None +;CHECK: OpBranchConditional %103 %105 %104 +;CHECK: %105 = OpLabel +;CHECK: %106 = OpIAdd %uint %100 %uint_0 +;CHECK: %107 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %106 +;CHECK: OpStore %107 %uint_11 +;CHECK: %109 = OpIAdd %uint %100 %uint_1 +;CHECK: %110 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %109 +;CHECK: OpStore %110 %uint_23 +;CHECK: %112 = OpIAdd %uint %100 %uint_2 +;CHECK: %113 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %112 +;CHECK: OpStore %113 %89 +;CHECK: %115 = OpIAdd %uint %100 %uint_3 ;CHECK: %116 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %115 ;CHECK: OpStore %116 %uint_4 ;CHECK: %119 = OpLoad %v4float %gl_FragCoord ;CHECK: %121 = OpBitcast %v4uint %119 ;CHECK: %122 = OpCompositeExtract %uint %121 0 -;CHECK: %123 = OpIAdd %uint %101 %uint_4 +;CHECK: %123 = OpIAdd %uint %100 %uint_4 ;CHECK: %124 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %123 ;CHECK: OpStore %124 %122 ;CHECK: %125 = OpCompositeExtract %uint %121 1 -;CHECK: %127 = OpIAdd %uint %101 %uint_5 +;CHECK: %127 = OpIAdd %uint %100 %uint_5 ;CHECK: %128 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %127 ;CHECK: OpStore %128 %125 -;CHECK: %129 = OpIAdd %uint %101 %uint_7 +;CHECK: %129 = OpIAdd %uint %100 %uint_7 ;CHECK: %130 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %129 ;CHECK: OpStore %130 %90 -;CHECK: %132 = OpIAdd %uint %101 %uint_8 +;CHECK: %132 = OpIAdd %uint %100 %uint_8 ;CHECK: %133 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %132 ;CHECK: OpStore %133 %91 -;CHECK: %135 = OpIAdd %uint %101 %uint_9 +;CHECK: %135 = OpIAdd %uint %100 %uint_9 ;CHECK: %136 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %135 ;CHECK: OpStore %136 %92 -;CHECK: %138 = OpIAdd %uint %101 %uint_10 +;CHECK: %138 = OpIAdd %uint %100 %uint_10 ;CHECK: %139 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %138 ;CHECK: OpStore %139 %93 -;CHECK: OpBranch %105 -;CHECK: %105 = OpLabel +;CHECK: OpBranch %104 +;CHECK: %104 = OpLabel ;CHECK: OpReturn ;CHECK: OpFunctionEnd )"; @@ -8557,6 +8559,7 @@ TEST_F(InstBindlessTest, UniformMatrixRefRowMajor) { %_ptr_Input_v4float = OpTypePointer Input %v4float %a_position = OpVariable %_ptr_Input_v4float Input ;CHECK; %uint_0 = OpConstant %uint 0 +;CHECK; %uint_16 = OpConstant %uint 16 ;CHECK; %uint_4 = OpConstant %uint 4 ;CHECK; %uint_3 = OpConstant %uint 3 ;CHECK; %37 = OpTypeFunction %uint %uint %uint %uint @@ -8583,10 +8586,13 @@ TEST_F(InstBindlessTest, UniformMatrixRefRowMajor) { ;CHECK; %uint_10 = OpConstant %uint 10 ;CHECK; %uint_45 = OpConstant %uint 45 ;CHECK; %115 = OpConstantNull %float -;CHECK; %uint_27 = OpConstant %uint 27 %main = OpFunction %void None %3 %5 = OpLabel ;CHECK: %55 = OpFunctionCall %uint %36 %uint_1 %uint_0 %uint_0 +;CHECK: OpBranch %26 +;CHECK: %26 = OpLabel +;CHECK: OpBranch %25 +;CHECK: %25 = OpLabel %20 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %int_2 %uint_1 %21 = OpLoad %float %20 ;CHECK-NOT: %21 = OpLoad %float %20 @@ -8602,7 +8608,7 @@ TEST_F(InstBindlessTest, UniformMatrixRefRowMajor) { ;CHECK: %61 = OpLoad %float %20 ;CHECK: OpBranch %58 ;CHECK: %60 = OpLabel -;CHECK: %114 = OpFunctionCall %void %62 %uint_45 %uint_3 %uint_0 %35 %55 +;CHECK: %114 = OpFunctionCall %void %62 %uint_45 %uint_4 %uint_0 %35 %55 ;CHECK: OpBranch %58 ;CHECK: %58 = OpLabel ;CHECK: %116 = OpPhi %float %61 %59 %115 %60 @@ -8790,9 +8796,14 @@ TEST_F(InstBindlessTest, UniformMatrixRefColumnMajor) { ;CHECK: %uint_9 = OpConstant %uint 9 ;CHECK: %uint_10 = OpConstant %uint 10 ;CHECK: %uint_45 = OpConstant %uint 45 +;CHECK: %114 = OpConstantNull %float %main = OpFunction %void None %3 %5 = OpLabel ;CHECK: %55 = OpFunctionCall %uint %36 %uint_1 %uint_0 %uint_0 +;CHECK: OpBranch %26 +;CHECK: %26 = OpLabel +;CHECK: OpBranch %25 +;CHECK: %25 = OpLabel %20 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %int_2 %uint_1 %21 = OpLoad %float %20 ;CHECK-NOT: %21 = OpLoad %float %20 @@ -8808,7 +8819,7 @@ TEST_F(InstBindlessTest, UniformMatrixRefColumnMajor) { ;CHECK: %61 = OpLoad %float %20 ;CHECK: OpBranch %58 ;CHECK: %60 = OpLabel -;CHECK: %113 = OpFunctionCall %void %62 %uint_45 %uint_3 %uint_0 %35 %55 +;CHECK: %113 = OpFunctionCall %void %62 %uint_45 %uint_4 %uint_0 %35 %55 ;CHECK: OpBranch %58 ;CHECK: %58 = OpLabel ;CHECK: %115 = OpPhi %float %61 %59 %114 %60 @@ -9032,7 +9043,7 @@ TEST_F(InstBindlessTest, UniformMatrixVecRefRowMajor) { ;CHECK: %70 = OpLoad %v2float %25 ;CHECK: OpBranch %67 ;CHECK: %69 = OpLabel -;CHECK: %123 = OpFunctionCall %void %71 %uint_51 %uint_3 %uint_0 %43 %64 +;CHECK: %123 = OpFunctionCall %void %71 %uint_51 %uint_4 %uint_0 %43 %64 ;CHECK: OpBranch %67 ;CHECK: %67 = OpLabel ;CHECK: %125 = OpPhi %v2float %70 %68 %124 %69 @@ -9167,7 +9178,7 @@ TEST_F(InstBindlessTest, ImageBufferOOBRead) { ;CHECK: %uint = OpTypeInt 32 0 ;CHECK: %uint_0 = OpConstant %uint 0 ;CHECK: %bool = OpTypeBool -;CHECK: %uint_3 = OpConstant %uint 3 +;CHECK: %uint_7 = OpConstant %uint 7 ;CHECK: %35 = OpTypeFunction %void %uint %uint %uint %uint %uint ;CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint ;CHECK: %_struct_43 = OpTypeStruct %uint %_runtimearr_uint @@ -9179,11 +9190,11 @@ TEST_F(InstBindlessTest, ImageBufferOOBRead) { ;CHECK: %uint_1 = OpConstant %uint 1 ;CHECK: %uint_23 = OpConstant %uint 23 ;CHECK: %uint_2 = OpConstant %uint 2 +;CHECK: %uint_3 = OpConstant %uint 3 ;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float ;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input ;CHECK: %v4uint = OpTypeVector %uint 4 ;CHECK: %uint_5 = OpConstant %uint 5 -;CHECK: %uint_7 = OpConstant %uint 7 ;CHECK: %uint_8 = OpConstant %uint 8 ;CHECK: %uint_9 = OpConstant %uint 9 ;CHECK: %uint_10 = OpConstant %uint 10 @@ -9213,7 +9224,7 @@ TEST_F(InstBindlessTest, ImageBufferOOBRead) { ;CHECK: %33 = OpImageRead %v4float %32 %17 ;CHECK: OpBranch %29 ;CHECK: %31 = OpLabel -;CHECK: %92 = OpFunctionCall %void %34 %uint_33 %uint_3 %uint_0 %23 %25 +;CHECK: %92 = OpFunctionCall %void %34 %uint_33 %uint_7 %uint_0 %23 %25 ;CHECK: OpBranch %29 ;CHECK: %29 = OpLabel ;CHECK: %94 = OpPhi %v4float %33 %30 %93 %31 @@ -9244,19 +9255,19 @@ TEST_F(InstBindlessTest, ImageBufferOOBRead) { ;CHECK: %63 = OpIAdd %uint %50 %uint_2 ;CHECK: %64 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %63 ;CHECK: OpStore %64 %36 -;CHECK: %65 = OpIAdd %uint %50 %uint_3 -;CHECK: %66 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %65 -;CHECK: OpStore %66 %uint_4 -;CHECK: %69 = OpLoad %v4float %gl_FragCoord -;CHECK: %71 = OpBitcast %v4uint %69 -;CHECK: %72 = OpCompositeExtract %uint %71 0 -;CHECK: %73 = OpIAdd %uint %50 %uint_4 -;CHECK: %74 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %73 -;CHECK: OpStore %74 %72 -;CHECK: %75 = OpCompositeExtract %uint %71 1 -;CHECK: %77 = OpIAdd %uint %50 %uint_5 -;CHECK: %78 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %77 -;CHECK: OpStore %78 %75 +;CHECK: %66 = OpIAdd %uint %50 %uint_3 +;CHECK: %67 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %66 +;CHECK: OpStore %67 %uint_4 +;CHECK: %70 = OpLoad %v4float %gl_FragCoord +;CHECK: %72 = OpBitcast %v4uint %70 +;CHECK: %73 = OpCompositeExtract %uint %72 0 +;CHECK: %74 = OpIAdd %uint %50 %uint_4 +;CHECK: %75 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %74 +;CHECK: OpStore %75 %73 +;CHECK: %76 = OpCompositeExtract %uint %72 1 +;CHECK: %78 = OpIAdd %uint %50 %uint_5 +;CHECK: %79 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %78 +;CHECK: OpStore %79 %76 ;CHECK: %80 = OpIAdd %uint %50 %uint_7 ;CHECK: %81 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %80 ;CHECK: OpStore %81 %37 @@ -9336,7 +9347,7 @@ TEST_F(InstBindlessTest, ImageBufferOOBWrite) { ;CHECK: %uint = OpTypeInt 32 0 ;CHECK: %uint_0 = OpConstant %uint 0 ;CHECK: %bool = OpTypeBool -;CHECK: %uint_3 = OpConstant %uint 3 +;CHECK: %uint_7 = OpConstant %uint 7 ;CHECK: %34 = OpTypeFunction %void %uint %uint %uint %uint %uint ;CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint ;CHECK: %_struct_42 = OpTypeStruct %uint %_runtimearr_uint @@ -9348,11 +9359,11 @@ TEST_F(InstBindlessTest, ImageBufferOOBWrite) { ;CHECK: %uint_1 = OpConstant %uint 1 ;CHECK: %uint_23 = OpConstant %uint 23 ;CHECK: %uint_2 = OpConstant %uint 2 +;CHECK: %uint_3 = OpConstant %uint 3 ;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float ;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input ;CHECK: %v4uint = OpTypeVector %uint 4 ;CHECK: %uint_5 = OpConstant %uint 5 -;CHECK: %uint_7 = OpConstant %uint 7 ;CHECK: %uint_8 = OpConstant %uint 8 ;CHECK: %uint_9 = OpConstant %uint 9 ;CHECK: %uint_10 = OpConstant %uint 10 @@ -9380,7 +9391,7 @@ TEST_F(InstBindlessTest, ImageBufferOOBWrite) { ;CHECK: OpImageWrite %32 %14 %18 ;CHECK: OpBranch %29 ;CHECK: %31 = OpLabel -;CHECK: %91 = OpFunctionCall %void %33 %uint_34 %uint_3 %uint_0 %23 %25 +;CHECK: %91 = OpFunctionCall %void %33 %uint_34 %uint_7 %uint_0 %23 %25 ;CHECK: OpBranch %29 ;CHECK: %29 = OpLabel OpReturn @@ -9409,19 +9420,19 @@ TEST_F(InstBindlessTest, ImageBufferOOBWrite) { ;CHECK: %62 = OpIAdd %uint %49 %uint_2 ;CHECK: %63 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %62 ;CHECK: OpStore %63 %35 -;CHECK: %64 = OpIAdd %uint %49 %uint_3 -;CHECK: %65 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %64 -;CHECK: OpStore %65 %uint_4 -;CHECK: %68 = OpLoad %v4float %gl_FragCoord -;CHECK: %70 = OpBitcast %v4uint %68 -;CHECK: %71 = OpCompositeExtract %uint %70 0 -;CHECK: %72 = OpIAdd %uint %49 %uint_4 -;CHECK: %73 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %72 -;CHECK: OpStore %73 %71 -;CHECK: %74 = OpCompositeExtract %uint %70 1 -;CHECK: %76 = OpIAdd %uint %49 %uint_5 -;CHECK: %77 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %76 -;CHECK: OpStore %77 %74 +;CHECK: %65 = OpIAdd %uint %49 %uint_3 +;CHECK: %66 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %65 +;CHECK: OpStore %66 %uint_4 +;CHECK: %69 = OpLoad %v4float %gl_FragCoord +;CHECK: %71 = OpBitcast %v4uint %69 +;CHECK: %72 = OpCompositeExtract %uint %71 0 +;CHECK: %73 = OpIAdd %uint %49 %uint_4 +;CHECK: %74 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %73 +;CHECK: OpStore %74 %72 +;CHECK: %75 = OpCompositeExtract %uint %71 1 +;CHECK: %77 = OpIAdd %uint %49 %uint_5 +;CHECK: %78 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %77 +;CHECK: OpStore %78 %75 ;CHECK: %79 = OpIAdd %uint %49 %uint_7 ;CHECK: %80 = OpAccessChain %_ptr_StorageBuffer_uint %44 %uint_1 %79 ;CHECK: OpStore %80 %36 @@ -9500,7 +9511,7 @@ TEST_F(InstBindlessTest, TextureBufferOOBFetch) { ;CHECK: %uint = OpTypeInt 32 0 ;CHECK: %uint_0 = OpConstant %uint 0 ;CHECK: %bool = OpTypeBool -;CHECK: %uint_3 = OpConstant %uint 3 +;CHECK: %uint_6 = OpConstant %uint 6 ;CHECK: %35 = OpTypeFunction %void %uint %uint %uint %uint %uint ;CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint ;CHECK: %_struct_43 = OpTypeStruct %uint %_runtimearr_uint @@ -9512,6 +9523,7 @@ TEST_F(InstBindlessTest, TextureBufferOOBFetch) { ;CHECK: %uint_1 = OpConstant %uint 1 ;CHECK: %uint_23 = OpConstant %uint 23 ;CHECK: %uint_2 = OpConstant %uint 2 +;CHECK: %uint_3 = OpConstant %uint 3 ;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float ;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input ;CHECK: %v4uint = OpTypeVector %uint 4 @@ -9521,7 +9533,7 @@ TEST_F(InstBindlessTest, TextureBufferOOBFetch) { ;CHECK: %uint_9 = OpConstant %uint 9 ;CHECK: %uint_10 = OpConstant %uint 10 ;CHECK: %uint_32 = OpConstant %uint 32 -;CHECK: %93 = OpConstantNull %v4float +;CHECK: %94 = OpConstantNull %v4float %main = OpFunction %void None %3 %5 = OpLabel ;CHECK: OpBranch %21 @@ -9546,11 +9558,11 @@ TEST_F(InstBindlessTest, TextureBufferOOBFetch) { ;CHECK: %33 = OpImageFetch %v4float %32 %17 ;CHECK: OpBranch %29 ;CHECK: %31 = OpLabel -;CHECK: %92 = OpFunctionCall %void %34 %uint_32 %uint_3 %uint_0 %23 %25 +;CHECK: %93 = OpFunctionCall %void %34 %uint_32 %uint_6 %uint_0 %23 %25 ;CHECK: OpBranch %29 ;CHECK: %29 = OpLabel -;CHECK: %94 = OpPhi %v4float %33 %30 %93 %31 -;CHECK: OpStore %x %94 +;CHECK: %95 = OpPhi %v4float %33 %30 %94 %31 +;CHECK: OpStore %x %95 OpReturn OpFunctionEnd ;CHECK: %34 = OpFunction %void None %35 @@ -9577,31 +9589,31 @@ TEST_F(InstBindlessTest, TextureBufferOOBFetch) { ;CHECK: %63 = OpIAdd %uint %50 %uint_2 ;CHECK: %64 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %63 ;CHECK: OpStore %64 %36 -;CHECK: %65 = OpIAdd %uint %50 %uint_3 -;CHECK: %66 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %65 -;CHECK: OpStore %66 %uint_4 -;CHECK: %69 = OpLoad %v4float %gl_FragCoord -;CHECK: %71 = OpBitcast %v4uint %69 -;CHECK: %72 = OpCompositeExtract %uint %71 0 -;CHECK: %73 = OpIAdd %uint %50 %uint_4 -;CHECK: %74 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %73 -;CHECK: OpStore %74 %72 -;CHECK: %75 = OpCompositeExtract %uint %71 1 -;CHECK: %77 = OpIAdd %uint %50 %uint_5 -;CHECK: %78 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %77 -;CHECK: OpStore %78 %75 -;CHECK: %80 = OpIAdd %uint %50 %uint_7 -;CHECK: %81 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %80 -;CHECK: OpStore %81 %37 -;CHECK: %83 = OpIAdd %uint %50 %uint_8 -;CHECK: %84 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %83 -;CHECK: OpStore %84 %38 -;CHECK: %86 = OpIAdd %uint %50 %uint_9 -;CHECK: %87 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %86 -;CHECK: OpStore %87 %39 -;CHECK: %89 = OpIAdd %uint %50 %uint_10 -;CHECK: %90 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %89 -;CHECK: OpStore %90 %40 +;CHECK: %66 = OpIAdd %uint %50 %uint_3 +;CHECK: %67 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %66 +;CHECK: OpStore %67 %uint_4 +;CHECK: %70 = OpLoad %v4float %gl_FragCoord +;CHECK: %72 = OpBitcast %v4uint %70 +;CHECK: %73 = OpCompositeExtract %uint %72 0 +;CHECK: %74 = OpIAdd %uint %50 %uint_4 +;CHECK: %75 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %74 +;CHECK: OpStore %75 %73 +;CHECK: %76 = OpCompositeExtract %uint %72 1 +;CHECK: %78 = OpIAdd %uint %50 %uint_5 +;CHECK: %79 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %78 +;CHECK: OpStore %79 %76 +;CHECK: %81 = OpIAdd %uint %50 %uint_7 +;CHECK: %82 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %81 +;CHECK: OpStore %82 %37 +;CHECK: %84 = OpIAdd %uint %50 %uint_8 +;CHECK: %85 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %84 +;CHECK: OpStore %85 %38 +;CHECK: %87 = OpIAdd %uint %50 %uint_9 +;CHECK: %88 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %87 +;CHECK: OpStore %88 %39 +;CHECK: %90 = OpIAdd %uint %50 %uint_10 +;CHECK: %91 = OpAccessChain %_ptr_StorageBuffer_uint %45 %uint_1 %90 +;CHECK: OpStore %91 %40 ;CHECK: OpBranch %54 ;CHECK: %54 = OpLabel ;CHECK: OpReturn @@ -9669,7 +9681,7 @@ TEST_F(InstBindlessTest, SamplerBufferOOBFetch) { ;CHECK: %uint = OpTypeInt 32 0 ;CHECK: %uint_0 = OpConstant %uint 0 ;CHECK: %bool = OpTypeBool -;CHECK: %uint_3 = OpConstant %uint 3 +;CHECK: %uint_6 = OpConstant %uint 6 ;CHECK: %38 = OpTypeFunction %void %uint %uint %uint %uint %uint ;CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint ;CHECK: %_struct_46 = OpTypeStruct %uint %_runtimearr_uint @@ -9681,6 +9693,7 @@ TEST_F(InstBindlessTest, SamplerBufferOOBFetch) { ;CHECK: %uint_1 = OpConstant %uint 1 ;CHECK: %uint_23 = OpConstant %uint 23 ;CHECK: %uint_2 = OpConstant %uint 2 +;CHECK: %uint_3 = OpConstant %uint 3 ;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float ;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input ;CHECK: %v4uint = OpTypeVector %uint 4 @@ -9690,7 +9703,7 @@ TEST_F(InstBindlessTest, SamplerBufferOOBFetch) { ;CHECK: %uint_9 = OpConstant %uint 9 ;CHECK: %uint_10 = OpConstant %uint 10 ;CHECK: %uint_34 = OpConstant %uint 34 -;CHECK: %96 = OpConstantNull %v4float +;CHECK: %97 = OpConstantNull %v4float %main = OpFunction %void None %3 %5 = OpLabel ;CHECK: OpBranch %23 @@ -9717,11 +9730,11 @@ TEST_F(InstBindlessTest, SamplerBufferOOBFetch) { ;CHECK: %36 = OpImageFetch %v4float %35 %18 ;CHECK: OpBranch %31 ;CHECK: %33 = OpLabel -;CHECK: %95 = OpFunctionCall %void %37 %uint_34 %uint_3 %uint_0 %25 %27 +;CHECK: %96 = OpFunctionCall %void %37 %uint_34 %uint_6 %uint_0 %25 %27 ;CHECK: OpBranch %31 ;CHECK: %31 = OpLabel -;CHECK: %97 = OpPhi %v4float %36 %32 %96 %33 -;CHECK: OpStore %x %97 +;CHECK: %98 = OpPhi %v4float %36 %32 %97 %33 +;CHECK: OpStore %x %98 OpReturn OpFunctionEnd ;CHECK: %37 = OpFunction %void None %38 @@ -9748,31 +9761,31 @@ TEST_F(InstBindlessTest, SamplerBufferOOBFetch) { ;CHECK: %66 = OpIAdd %uint %53 %uint_2 ;CHECK: %67 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %66 ;CHECK: OpStore %67 %39 -;CHECK: %68 = OpIAdd %uint %53 %uint_3 -;CHECK: %69 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %68 -;CHECK: OpStore %69 %uint_4 -;CHECK: %72 = OpLoad %v4float %gl_FragCoord -;CHECK: %74 = OpBitcast %v4uint %72 -;CHECK: %75 = OpCompositeExtract %uint %74 0 -;CHECK: %76 = OpIAdd %uint %53 %uint_4 -;CHECK: %77 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %76 -;CHECK: OpStore %77 %75 -;CHECK: %78 = OpCompositeExtract %uint %74 1 -;CHECK: %80 = OpIAdd %uint %53 %uint_5 -;CHECK: %81 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %80 -;CHECK: OpStore %81 %78 -;CHECK: %83 = OpIAdd %uint %53 %uint_7 -;CHECK: %84 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %83 -;CHECK: OpStore %84 %40 -;CHECK: %86 = OpIAdd %uint %53 %uint_8 -;CHECK: %87 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %86 -;CHECK: OpStore %87 %41 -;CHECK: %89 = OpIAdd %uint %53 %uint_9 -;CHECK: %90 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %89 -;CHECK: OpStore %90 %42 -;CHECK: %92 = OpIAdd %uint %53 %uint_10 -;CHECK: %93 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %92 -;CHECK: OpStore %93 %43 +;CHECK: %69 = OpIAdd %uint %53 %uint_3 +;CHECK: %70 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %69 +;CHECK: OpStore %70 %uint_4 +;CHECK: %73 = OpLoad %v4float %gl_FragCoord +;CHECK: %75 = OpBitcast %v4uint %73 +;CHECK: %76 = OpCompositeExtract %uint %75 0 +;CHECK: %77 = OpIAdd %uint %53 %uint_4 +;CHECK: %78 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %77 +;CHECK: OpStore %78 %76 +;CHECK: %79 = OpCompositeExtract %uint %75 1 +;CHECK: %81 = OpIAdd %uint %53 %uint_5 +;CHECK: %82 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %81 +;CHECK: OpStore %82 %79 +;CHECK: %84 = OpIAdd %uint %53 %uint_7 +;CHECK: %85 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %84 +;CHECK: OpStore %85 %40 +;CHECK: %87 = OpIAdd %uint %53 %uint_8 +;CHECK: %88 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %87 +;CHECK: OpStore %88 %41 +;CHECK: %90 = OpIAdd %uint %53 %uint_9 +;CHECK: %91 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %90 +;CHECK: OpStore %91 %42 +;CHECK: %93 = OpIAdd %uint %53 %uint_10 +;CHECK: %94 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_1 %93 +;CHECK: OpStore %94 %43 ;CHECK: OpBranch %57 ;CHECK: %57 = OpLabel ;CHECK: OpReturn @@ -9847,7 +9860,7 @@ TEST_F(InstBindlessTest, SamplerBufferConstructorOOBFetch) { ;CHECK: %uint = OpTypeInt 32 0 ;CHECK: %uint_0 = OpConstant %uint 0 ;CHECK: %bool = OpTypeBool -;CHECK: %uint_3 = OpConstant %uint 3 +;CHECK: %uint_6 = OpConstant %uint 6 ;CHECK: %44 = OpTypeFunction %void %uint %uint %uint %uint %uint ;CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint ;CHECK: %_struct_52 = OpTypeStruct %uint %_runtimearr_uint @@ -9859,6 +9872,7 @@ TEST_F(InstBindlessTest, SamplerBufferConstructorOOBFetch) { ;CHECK: %uint_1 = OpConstant %uint 1 ;CHECK: %uint_23 = OpConstant %uint 23 ;CHECK: %uint_2 = OpConstant %uint 2 +;CHECK: %uint_3 = OpConstant %uint 3 ;CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float ;CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input ;CHECK: %v4uint = OpTypeVector %uint 4 @@ -9868,7 +9882,7 @@ TEST_F(InstBindlessTest, SamplerBufferConstructorOOBFetch) { ;CHECK: %uint_9 = OpConstant %uint 9 ;CHECK: %uint_10 = OpConstant %uint 10 ;CHECK: %uint_42 = OpConstant %uint 42 -;CHECK: %102 = OpConstantNull %v4float +;CHECK: %103 = OpConstantNull %v4float %main = OpFunction %void None %3 %5 = OpLabel ;CHECK: OpBranch %28 @@ -9898,11 +9912,11 @@ TEST_F(InstBindlessTest, SamplerBufferConstructorOOBFetch) { ;CHECK: %42 = OpImageFetch %v4float %41 %23 ;CHECK: OpBranch %36 ;CHECK: %38 = OpLabel -;CHECK: %101 = OpFunctionCall %void %43 %uint_42 %uint_3 %uint_0 %30 %32 +;CHECK: %102 = OpFunctionCall %void %43 %uint_42 %uint_6 %uint_0 %30 %32 ;CHECK: OpBranch %36 ;CHECK: %36 = OpLabel -;CHECK: %103 = OpPhi %v4float %42 %37 %102 %38 -;CHECK: OpStore %x %103 +;CHECK: %104 = OpPhi %v4float %42 %37 %103 %38 +;CHECK: OpStore %x %104 OpReturn OpFunctionEnd ;CHECK: %43 = OpFunction %void None %44 @@ -9929,31 +9943,31 @@ TEST_F(InstBindlessTest, SamplerBufferConstructorOOBFetch) { ;CHECK: %72 = OpIAdd %uint %59 %uint_2 ;CHECK: %73 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %72 ;CHECK: OpStore %73 %45 -;CHECK: %74 = OpIAdd %uint %59 %uint_3 -;CHECK: %75 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %74 -;CHECK: OpStore %75 %uint_4 -;CHECK: %78 = OpLoad %v4float %gl_FragCoord -;CHECK: %80 = OpBitcast %v4uint %78 -;CHECK: %81 = OpCompositeExtract %uint %80 0 -;CHECK: %82 = OpIAdd %uint %59 %uint_4 -;CHECK: %83 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %82 -;CHECK: OpStore %83 %81 -;CHECK: %84 = OpCompositeExtract %uint %80 1 -;CHECK: %86 = OpIAdd %uint %59 %uint_5 -;CHECK: %87 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %86 -;CHECK: OpStore %87 %84 -;CHECK: %89 = OpIAdd %uint %59 %uint_7 -;CHECK: %90 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %89 -;CHECK: OpStore %90 %46 -;CHECK: %92 = OpIAdd %uint %59 %uint_8 -;CHECK: %93 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %92 -;CHECK: OpStore %93 %47 -;CHECK: %95 = OpIAdd %uint %59 %uint_9 -;CHECK: %96 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %95 -;CHECK: OpStore %96 %48 -;CHECK: %98 = OpIAdd %uint %59 %uint_10 -;CHECK: %99 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %98 -;CHECK: OpStore %99 %49 +;CHECK: %75 = OpIAdd %uint %59 %uint_3 +;CHECK: %76 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %75 +;CHECK: OpStore %76 %uint_4 +;CHECK: %79 = OpLoad %v4float %gl_FragCoord +;CHECK: %81 = OpBitcast %v4uint %79 +;CHECK: %82 = OpCompositeExtract %uint %81 0 +;CHECK: %83 = OpIAdd %uint %59 %uint_4 +;CHECK: %84 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %83 +;CHECK: OpStore %84 %82 +;CHECK: %85 = OpCompositeExtract %uint %81 1 +;CHECK: %87 = OpIAdd %uint %59 %uint_5 +;CHECK: %88 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %87 +;CHECK: OpStore %88 %85 +;CHECK: %90 = OpIAdd %uint %59 %uint_7 +;CHECK: %91 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %90 +;CHECK: OpStore %91 %46 +;CHECK: %93 = OpIAdd %uint %59 %uint_8 +;CHECK: %94 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %93 +;CHECK: OpStore %94 %47 +;CHECK: %96 = OpIAdd %uint %59 %uint_9 +;CHECK: %97 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %96 +;CHECK: OpStore %97 %48 +;CHECK: %99 = OpIAdd %uint %59 %uint_10 +;CHECK: %100 = OpAccessChain %_ptr_StorageBuffer_uint %54 %uint_1 %99 +;CHECK: OpStore %100 %49 ;CHECK: OpBranch %63 ;CHECK: %63 = OpLabel ;CHECK: OpReturn diff --git a/test/opt/instruction_list_test.cpp b/test/opt/instruction_list_test.cpp index e745790a..2c3c2429 100644 --- a/test/opt/instruction_list_test.cpp +++ b/test/opt/instruction_list_test.cpp @@ -35,7 +35,7 @@ class TestInstruction : public Instruction { public: TestInstruction() : Instruction() { created_instructions_.push_back(this); } - ~TestInstruction() { deleted_instructions_.push_back(this); } + ~TestInstruction() override{ deleted_instructions_.push_back(this); } static std::vector<TestInstruction*> created_instructions_; static std::vector<TestInstruction*> deleted_instructions_; diff --git a/test/opt/interp_fixup_test.cpp b/test/opt/interp_fixup_test.cpp new file mode 100644 index 00000000..a43a29c0 --- /dev/null +++ b/test/opt/interp_fixup_test.cpp @@ -0,0 +1,172 @@ +// Copyright (c) 2021 The Khronos Group Inc. +// Copyright (c) 2021 Valve Corporation +// Copyright (c) 2021 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 InterpFixupTest = PassTest<::testing::Test>; + +using ::testing::HasSubstr; + +TEST_F(InterpFixupTest, FixInterpAtSample) { + const std::string text = R"( + OpCapability Shader + OpCapability InterpolationFunction + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %MainPs "MainPs" %i_vPositionOs %_entryPointOutput + OpExecutionMode %MainPs OriginUpperLeft + OpSource HLSL 500 + OpName %MainPs "MainPs" + OpName %i_vPositionOs "i.vPositionOs" + OpName %_entryPointOutput "@entryPointOutput" + OpDecorate %i_vPositionOs Location 0 + OpDecorate %_entryPointOutput Location 0 + %void = OpTypeVoid + %6 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %float_0 = OpConstant %float 0 + %10 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_4 = OpConstant %uint 4 + %bool = OpTypeBool + %int = OpTypeInt 32 1 + %int_1 = OpConstant %int 1 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%i_vPositionOs = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput = OpVariable %_ptr_Output_v4float Output + %MainPs = OpFunction %void None %6 + %19 = OpLabel + %20 = OpLoad %v4float %i_vPositionOs + OpBranch %21 + %21 = OpLabel + %22 = OpPhi %v4float %10 %19 %23 %24 + %25 = OpPhi %uint %uint_0 %19 %26 %24 + %27 = OpULessThan %bool %25 %uint_4 + OpLoopMerge %28 %24 None + OpBranchConditional %27 %24 %28 + %24 = OpLabel + %29 = OpExtInst %v4float %1 InterpolateAtSample %20 %25 +;CHECK: %29 = OpExtInst %v4float %1 InterpolateAtSample %i_vPositionOs %25 + %30 = OpCompositeExtract %float %29 0 + %31 = OpCompositeExtract %float %22 0 + %32 = OpFAdd %float %31 %30 + %23 = OpCompositeInsert %v4float %32 %22 0 + %26 = OpIAdd %uint %25 %int_1 + OpBranch %21 + %28 = OpLabel + OpStore %_entryPointOutput %22 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch<InterpFixupPass>(text, false); +} + +TEST_F(InterpFixupTest, FixInterpAtCentroid) { + const std::string text = R"( + OpCapability Shader + OpCapability InterpolationFunction + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %MainPs "MainPs" %i_vPositionOs %_entryPointOutput + OpExecutionMode %MainPs OriginUpperLeft + OpSource HLSL 500 + OpName %MainPs "MainPs" + OpName %i_vPositionOs "i.vPositionOs" + OpName %_entryPointOutput "@entryPointOutput" + OpDecorate %i_vPositionOs Location 0 + OpDecorate %_entryPointOutput Location 0 + %void = OpTypeVoid + %6 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %float_0 = OpConstant %float 0 + %10 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%i_vPositionOs = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput = OpVariable %_ptr_Output_v4float Output + %MainPs = OpFunction %void None %6 + %13 = OpLabel + %14 = OpLoad %v4float %i_vPositionOs + %15 = OpExtInst %v4float %1 InterpolateAtCentroid %14 +;CHECK: %15 = OpExtInst %v4float %1 InterpolateAtCentroid %i_vPositionOs + %16 = OpCompositeExtract %float %15 0 + %17 = OpCompositeInsert %v4float %16 %10 0 + OpStore %_entryPointOutput %17 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch<InterpFixupPass>(text, false); +} + +TEST_F(InterpFixupTest, FixInterpAtOffset) { + const std::string text = R"( + OpCapability Shader + OpCapability InterpolationFunction + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %MainPs "MainPs" %i_vPositionOs %_entryPointOutput + OpExecutionMode %MainPs OriginUpperLeft + OpSource HLSL 500 + OpName %MainPs "MainPs" + OpName %i_vPositionOs "i.vPositionOs" + OpName %_entryPointOutput "@entryPointOutput" + OpDecorate %i_vPositionOs Location 0 + OpDecorate %_entryPointOutput Location 0 + %void = OpTypeVoid + %6 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %float_0 = OpConstant %float 0 + %10 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %v2float = OpTypeVector %float 2 +%float_0_0625 = OpConstant %float 0.0625 + %13 = OpConstantComposite %v2float %float_0_0625 %float_0_0625 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%i_vPositionOs = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput = OpVariable %_ptr_Output_v4float Output + %MainPs = OpFunction %void None %6 + %16 = OpLabel + %17 = OpLoad %v4float %i_vPositionOs + %18 = OpExtInst %v4float %1 InterpolateAtOffset %17 %13 +;CHECK: %18 = OpExtInst %v4float %1 InterpolateAtOffset %i_vPositionOs %13 + %19 = OpCompositeExtract %float %18 0 + %20 = OpCompositeInsert %v4float %19 %10 0 + OpStore %_entryPointOutput %20 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch<InterpFixupPass>(text, false); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/test/opt/loop_optimizations/unroll_simple.cpp b/test/opt/loop_optimizations/unroll_simple.cpp index 016316ae..6a3cb6ee 100644 --- a/test/opt/loop_optimizations/unroll_simple.cpp +++ b/test/opt/loop_optimizations/unroll_simple.cpp @@ -3401,6 +3401,215 @@ OpFunctionEnd SinglePassRunAndCheck<LoopUnroller>(shader, output, false); } +TEST_F(PassClassTest, UnrollWithPhiReferencesPhi) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %color + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + OpName %main "main" + OpName %color "color" + OpDecorate %color Location 0 + %uint = OpTypeInt 32 0 + %float = OpTypeFloat 32 + %float_0 = OpConstant %float 0 + %float_1 = OpConstant %float 1 + %uint_1 = OpConstant %uint 1 + %uint_3 = OpConstant %uint 3 + %void = OpTypeVoid + %11 = OpTypeFunction %void + %bool = OpTypeBool + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %color = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %11 + %15 = OpLabel + OpBranch %16 + %16 = OpLabel + %17 = OpPhi %float %float_0 %15 %18 %19 + %18 = OpPhi %float %float_1 %15 %20 %19 + %21 = OpPhi %uint %uint_1 %15 %22 %19 + %23 = OpULessThanEqual %bool %21 %uint_3 + OpLoopMerge %24 %19 Unroll + OpBranchConditional %23 %25 %24 + %25 = OpLabel + +; First loop iteration +; CHECK: [[next_phi1_0:%\w+]] = OpFSub %float %float_1 %float_0 + +; Second loop iteration +; CHECK: [[next_phi1_1:%\w+]] = OpFSub %float [[next_phi1_0]] %float_1 + +; Third loop iteration +; CHECK: OpFSub %float [[next_phi1_1]] [[next_phi1_0]] + + %20 = OpFSub %float %18 %17 + OpBranch %19 + %19 = OpLabel + %22 = OpIAdd %uint %21 %uint_1 + OpBranch %16 + %24 = OpLabel + OpReturn + OpFunctionEnd +)"; + + std::unique_ptr<IRContext> context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" + << text << std::endl; + + LoopUnroller loop_unroller; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + SinglePassRunAndMatch<LoopUnroller>(text, true); +} + +TEST_F(PassClassTest, UnrollWithDoublePhiReferencesPhi) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %color + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + OpName %main "main" + OpName %color "color" + OpDecorate %color Location 0 + %uint = OpTypeInt 32 0 + %float = OpTypeFloat 32 + %float_0 = OpConstant %float 0 + %float_1 = OpConstant %float 1 + %uint_1 = OpConstant %uint 1 + %uint_3 = OpConstant %uint 3 + %void = OpTypeVoid + %11 = OpTypeFunction %void + %bool = OpTypeBool + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %color = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %11 + %15 = OpLabel + OpBranch %16 + %16 = OpLabel + %17 = OpPhi %float %float_1 %15 %18 %19 + %18 = OpPhi %float %float_0 %15 %20 %19 + %20 = OpPhi %float %float_1 %15 %21 %19 + %22 = OpPhi %uint %uint_1 %15 %23 %19 + %24 = OpULessThanEqual %bool %22 %uint_3 + OpLoopMerge %25 %19 Unroll + OpBranchConditional %24 %26 %25 + %26 = OpLabel + +; First loop iteration +; CHECK: [[next_phi1_0:%\w+]] = OpFSub %float %float_1 %float_0 +; CHECK: OpFMul %float %float_1 + +; Second loop iteration +; CHECK: [[next_phi1_1:%\w+]] = OpFSub %float [[next_phi1_0]] %float_1 +; CHECK: OpFMul %float %float_0 + +; Third loop iteration +; CHECK: OpFSub %float [[next_phi1_1]] [[next_phi1_0]] +; CHECK: OpFMul %float %float_1 + + %21 = OpFSub %float %20 %18 + %27 = OpFMul %float %17 %21 + OpBranch %19 + %19 = OpLabel + %23 = OpIAdd %uint %22 %uint_1 + OpBranch %16 + %25 = OpLabel + OpReturn + OpFunctionEnd +)"; + + std::unique_ptr<IRContext> context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" + << text << std::endl; + + LoopUnroller loop_unroller; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + SinglePassRunAndMatch<LoopUnroller>(text, true); +} + +TEST_F(PassClassTest, PartialUnrollWithPhiReferencesPhi) { + // With LocalMultiStoreElimPass + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %color + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + OpName %main "main" + OpName %color "color" + OpDecorate %color Location 0 + %uint = OpTypeInt 32 0 + %float = OpTypeFloat 32 + %float_0 = OpConstant %float 0 + %float_1 = OpConstant %float 1 + %uint_1 = OpConstant %uint 1 + %uint_3 = OpConstant %uint 3 + %void = OpTypeVoid + %11 = OpTypeFunction %void + %bool = OpTypeBool + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %color = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %11 + %15 = OpLabel + OpBranch %16 + %16 = OpLabel + %17 = OpPhi %float %float_0 %15 %18 %19 + %18 = OpPhi %float %float_1 %15 %20 %19 + %21 = OpPhi %uint %uint_1 %15 %22 %19 + %23 = OpULessThanEqual %bool %21 %uint_3 + OpLoopMerge %24 %19 Unroll + OpBranchConditional %23 %25 %24 + %25 = OpLabel + +; CHECK: [[phi0_0:%\w+]] = OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[phi1_0:%\w+]] +; CHECK: [[phi1_0]] = OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[sub:%\w+]] + +; CHECK: [[sub]] = OpFSub {{%\w+}} [[phi1_0]] [[phi0_0]] + +; CHECK: [[phi0_1:%\w+]] = OpPhi {{%\w+}} [[phi0_0]] +; CHECK: [[phi1_1:%\w+]] = OpPhi {{%\w+}} [[phi1_0]] + +; CHECK: [[sub:%\w+]] = OpFSub {{%\w+}} [[phi1_1]] [[phi0_1]] + +; CHECK: OpFSub {{%\w+}} [[sub]] [[phi1_1]] + + %20 = OpFSub %float %18 %17 + OpBranch %19 + %19 = OpLabel + %22 = OpIAdd %uint %21 %uint_1 + OpBranch %16 + %24 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr<IRContext> context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" + << text << std::endl; + + LoopUnroller loop_unroller; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndMatch<PartialUnrollerTestPass<2>>(text, true); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/test/opt/scalar_replacement_test.cpp b/test/opt/scalar_replacement_test.cpp index 10738558..8115f5fb 100644 --- a/test/opt/scalar_replacement_test.cpp +++ b/test/opt/scalar_replacement_test.cpp @@ -2197,6 +2197,46 @@ OpFunctionEnd SinglePassRunAndMatch<ScalarReplacementPass>(text, true); } +TEST_F(ScalarReplacementTest, ImageTexelPointer) { + // Test whether the scalar replacement correctly checks the + // OpImageTexelPointer user of an aggregate with an image type. + const std::string text = R"( +; +; CHECK: [[imgTy:%\w+]] = OpTypeImage %uint Buffer 2 0 0 2 R32ui +; CHECK: [[ptrImgTy:%\w+]] = OpTypePointer Function [[imgTy]] +; CHECK: [[img:%\w+]] = OpVariable [[ptrImgTy]] Function +; CHECK: [[imgTexelPtr:%\w+]] = OpImageTexelPointer {{%\w+}} [[img]] %uint_0 %uint_0 +; CHECK: OpAtomicIAdd %uint [[imgTexelPtr]] %uint_1 %uint_0 %uint_1 +; +OpCapability Shader +OpCapability SampledBuffer +OpCapability ImageBuffer +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %1 "main" +OpExecutionMode %1 LocalSize 64 1 1 +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%_ptr_Image_uint = OpTypePointer Image %uint +%type_buffer_image = OpTypeImage %uint Buffer 2 0 0 2 R32ui +%_ptr_Function_type_buffer_image = OpTypePointer Function %type_buffer_image +%image_struct = OpTypeStruct %type_buffer_image %type_buffer_image +%_ptr_Function_image_struct = OpTypePointer Function %image_struct +%func = OpTypeFunction %void +%1 = OpFunction %void None %func +%2 = OpLabel +%3 = OpVariable %_ptr_Function_image_struct Function +%4 = OpAccessChain %_ptr_Function_type_buffer_image %3 %uint_1 +%5 = OpImageTexelPointer %_ptr_Image_uint %4 %uint_0 %uint_0 +%6 = OpAtomicIAdd %uint %5 %uint_1 %uint_0 %uint_1 +OpReturn +OpFunctionEnd + )"; + + SinglePassRunAndMatch<ScalarReplacementPass>(text, false); +} + } // namespace } // namespace opt } // namespace spvtools diff --git a/test/test_fixture.h b/test/test_fixture.h index 436993e2..0c5bfc9c 100644 --- a/test/test_fixture.h +++ b/test/test_fixture.h @@ -46,7 +46,7 @@ class TextToBinaryTestBase : public T { text = {textStr, strlen(textStr)}; } - virtual ~TextToBinaryTestBase() { + ~TextToBinaryTestBase() override { DestroyBinary(); if (diagnostic) spvDiagnosticDestroy(diagnostic); } diff --git a/test/text_to_binary.extension_test.cpp b/test/text_to_binary.extension_test.cpp index 023763b7..08579843 100644 --- a/test/text_to_binary.extension_test.cpp +++ b/test/text_to_binary.extension_test.cpp @@ -905,5 +905,118 @@ INSTANTIATE_TEST_SUITE_P( MakeInstruction(SpvOpDecorate, {1, 5300})}, }))); +// SPV_KHR_linkonce_odr + +INSTANTIATE_TEST_SUITE_P( + SPV_KHR_linkonce_odr, ExtensionRoundTripTest, + Combine( + Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_0, + SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_2), + ValuesIn(std::vector<AssemblyCase>{ + {"OpExtension \"SPV_KHR_linkonce_odr\"\n", + MakeInstruction(SpvOpExtension, + MakeVector("SPV_KHR_linkonce_odr"))}, + {"OpDecorate %1 LinkageAttributes \"foobar\" LinkOnceODR\n", + MakeInstruction(SpvOpDecorate, + Concatenate({{1, SpvDecorationLinkageAttributes}, + MakeVector("foobar"), + {SpvLinkageTypeLinkOnceODR}}))}, + }))); + +// SPV_KHR_expect_assume + +INSTANTIATE_TEST_SUITE_P( + SPV_KHR_expect_assume, ExtensionRoundTripTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_3, + SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_2), + ValuesIn(std::vector<AssemblyCase>{ + {"OpExtension \"SPV_KHR_expect_assume\"\n", + MakeInstruction(SpvOpExtension, + MakeVector("SPV_KHR_expect_assume"))}, + {"OpAssumeTrueKHR %1\n", + MakeInstruction(SpvOpAssumeTrueKHR, {1})}}))); +// SPV_KHR_subgroup_uniform_control_flow + +INSTANTIATE_TEST_SUITE_P( + SPV_KHR_subgroup_uniform_control_flow, ExtensionRoundTripTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_3, + SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_2), + ValuesIn(std::vector<AssemblyCase>{ + {"OpExtension \"SPV_KHR_subgroup_uniform_control_flow\"\n", + MakeInstruction( + SpvOpExtension, + MakeVector("SPV_KHR_subgroup_uniform_control_flow"))}, + {"OpExecutionMode %1 SubgroupUniformControlFlowKHR\n", + MakeInstruction( + SpvOpExecutionMode, + {1, SpvExecutionModeSubgroupUniformControlFlowKHR})}, + }))); + +// SPV_KHR_integer_dot_product + +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), + ValuesIn(std::vector<AssemblyCase>{ + {"OpExtension \"SPV_KHR_integer_dot_product\"\n", + MakeInstruction(SpvOpExtension, + MakeVector("SPV_KHR_integer_dot_product"))}, + {"OpCapability DotProductInputAllKHR\n", + MakeInstruction(SpvOpCapability, + {SpvCapabilityDotProductInputAllKHR})}, + {"OpCapability DotProductInput4x8BitKHR\n", + MakeInstruction(SpvOpCapability, + {SpvCapabilityDotProductInput4x8BitKHR})}, + {"OpCapability DotProductInput4x8BitPackedKHR\n", + MakeInstruction(SpvOpCapability, + {SpvCapabilityDotProductInput4x8BitPackedKHR})}, + {"OpCapability DotProductKHR\n", + MakeInstruction(SpvOpCapability, {SpvCapabilityDotProductKHR})}, + {"%2 = OpSDotKHR %1 %3 %4\n", + MakeInstruction(SpvOpSDotKHR, {1, 2, 3, 4})}, + {"%2 = OpSDotKHR %1 %3 %4 PackedVectorFormat4x8BitKHR\n", + MakeInstruction( + SpvOpSDotKHR, + {1, 2, 3, 4, + SpvPackedVectorFormatPackedVectorFormat4x8BitKHR})}, + {"%2 = OpUDotKHR %1 %3 %4\n", + MakeInstruction(SpvOpUDotKHR, {1, 2, 3, 4})}, + {"%2 = OpUDotKHR %1 %3 %4 PackedVectorFormat4x8BitKHR\n", + MakeInstruction( + SpvOpUDotKHR, + {1, 2, 3, 4, + SpvPackedVectorFormatPackedVectorFormat4x8BitKHR})}, + {"%2 = OpSUDotKHR %1 %3 %4\n", + MakeInstruction(SpvOpSUDotKHR, {1, 2, 3, 4})}, + {"%2 = OpSUDotKHR %1 %3 %4 PackedVectorFormat4x8BitKHR\n", + MakeInstruction( + SpvOpSUDotKHR, + {1, 2, 3, 4, + SpvPackedVectorFormatPackedVectorFormat4x8BitKHR})}, + {"%2 = OpSDotAccSatKHR %1 %3 %4 %5\n", + MakeInstruction(SpvOpSDotAccSatKHR, {1, 2, 3, 4, 5})}, + {"%2 = OpSDotAccSatKHR %1 %3 %4 %5 PackedVectorFormat4x8BitKHR\n", + MakeInstruction( + SpvOpSDotAccSatKHR, + {1, 2, 3, 4, 5, + SpvPackedVectorFormatPackedVectorFormat4x8BitKHR})}, + {"%2 = OpUDotAccSatKHR %1 %3 %4 %5\n", + MakeInstruction(SpvOpUDotAccSatKHR, {1, 2, 3, 4, 5})}, + {"%2 = OpUDotAccSatKHR %1 %3 %4 %5 PackedVectorFormat4x8BitKHR\n", + MakeInstruction( + SpvOpUDotAccSatKHR, + {1, 2, 3, 4, 5, + SpvPackedVectorFormatPackedVectorFormat4x8BitKHR})}, + {"%2 = OpSUDotAccSatKHR %1 %3 %4 %5\n", + MakeInstruction(SpvOpSUDotAccSatKHR, {1, 2, 3, 4, 5})}, + {"%2 = OpSUDotAccSatKHR %1 %3 %4 %5 PackedVectorFormat4x8BitKHR\n", + MakeInstruction( + SpvOpSUDotAccSatKHR, + {1, 2, 3, 4, 5, + SpvPackedVectorFormatPackedVectorFormat4x8BitKHR})}, + }))); + } // namespace } // namespace spvtools diff --git a/test/text_to_binary.mode_setting_test.cpp b/test/text_to_binary.mode_setting_test.cpp index 8ddf4219..647bb3d9 100644 --- a/test/text_to_binary.mode_setting_test.cpp +++ b/test/text_to_binary.mode_setting_test.cpp @@ -189,6 +189,7 @@ INSTANTIATE_TEST_SUITE_P( {CASE(OutputTriangleStrip), {}}, {CASE(VecTypeHint), {96}}, {CASE(ContractionOff), {}}, + {CASE(SubgroupUniformControlFlowKHR), {}}, }))); INSTANTIATE_TEST_SUITE_P( diff --git a/test/val/CMakeLists.txt b/test/val/CMakeLists.txt index b17d1cb1..83249641 100644 --- a/test/val/CMakeLists.txt +++ b/test/val/CMakeLists.txt @@ -38,6 +38,10 @@ add_spvtools_unittest(TARGET val_abcde val_entry_point.cpp val_explicit_reserved_test.cpp val_extensions_test.cpp + val_extension_spv_khr_expect_assume.cpp + val_extension_spv_khr_linkonce_odr.cpp + val_extension_spv_khr_subgroup_uniform_control_flow.cpp + val_extension_spv_khr_integer_dot_product.cpp val_extension_spv_khr_terminate_invocation.cpp val_ext_inst_test.cpp ${VAL_TEST_COMMON_SRCS} diff --git a/test/val/val_atomics_test.cpp b/test/val/val_atomics_test.cpp index fccfabc3..fc3aedbe 100644 --- a/test/val/val_atomics_test.cpp +++ b/test/val/val_atomics_test.cpp @@ -95,12 +95,13 @@ OpFunctionEnd)"; std::string GenerateShaderCode( const std::string& body, const std::string& capabilities_and_extensions = "", + const std::string& extra_defs = "", const std::string& memory_model = "GLSL450") { const std::string execution = R"( OpEntryPoint Fragment %main "main" OpExecutionMode %main OriginUpperLeft )"; - const std::string defintions = R"( + const std::string definitions = R"( %u64 = OpTypeInt 64 0 %s64 = OpTypeInt 64 1 @@ -113,19 +114,20 @@ OpExecutionMode %main OriginUpperLeft %s64_var = OpVariable %s64_ptr Workgroup )"; return GenerateShaderCodeImpl( - body, "OpCapability Int64\n" + capabilities_and_extensions, defintions, - memory_model, execution); + body, "OpCapability Int64\n" + capabilities_and_extensions, + definitions + extra_defs, memory_model, execution); } std::string GenerateShaderComputeCode( const std::string& body, const std::string& capabilities_and_extensions = "", + const std::string& extra_defs = "", const std::string& memory_model = "GLSL450") { const std::string execution = R"( OpEntryPoint GLCompute %main "main" OpExecutionMode %main LocalSize 32 1 1 )"; - const std::string defintions = R"( + const std::string definitions = R"( %u64 = OpTypeInt 64 0 %s64 = OpTypeInt 64 1 @@ -138,8 +140,8 @@ OpExecutionMode %main LocalSize 32 1 1 %s64_var = OpVariable %s64_ptr Workgroup )"; return GenerateShaderCodeImpl( - body, "OpCapability Int64\n" + capabilities_and_extensions, defintions, - memory_model, execution); + body, "OpCapability Int64\n" + capabilities_and_extensions, + definitions + extra_defs, memory_model, execution); } std::string GenerateKernelCode( @@ -222,7 +224,6 @@ TEST_F(ValidateAtomics, AtomicLoadShaderSuccess) { const std::string body = R"( %val1 = OpAtomicLoad %u32 %u32_var %device %relaxed %val2 = OpAtomicLoad %u32 %u32_var %workgroup %acquire -%val3 = OpAtomicLoad %u64 %u64_var %subgroup %sequentially_consistent )"; CompileSuccessfully(GenerateShaderCode(body)); @@ -233,23 +234,56 @@ TEST_F(ValidateAtomics, AtomicLoadKernelSuccess) { const std::string body = R"( %val1 = OpAtomicLoad %f32 %f32_var %device %relaxed %val2 = OpAtomicLoad %u32 %u32_var %workgroup %sequentially_consistent -%val3 = OpAtomicLoad %u64 %u64_var %subgroup %acquire )"; CompileSuccessfully(GenerateKernelCode(body)); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } +TEST_F(ValidateAtomics, AtomicLoadInt64ShaderSuccess) { + const std::string body = R"( +%val1 = OpAtomicLoad %u64 %u64_var %subgroup %sequentially_consistent +)"; + + CompileSuccessfully(GenerateShaderCode(body, "OpCapability Int64Atomics\n")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateAtomics, AtomicLoadInt64KernelSuccess) { + const std::string body = R"( +%val1 = OpAtomicLoad %u64 %u64_var %subgroup %acquire +)"; + + CompileSuccessfully(GenerateKernelCode(body, "OpCapability Int64Atomics\n")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + TEST_F(ValidateAtomics, AtomicLoadInt32VulkanSuccess) { const std::string body = R"( %val1 = OpAtomicLoad %u32 %u32_var %device %relaxed %val2 = OpAtomicLoad %u32 %u32_var %workgroup %acquire +%val3 = OpAtomicLoad %u32 %u32_var %invocation %relaxed )"; CompileSuccessfully(GenerateShaderComputeCode(body), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); } +TEST_F(ValidateAtomics, AtomicLoadVulkanWrongStorageClass) { + const std::string body = R"( +%val1 = OpAtomicLoad %u32 %u32_var %device %relaxed +)"; + + CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-None-04645")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("in Vulkan evironment, Workgroup Storage Class is limited to " + "MeshNV, TaskNV, and GLCompute execution model")); +} + TEST_F(ValidateAtomics, AtomicAddIntVulkanWrongType1) { const std::string body = R"( %val1 = OpAtomicIAdd %f32 %f32_var %device %relaxed %f32_1 @@ -259,7 +293,7 @@ TEST_F(ValidateAtomics, AtomicAddIntVulkanWrongType1) { ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("AtomicIAdd: " - "expected Result Type to be int scalar type")); + "expected Result Type to be integer scalar type")); } TEST_F(ValidateAtomics, AtomicAddIntVulkanWrongType2) { @@ -287,6 +321,32 @@ TEST_F(ValidateAtomics, AtomicAddFloatVulkan) { "AtomicFloat32AddEXT AtomicFloat64AddEXT")); } +TEST_F(ValidateAtomics, AtomicMinFloatVulkan) { + const std::string body = R"( +%val1 = OpAtomicFMinEXT %f32 %f32_var %device %relaxed %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Opcode AtomicFMinEXT requires one of these capabilities: " + "AtomicFloat32MinMaxEXT AtomicFloat64MinMaxEXT AtomicFloat16MinMaxEXT")); +} + +TEST_F(ValidateAtomics, AtomicMaxFloatVulkan) { + const std::string body = R"( +%val1 = OpAtomicFMaxEXT %f32 %f32_var %device %relaxed %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Opcode AtomicFMaxEXT requires one of these capabilities: " + "AtomicFloat32MinMaxEXT AtomicFloat64MinMaxEXT AtomicFloat16MinMaxEXT")); +} + TEST_F(ValidateAtomics, AtomicAddFloatVulkanWrongType1) { const std::string body = R"( %val1 = OpAtomicFAddEXT %f32vec4 %f32vec4_var %device %relaxed %f32_1 @@ -303,6 +363,38 @@ OpExtension "SPV_EXT_shader_atomic_float_add" "expected Result Type to be float scalar type")); } +TEST_F(ValidateAtomics, AtomicMinFloatVulkanWrongType1) { + const std::string body = R"( +%val1 = OpAtomicFMinEXT %f32vec4 %f32vec4_var %device %relaxed %f32_1 +)"; + const std::string extra = R"( +OpCapability AtomicFloat32MinMaxEXT +OpExtension "SPV_EXT_shader_atomic_float_min_max" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicFMinEXT: " + "expected Result Type to be float scalar type")); +} + +TEST_F(ValidateAtomics, AtomicMaxFloatVulkanWrongType1) { + const std::string body = R"( +%val1 = OpAtomicFMaxEXT %f32vec4 %f32vec4_var %device %relaxed %f32_1 +)"; + const std::string extra = R"( +OpCapability AtomicFloat32MinMaxEXT +OpExtension "SPV_EXT_shader_atomic_float_min_max" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicFMaxEXT: " + "expected Result Type to be float scalar type")); +} + TEST_F(ValidateAtomics, AtomicAddFloatVulkanWrongType2) { const std::string body = R"( %val1 = OpAtomicFAddEXT %u32 %u32_var %device %relaxed %u32_1 @@ -319,6 +411,38 @@ OpExtension "SPV_EXT_shader_atomic_float_add" "expected Result Type to be float scalar type")); } +TEST_F(ValidateAtomics, AtomicMinFloatVulkanWrongType2) { + const std::string body = R"( +%val1 = OpAtomicFMinEXT %u32 %u32_var %device %relaxed %u32_1 +)"; + const std::string extra = R"( +OpCapability AtomicFloat32MinMaxEXT +OpExtension "SPV_EXT_shader_atomic_float_min_max" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicFMinEXT: " + "expected Result Type to be float scalar type")); +} + +TEST_F(ValidateAtomics, AtomicMaxFloatVulkanWrongType2) { + const std::string body = R"( +%val1 = OpAtomicFMaxEXT %u32 %u32_var %device %relaxed %u32_1 +)"; + const std::string extra = R"( +OpCapability AtomicFloat32MinMaxEXT +OpExtension "SPV_EXT_shader_atomic_float_min_max" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicFMaxEXT: " + "expected Result Type to be float scalar type")); +} + TEST_F(ValidateAtomics, AtomicAddFloatVulkanWrongType3) { const std::string body = R"( %val1 = OpAtomicFAddEXT %u64 %u64_var %device %relaxed %u64_1 @@ -335,6 +459,38 @@ OpExtension "SPV_EXT_shader_atomic_float_add" "expected Result Type to be float scalar type")); } +TEST_F(ValidateAtomics, AtomicMinFloatVulkanWrongType3) { + const std::string body = R"( +%val1 = OpAtomicFMinEXT %u64 %u64_var %device %relaxed %u64_1 +)"; + const std::string extra = R"( +OpCapability AtomicFloat32MinMaxEXT +OpExtension "SPV_EXT_shader_atomic_float_min_max" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicFMinEXT: " + "expected Result Type to be float scalar type")); +} + +TEST_F(ValidateAtomics, AtomicMaxFloatVulkanWrongType3) { + const std::string body = R"( +%val1 = OpAtomicFMaxEXT %u64 %u64_var %device %relaxed %u64_1 +)"; + const std::string extra = R"( +OpCapability AtomicFloat32MinMaxEXT +OpExtension "SPV_EXT_shader_atomic_float_min_max" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicFMaxEXT: " + "expected Result Type to be float scalar type")); +} + TEST_F(ValidateAtomics, AtomicAddFloatVulkanWrongCapability) { const std::string body = R"( %val1 = OpAtomicFAddEXT %f32 %f32_var %device %relaxed %f32_1 @@ -351,16 +507,162 @@ OpExtension "SPV_EXT_shader_atomic_float_add" "require the AtomicFloat32AddEXT capability")); } +TEST_F(ValidateAtomics, AtomicMinFloatVulkanWrongCapability) { + const std::string body = R"( +%val1 = OpAtomicFMinEXT %f32 %f32_var %device %relaxed %f32_1 +)"; + const std::string extra = R"( +OpCapability AtomicFloat64MinMaxEXT +OpExtension "SPV_EXT_shader_atomic_float_min_max" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicFMinEXT: float min/max atomics " + "require the AtomicFloat32MinMaxEXT capability")); +} + +TEST_F(ValidateAtomics, AtomicMaxFloatVulkanWrongCapability) { + const std::string body = R"( +%val1 = OpAtomicFMaxEXT %f32 %f32_var %device %relaxed %f32_1 +)"; + const std::string extra = R"( +OpCapability AtomicFloat64MinMaxEXT +OpExtension "SPV_EXT_shader_atomic_float_min_max" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicFMaxEXT: float min/max atomics " + "require the AtomicFloat32MinMaxEXT capability")); +} + TEST_F(ValidateAtomics, AtomicAddFloatVulkanSuccess) { const std::string body = R"( %val1 = OpAtomicFAddEXT %f32 %f32_var %device %relaxed %f32_1 +%val2 = OpAtomicFAddEXT %f32 %f32_var %invocation %relaxed %f32_1 )"; const std::string extra = R"( OpCapability AtomicFloat32AddEXT OpExtension "SPV_EXT_shader_atomic_float_add" )"; - CompileSuccessfully(GenerateShaderCode(body, extra), SPV_ENV_VULKAN_1_0); + CompileSuccessfully(GenerateShaderComputeCode(body, extra), + SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateAtomics, AtomicMinFloat16VulkanSuccess) { + const std::string defs = R"( +%f16 = OpTypeFloat 16 +%f16_1 = OpConstant %f16 1 +%f16_ptr = OpTypePointer Workgroup %f16 +%f16_var = OpVariable %f16_ptr Workgroup +)"; + const std::string body = R"( +%val1 = OpAtomicFMinEXT %f16 %f16_var %device %relaxed %f16_1 +)"; + const std::string extra = R"( +OpCapability Float16 +OpCapability AtomicFloat16MinMaxEXT +OpExtension "SPV_EXT_shader_atomic_float_min_max" +)"; + + CompileSuccessfully(GenerateShaderComputeCode(body, extra, defs), + SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateAtomics, AtomicMaxFloat16VulkanSuccess) { + const std::string defs = R"( +%f16 = OpTypeFloat 16 +%f16_1 = OpConstant %f16 1 +%f16_ptr = OpTypePointer Workgroup %f16 +%f16_var = OpVariable %f16_ptr Workgroup +)"; + const std::string body = R"( +%val1 = OpAtomicFMaxEXT %f16 %f16_var %device %relaxed %f16_1 +)"; + const std::string extra = R"( +OpCapability Float16 +OpCapability AtomicFloat16MinMaxEXT +OpExtension "SPV_EXT_shader_atomic_float_min_max" +)"; + + CompileSuccessfully(GenerateShaderComputeCode(body, extra, defs), + SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateAtomics, AtomicMinFloat32VulkanSuccess) { + const std::string body = R"( +%val1 = OpAtomicFMinEXT %f32 %f32_var %device %relaxed %f32_1 +)"; + const std::string extra = R"( +OpCapability AtomicFloat32MinMaxEXT +OpExtension "SPV_EXT_shader_atomic_float_min_max" +)"; + + CompileSuccessfully(GenerateShaderComputeCode(body, extra), + SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateAtomics, AtomicMaxFloat32VulkanSuccess) { + const std::string body = R"( +%val1 = OpAtomicFMaxEXT %f32 %f32_var %device %relaxed %f32_1 +)"; + const std::string extra = R"( +OpCapability AtomicFloat32MinMaxEXT +OpExtension "SPV_EXT_shader_atomic_float_min_max" +)"; + + CompileSuccessfully(GenerateShaderComputeCode(body, extra), + SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateAtomics, AtomicMinFloat64VulkanSuccess) { + const std::string defs = R"( +%f64 = OpTypeFloat 64 +%f64_1 = OpConstant %f64 1 +%f64_ptr = OpTypePointer Workgroup %f64 +%f64_var = OpVariable %f64_ptr Workgroup +)"; + const std::string body = R"( +%val1 = OpAtomicFMinEXT %f64 %f64_var %device %relaxed %f64_1 +)"; + const std::string extra = R"( +OpCapability Float64 +OpCapability AtomicFloat64MinMaxEXT +OpExtension "SPV_EXT_shader_atomic_float_min_max" +)"; + + CompileSuccessfully(GenerateShaderComputeCode(body, extra, defs), + SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateAtomics, AtomicMaxFloat64VulkanSuccess) { + const std::string defs = R"( +%f64 = OpTypeFloat 64 +%f64_1 = OpConstant %f64 1 +%f64_ptr = OpTypePointer Workgroup %f64 +%f64_var = OpVariable %f64_ptr Workgroup +)"; + const std::string body = R"( +%val1 = OpAtomicFMaxEXT %f64 %f64_var %device %relaxed %f64_1 +)"; + const std::string extra = R"( +OpCapability Float64 +OpCapability AtomicFloat64MinMaxEXT +OpExtension "SPV_EXT_shader_atomic_float_min_max" +)"; + + CompileSuccessfully(GenerateShaderComputeCode(body, extra, defs), + SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); } @@ -374,12 +676,27 @@ TEST_F(ValidateAtomics, AtomicLoadFloatVulkan) { ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); } -TEST_F(ValidateAtomics, AtomicStoreFloatVulkan) { +TEST_F(ValidateAtomics, AtomicStoreVulkanWrongStorageClass) { const std::string body = R"( OpAtomicStore %f32_var %device %relaxed %f32_1 )"; CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-None-04645")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("in Vulkan evironment, Workgroup Storage Class is limited to " + "MeshNV, TaskNV, and GLCompute execution model")); +} + +TEST_F(ValidateAtomics, AtomicStoreFloatVulkan) { + const std::string body = R"( +OpAtomicStore %f32_var %device %relaxed %f32_1 +)"; + + CompileSuccessfully(GenerateShaderComputeCode(body), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); } @@ -388,7 +705,7 @@ TEST_F(ValidateAtomics, AtomicExchangeFloatVulkan) { %val2 = OpAtomicExchange %f32 %f32_var %device %relaxed %f32_0 )"; - CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + CompileSuccessfully(GenerateShaderComputeCode(body), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); } @@ -396,6 +713,7 @@ TEST_F(ValidateAtomics, AtomicLoadInt64WithCapabilityVulkanSuccess) { const std::string body = R"( %val1 = OpAtomicLoad %u64 %u64_var %device %relaxed %val2 = OpAtomicLoad %u64 %u64_var %workgroup %acquire + %val3 = OpAtomicLoad %u64 %u64_var %invocation %relaxed )"; CompileSuccessfully( @@ -515,6 +833,21 @@ TEST_F(ValidateAtomics, AtomicLoadVulkanSequentiallyConsistent) { "Release, AcquireRelease and SequentiallyConsistent")); } +TEST_F(ValidateAtomics, AtomicLoadVulkanInvocationSemantics) { + const std::string body = R"( +%val1 = OpAtomicLoad %u32 %u32_var %invocation %acquire +)"; + + CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-None-04641")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("AtomicLoad: Vulkan specification requires Memory Semantics to " + "be None if used with Invocation Memory Scope")); +} + TEST_F(ValidateAtomics, AtomicLoadShaderFloat) { const std::string body = R"( %val1 = OpAtomicLoad %f32 %f32_var %device %relaxed @@ -537,6 +870,45 @@ TEST_F(ValidateAtomics, AtomicLoadVulkanInt64) { "AtomicLoad: 64-bit atomics require the Int64Atomics capability")); } +TEST_F(ValidateAtomics, AtomicLoadKernelInt64) { + const std::string body = R"( +%val1 = OpAtomicLoad %u64 %u64_var %device %relaxed +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "AtomicLoad: 64-bit atomics require the Int64Atomics capability")); +} + +TEST_F(ValidateAtomics, AtomicStoreVulkanInt64) { + const std::string body = R"( +OpAtomicStore %u64_var %device %relaxed %u64_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "AtomicStore: 64-bit atomics require the Int64Atomics capability")); +} + +TEST_F(ValidateAtomics, AtomicStoreKernelInt64) { + const std::string body = R"( +OpAtomicStore %u64_var %device %relaxed %u64_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "AtomicStore: 64-bit atomics require the Int64Atomics capability")); +} + TEST_F(ValidateAtomics, VK_KHR_shader_atomic_int64Success) { const std::string body = R"( %val1 = OpAtomicUMin %u64 %u64_var %device %relaxed %u64_1 @@ -568,8 +940,9 @@ OpAtomicStore %u64_var %device %relaxed %u64_1 OpAtomicStore %s64_var %device %relaxed %s64_1 )"; - CompileSuccessfully(GenerateShaderCode(body, "OpCapability Int64Atomics\n"), - SPV_ENV_VULKAN_1_0); + CompileSuccessfully( + GenerateShaderComputeCode(body, "OpCapability Int64Atomics\n"), + SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); } @@ -593,9 +966,10 @@ TEST_F(ValidateAtomics, AtomicLoadWrongResultType) { CompileSuccessfully(GenerateKernelCode(body)); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("AtomicLoad: " - "expected Result Type to be int or float scalar type")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("AtomicLoad: " + "expected Result Type to be integer or float scalar type")); } TEST_F(ValidateAtomics, AtomicLoadWrongPointerType) { @@ -668,9 +1042,10 @@ OpAtomicStore %u32_var %subgroup %sequentially_consistent %u32_1 TEST_F(ValidateAtomics, AtomicStoreVulkanSuccess) { const std::string body = R"( OpAtomicStore %u32_var %device %release %u32_1 +OpAtomicStore %u32_var %invocation %relaxed %u32_1 )"; - CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + CompileSuccessfully(GenerateShaderComputeCode(body), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); } @@ -719,6 +1094,21 @@ OpAtomicStore %u32_var %device %sequentially_consistent %u32_1 "Acquire, AcquireRelease and SequentiallyConsistent")); } +TEST_F(ValidateAtomics, AtomicStoreVulkanInvocationSemantics) { + const std::string body = R"( +OpAtomicStore %u32_var %invocation %acquire %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-None-04641")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("AtomicStore: Vulkan specification requires Memory Semantics " + "to be None if used with Invocation Memory Scope")); +} + TEST_F(ValidateAtomics, AtomicStoreWrongPointerType) { const std::string body = R"( OpAtomicStore %f32_1 %device %relaxed %f32_1 @@ -740,9 +1130,9 @@ OpAtomicStore %f32vec4_var %device %relaxed %f32_1 ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), - HasSubstr("AtomicStore: " - "expected Pointer to be a pointer to int or float scalar " - "type")); + HasSubstr( + "AtomicStore: " + "expected Pointer to be a pointer to integer or float scalar type")); } TEST_F(ValidateAtomics, AtomicStoreWrongPointerStorageTypeForOpenCL) { @@ -848,9 +1238,10 @@ OpStore %f32vec4_var %f32vec4_0000 CompileSuccessfully(GenerateKernelCode(body)); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), - HasSubstr("AtomicExchange: " - "expected Result Type to be int or float scalar type")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("AtomicExchange: " + "expected Result Type to be integer or float scalar type")); } TEST_F(ValidateAtomics, AtomicExchangeWrongPointerType) { @@ -918,6 +1309,22 @@ OpAtomicStore %f32_var %device %relaxed %f32_1 "expected Value to be of type Result Type")); } +TEST_F(ValidateAtomics, AtomicExchangeVulkanInvocationSemantics) { + const std::string body = R"( +OpAtomicStore %u32_var %invocation %relaxed %u32_1 +%val2 = OpAtomicExchange %u32 %u32_var %invocation %acquire %u32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-None-04641")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("AtomicExchange: Vulkan specification requires Memory " + "Semantics to be None if used with Invocation Memory Scope")); +} + TEST_F(ValidateAtomics, AtomicCompareExchangeShaderSuccess) { const std::string body = R"( OpAtomicStore %u32_var %device %relaxed %u32_1 @@ -930,10 +1337,8 @@ OpAtomicStore %u32_var %device %relaxed %u32_1 TEST_F(ValidateAtomics, AtomicCompareExchangeKernelSuccess) { const std::string body = R"( -OpAtomicStore %f32_var %device %relaxed %f32_1 -%val2 = OpAtomicCompareExchange %f32 %f32_var %device %relaxed %relaxed %f32_0 %f32_1 OpAtomicStore %u32_var %device %relaxed %u32_1 -%val4 = OpAtomicCompareExchange %u32 %u32_var %device %relaxed %relaxed %u32_0 %u32_0 +%val2 = OpAtomicCompareExchange %u32 %u32_var %device %relaxed %relaxed %u32_0 %u32_0 )"; CompileSuccessfully(GenerateKernelCode(body)); @@ -950,7 +1355,7 @@ OpAtomicStore %f32_var %device %relaxed %f32_1 ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("AtomicCompareExchange: " - "expected Result Type to be int scalar type")); + "expected Result Type to be integer scalar type")); } TEST_F(ValidateAtomics, AtomicCompareExchangeWrongResultType) { @@ -963,7 +1368,7 @@ OpStore %f32vec4_var %f32vec4_0000 ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("AtomicCompareExchange: " - "expected Result Type to be int or float scalar type")); + "expected Result Type to be integer scalar type")); } TEST_F(ValidateAtomics, AtomicCompareExchangeWrongPointerType) { @@ -981,7 +1386,7 @@ TEST_F(ValidateAtomics, AtomicCompareExchangeWrongPointerType) { TEST_F(ValidateAtomics, AtomicCompareExchangeWrongPointerDataType) { const std::string body = R"( OpStore %f32vec4_var %f32vec4_0000 -%val2 = OpAtomicCompareExchange %f32 %f32vec4_var %device %relaxed %relaxed %f32_0 %f32_1 +%val2 = OpAtomicCompareExchange %u32 %f32vec4_var %device %relaxed %relaxed %u32_0 %u32_0 )"; CompileSuccessfully(GenerateKernelCode(body)); @@ -994,11 +1399,11 @@ OpStore %f32vec4_var %f32vec4_0000 TEST_F(ValidateAtomics, AtomicCompareExchangeWrongScopeType) { const std::string body = R"( -OpAtomicStore %f32_var %device %relaxed %f32_1 -%val2 = OpAtomicCompareExchange %f32 %f32_var %f32_1 %relaxed %relaxed %f32_0 %f32_0 +OpAtomicStore %u64_var %device %relaxed %u64_1 +%val2 = OpAtomicCompareExchange %u64 %u64_var %u64_1 %relaxed %relaxed %u32_0 %u32_0 )"; - CompileSuccessfully(GenerateKernelCode(body)); + CompileSuccessfully(GenerateKernelCode(body, "OpCapability Int64Atomics\n")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("AtomicCompareExchange: expected scope to be a 32-bit " @@ -1007,8 +1412,8 @@ OpAtomicStore %f32_var %device %relaxed %f32_1 TEST_F(ValidateAtomics, AtomicCompareExchangeWrongMemorySemanticsType1) { const std::string body = R"( -OpAtomicStore %f32_var %device %relaxed %f32_1 -%val2 = OpAtomicCompareExchange %f32 %f32_var %device %f32_1 %relaxed %f32_0 %f32_0 +OpAtomicStore %u32_var %device %relaxed %u32_1 +%val2 = OpAtomicCompareExchange %u32 %u32_var %device %f32_1 %relaxed %u32_0 %u32_0 )"; CompileSuccessfully(GenerateKernelCode(body)); @@ -1020,8 +1425,8 @@ OpAtomicStore %f32_var %device %relaxed %f32_1 TEST_F(ValidateAtomics, AtomicCompareExchangeWrongMemorySemanticsType2) { const std::string body = R"( -OpAtomicStore %f32_var %device %relaxed %f32_1 -%val2 = OpAtomicCompareExchange %f32 %f32_var %device %relaxed %f32_1 %f32_0 %f32_0 +OpAtomicStore %u32_var %device %relaxed %u32_1 +%val2 = OpAtomicCompareExchange %u32 %u32_var %device %relaxed %f32_1 %u32_0 %u32_0 )"; CompileSuccessfully(GenerateKernelCode(body)); @@ -1033,8 +1438,8 @@ OpAtomicStore %f32_var %device %relaxed %f32_1 TEST_F(ValidateAtomics, AtomicCompareExchangeUnequalRelease) { const std::string body = R"( -OpAtomicStore %f32_var %device %relaxed %f32_1 -%val2 = OpAtomicCompareExchange %f32 %f32_var %device %relaxed %release %f32_0 %f32_0 +OpAtomicStore %u32_var %device %relaxed %u32_1 +%val2 = OpAtomicCompareExchange %u32 %u32_var %device %relaxed %release %u32_0 %u32_0 )"; CompileSuccessfully(GenerateKernelCode(body)); @@ -1046,8 +1451,8 @@ OpAtomicStore %f32_var %device %relaxed %f32_1 TEST_F(ValidateAtomics, AtomicCompareExchangeWrongValueType) { const std::string body = R"( -OpAtomicStore %f32_var %device %relaxed %f32_1 -%val2 = OpAtomicCompareExchange %f32 %f32_var %device %relaxed %relaxed %u32_0 %f32_1 +OpAtomicStore %u32_var %device %relaxed %u32_1 +%val2 = OpAtomicCompareExchange %u32 %u32_var %device %relaxed %relaxed %f32_1 %u32_0 )"; CompileSuccessfully(GenerateKernelCode(body)); @@ -1059,8 +1464,8 @@ OpAtomicStore %f32_var %device %relaxed %f32_1 TEST_F(ValidateAtomics, AtomicCompareExchangeWrongComparatorType) { const std::string body = R"( -OpAtomicStore %f32_var %device %relaxed %f32_1 -%val2 = OpAtomicCompareExchange %f32 %f32_var %device %relaxed %relaxed %f32_0 %u32_1 +OpAtomicStore %u32_var %device %relaxed %u32_1 +%val2 = OpAtomicCompareExchange %u32 %u32_var %device %relaxed %relaxed %u32_0 %f32_0 )"; CompileSuccessfully(GenerateKernelCode(body)); @@ -1090,7 +1495,39 @@ OpAtomicStore %f32_var %device %relaxed %f32_1 ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("AtomicCompareExchangeWeak: " - "expected Result Type to be int scalar type")); + "expected Result Type to be integer scalar type")); +} + +TEST_F(ValidateAtomics, AtomicCompareExchangeVulkanInvocationSemanticsEqual) { + const std::string body = R"( +OpAtomicStore %u32_var %device %relaxed %u32_1 +%val2 = OpAtomicCompareExchange %u32 %u32_var %invocation %release %relaxed %u32_0 %u32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-None-04641")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("AtomicCompareExchange: Vulkan specification requires Memory " + "Semantics to be None if used with Invocation Memory Scope")); +} + +TEST_F(ValidateAtomics, AtomicCompareExchangeVulkanInvocationSemanticsUnequal) { + const std::string body = R"( +OpAtomicStore %u32_var %device %relaxed %u32_1 +%val2 = OpAtomicCompareExchange %u32 %u32_var %invocation %relaxed %acquire %u32_0 %u32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-None-04641")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("AtomicCompareExchange: Vulkan specification requires Memory " + "Semantics to be None if used with Invocation Memory Scope")); } TEST_F(ValidateAtomics, AtomicArithmeticsSuccess) { @@ -1157,7 +1594,7 @@ TEST_F(ValidateAtomics, AtomicFlagTestAndSetNotIntPointer) { EXPECT_THAT( getDiagnosticString(), HasSubstr("AtomicFlagTestAndSet: " - "expected Pointer to point to a value of 32-bit int type")); + "expected Pointer to point to a value of 32-bit integer type")); } TEST_F(ValidateAtomics, AtomicFlagTestAndSetNotInt32Pointer) { @@ -1165,12 +1602,12 @@ TEST_F(ValidateAtomics, AtomicFlagTestAndSetNotInt32Pointer) { %val1 = OpAtomicFlagTestAndSet %bool %u64_var %device %relaxed )"; - CompileSuccessfully(GenerateKernelCode(body)); + CompileSuccessfully(GenerateKernelCode(body, "OpCapability Int64Atomics\n")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), HasSubstr("AtomicFlagTestAndSet: " - "expected Pointer to point to a value of 32-bit int type")); + "expected Pointer to point to a value of 32-bit integer type")); } TEST_F(ValidateAtomics, AtomicFlagTestAndSetWrongScopeType) { @@ -1231,7 +1668,7 @@ OpAtomicFlagClear %f32_var %device %relaxed EXPECT_THAT( getDiagnosticString(), HasSubstr("AtomicFlagClear: " - "expected Pointer to point to a value of 32-bit int type")); + "expected Pointer to point to a value of 32-bit integer type")); } TEST_F(ValidateAtomics, AtomicFlagClearNotInt32Pointer) { @@ -1239,12 +1676,12 @@ TEST_F(ValidateAtomics, AtomicFlagClearNotInt32Pointer) { OpAtomicFlagClear %u64_var %device %relaxed )"; - CompileSuccessfully(GenerateKernelCode(body)); + CompileSuccessfully(GenerateKernelCode(body, "OpCapability Int64Atomics\n")); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT( getDiagnosticString(), HasSubstr("AtomicFlagClear: " - "expected Pointer to point to a value of 32-bit int type")); + "expected Pointer to point to a value of 32-bit integer type")); } TEST_F(ValidateAtomics, AtomicFlagClearWrongScopeType) { @@ -1342,7 +1779,7 @@ OpCapability VulkanMemoryModelKHR OpExtension "SPV_KHR_vulkan_memory_model" )"; - CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"), + CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"), SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); @@ -1361,7 +1798,7 @@ OpCapability VulkanMemoryModelKHR OpExtension "SPV_KHR_vulkan_memory_model" )"; - CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"), + CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"), SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); @@ -1381,7 +1818,7 @@ OpCapability VulkanMemoryModelKHR OpExtension "SPV_KHR_vulkan_memory_model" )"; - CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"), + CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"), SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); @@ -1401,7 +1838,7 @@ OpCapability VulkanMemoryModelKHR OpExtension "SPV_KHR_vulkan_memory_model" )"; - CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"), + CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"), SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); @@ -1421,7 +1858,7 @@ OpCapability VulkanMemoryModelKHR OpExtension "SPV_KHR_vulkan_memory_model" )"; - CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"), + CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"), SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); @@ -1441,7 +1878,7 @@ OpCapability VulkanMemoryModelKHR OpExtension "SPV_KHR_vulkan_memory_model" )"; - CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"), + CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"), SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); @@ -1461,7 +1898,7 @@ OpCapability VulkanMemoryModelKHR OpExtension "SPV_KHR_vulkan_memory_model" )"; - CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"), + CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"), SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); @@ -1480,7 +1917,7 @@ OpCapability VulkanMemoryModelKHR OpExtension "SPV_KHR_vulkan_memory_model" )"; - CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"), + CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"), SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); @@ -1499,7 +1936,7 @@ OpCapability VulkanMemoryModelKHR OpExtension "SPV_KHR_vulkan_memory_model" )"; - CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"), + CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"), SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); @@ -1518,7 +1955,7 @@ OpCapability VulkanMemoryModelKHR OpExtension "SPV_KHR_vulkan_memory_model" )"; - CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"), + CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"), SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); @@ -1537,7 +1974,28 @@ OpCapability VulkanMemoryModelKHR OpExtension "SPV_KHR_vulkan_memory_model" )"; - CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"), + CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"), + SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("SequentiallyConsistent memory semantics cannot be " + "used with the VulkanKHR memory model.")); +} + +TEST_F(ValidateAtomics, VulkanMemoryModelBanSequentiallyConsistentAtomicFMinEXT) { + const std::string body = R"( +%max = OpAtomicFMinEXT %f32 %f32_var %workgroup %sequentially_consistent %f32_0 +)"; + + const std::string extra = R"( +OpCapability VulkanMemoryModelKHR +OpCapability AtomicFloat32MinMaxEXT +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_EXT_shader_atomic_float_min_max" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"), SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); @@ -1556,7 +2014,7 @@ OpCapability VulkanMemoryModelKHR OpExtension "SPV_KHR_vulkan_memory_model" )"; - CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"), + CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"), SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); @@ -1575,7 +2033,28 @@ OpCapability VulkanMemoryModelKHR OpExtension "SPV_KHR_vulkan_memory_model" )"; - CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"), + CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"), + SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("SequentiallyConsistent memory semantics cannot be " + "used with the VulkanKHR memory model.")); +} + +TEST_F(ValidateAtomics, VulkanMemoryModelBanSequentiallyConsistentAtomicFMaxEXT) { + const std::string body = R"( +%max = OpAtomicFMaxEXT %f32 %f32_var %workgroup %sequentially_consistent %f32_0 +)"; + + const std::string extra = R"( +OpCapability VulkanMemoryModelKHR +OpCapability AtomicFloat32MinMaxEXT +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_EXT_shader_atomic_float_min_max" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"), SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); @@ -1594,7 +2073,7 @@ OpCapability VulkanMemoryModelKHR OpExtension "SPV_KHR_vulkan_memory_model" )"; - CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"), + CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"), SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); @@ -1613,7 +2092,7 @@ OpCapability VulkanMemoryModelKHR OpExtension "SPV_KHR_vulkan_memory_model" )"; - CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"), + CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"), SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); @@ -1632,7 +2111,7 @@ OpCapability VulkanMemoryModelKHR OpExtension "SPV_KHR_vulkan_memory_model" )"; - CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"), + CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"), SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); @@ -1856,7 +2335,7 @@ OpCapability VulkanMemoryModelKHR OpExtension "SPV_KHR_vulkan_memory_model" )"; - CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"), + CompileSuccessfully(GenerateShaderComputeCode(body, extra, "", "VulkanKHR"), SPV_ENV_VULKAN_1_1); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); } @@ -1984,7 +2463,7 @@ TEST_F(ValidateAtomics, VulkanMemoryModelDeviceScopeBad) { OpExtension "SPV_KHR_vulkan_memory_model" )"; - CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"), + CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"), SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); @@ -2004,7 +2483,7 @@ OpCapability VulkanMemoryModelDeviceScopeKHR OpExtension "SPV_KHR_vulkan_memory_model" )"; - CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"), + CompileSuccessfully(GenerateShaderCode(body, extra, "", "VulkanKHR"), SPV_ENV_UNIVERSAL_1_3); EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); } diff --git a/test/val/val_barriers_test.cpp b/test/val/val_barriers_test.cpp index 46f5b5a1..1178ca02 100644 --- a/test/val/val_barriers_test.cpp +++ b/test/val/val_barriers_test.cpp @@ -346,6 +346,8 @@ OpControlBarrier %device %workgroup %none CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-None-04636")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("ControlBarrier: in Vulkan environment Execution Scope " "is limited to Workgroup and Subgroup")); } @@ -388,9 +390,10 @@ OpControlBarrier %subgroup %cross_device %none "cannot be CrossDevice")); } -TEST_F(ValidateBarriers, OpControlBarrierVulkan1p1WorkgroupNonComputeFailure) { +TEST_F(ValidateBarriers, + OpControlBarrierVulkan1p1WorkgroupNonComputeMemoryFailure) { const std::string body = R"( -OpControlBarrier %workgroup %workgroup %acquire +OpControlBarrier %subgroup %workgroup %acquire )"; CompileSuccessfully(GenerateVulkanVertexShaderCode(body), SPV_ENV_VULKAN_1_1); @@ -402,7 +405,23 @@ OpControlBarrier %workgroup %workgroup %acquire "and GLCompute execution model")); } -TEST_F(ValidateBarriers, OpControlBarrierVulkan1p1WorkgroupNonComputeSuccess) { +TEST_F(ValidateBarriers, + OpControlBarrierVulkan1p1WorkgroupNonComputeExecutionFailure) { + const std::string body = R"( +OpControlBarrier %workgroup %subgroup %acquire +)"; + + CompileSuccessfully(GenerateVulkanVertexShaderCode(body), SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-None-04637")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("in Vulkan environment, Workgroup execution scope is " + "only for TaskNV, MeshNV, TessellationControl, and " + "GLCompute execution models")); +} + +TEST_F(ValidateBarriers, OpControlBarrierVulkan1p1WorkgroupComputeSuccess) { const std::string body = R"( OpControlBarrier %workgroup %workgroup %acquire )"; @@ -411,6 +430,39 @@ OpControlBarrier %workgroup %workgroup %acquire ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); } +TEST_F(ValidateBarriers, OpControlBarrierVulkan1p1WorkgroupNonComputeSuccess) { + const std::string body = R"( +OpControlBarrier %subgroup %subgroup %acquire +)"; + + CompileSuccessfully(GenerateVulkanVertexShaderCode(body), SPV_ENV_VULKAN_1_1); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); +} + +TEST_F(ValidateBarriers, OpControlBarrierVulkanInvocationSuccess) { + const std::string body = R"( +OpControlBarrier %workgroup %invocation %none +)"; + + CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateBarriers, OpControlBarrierVulkanInvocationFailure) { + const std::string body = R"( +OpControlBarrier %workgroup %invocation %acquire +)"; + + CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-None-04641")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("ControlBarrier: Vulkan specification requires Memory " + "Semantics to be None if used with Invocation Memory Scope")); +} + TEST_F(ValidateBarriers, OpControlBarrierAcquireAndRelease) { const std::string body = R"( OpControlBarrier %device %device %acquire_and_release_uniform @@ -459,9 +511,13 @@ OpControlBarrier %workgroup %workgroup %acquire_release SPV_ENV_VULKAN_1_1); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpControlBarrier execution scope must be Subgroup for " - "Fragment, Vertex, Geometry and TessellationEvaluation " - "execution models")); + AnyVUID("VUID-StandaloneSpirv-OpControlBarrier-04682")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpControlBarrier execution scope must be Subgroup for Fragment, " + "Vertex, Geometry, TessellationEvaluation, RayGeneration, " + "Intersection, AnyHit, ClosestHit, and Miss execution models")); } TEST_F(ValidateBarriers, OpControlBarrierSubgroupExecutionFragment1p0) { @@ -497,9 +553,13 @@ OpControlBarrier %workgroup %workgroup %acquire_release SPV_ENV_VULKAN_1_1); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpControlBarrier execution scope must be Subgroup for " - "Fragment, Vertex, Geometry and TessellationEvaluation " - "execution models")); + AnyVUID("VUID-StandaloneSpirv-OpControlBarrier-04682")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpControlBarrier execution scope must be Subgroup for Fragment, " + "Vertex, Geometry, TessellationEvaluation, RayGeneration, " + "Intersection, AnyHit, ClosestHit, and Miss execution models")); } TEST_F(ValidateBarriers, OpControlBarrierSubgroupExecutionVertex1p0) { @@ -537,9 +597,13 @@ OpControlBarrier %workgroup %workgroup %acquire_release SPV_ENV_VULKAN_1_1); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpControlBarrier execution scope must be Subgroup for " - "Fragment, Vertex, Geometry and TessellationEvaluation " - "execution models")); + AnyVUID("VUID-StandaloneSpirv-OpControlBarrier-04682")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpControlBarrier execution scope must be Subgroup for Fragment, " + "Vertex, Geometry, TessellationEvaluation, RayGeneration, " + "Intersection, AnyHit, ClosestHit, and Miss execution models")); } TEST_F(ValidateBarriers, OpControlBarrierSubgroupExecutionGeometry1p0) { @@ -580,9 +644,13 @@ OpControlBarrier %workgroup %workgroup %acquire_release SPV_ENV_VULKAN_1_1); ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); EXPECT_THAT(getDiagnosticString(), - HasSubstr("OpControlBarrier execution scope must be Subgroup for " - "Fragment, Vertex, Geometry and TessellationEvaluation " - "execution models")); + AnyVUID("VUID-StandaloneSpirv-OpControlBarrier-04682")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpControlBarrier execution scope must be Subgroup for Fragment, " + "Vertex, Geometry, TessellationEvaluation, RayGeneration, " + "Intersection, AnyHit, ClosestHit, and Miss execution models")); } TEST_F(ValidateBarriers, @@ -1464,6 +1532,8 @@ TEST_F(ValidateBarriers, OpControlBarrierShaderCallRayGenFailure) { ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_1)); EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-None-04636")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("in Vulkan environment Execution Scope is limited to " "Workgroup and Subgroup")); } diff --git a/test/val/val_builtins_test.cpp b/test/val/val_builtins_test.cpp index bbcdbb11..2116fff2 100644 --- a/test/val/val_builtins_test.cpp +++ b/test/val/val_builtins_test.cpp @@ -3658,23 +3658,25 @@ OpFunctionEnd INSTANTIATE_TEST_SUITE_P( SubgroupInvocationIdAndSizeNotU32, ValidateVulkanSubgroupBuiltIns, - Combine(Values("SubgroupLocalInvocationId", "SubgroupSize"), - Values("GLCompute"), Values("Input"), Values("%f32"), - Values("VUID-SubgroupLocalInvocationId-SubgroupLocalInvocationId-" - "04381 VUID-SubgroupSize-SubgroupSize-04383"), - Values(TestResult(SPV_ERROR_INVALID_DATA, - "needs to be a 32-bit int")))); + Combine( + Values("SubgroupLocalInvocationId", "SubgroupSize"), + Values("GLCompute"), Values("Input"), Values("%f32"), + Values("VUID-SubgroupLocalInvocationId-SubgroupLocalInvocationId-04381 " + "VUID-SubgroupSize-SubgroupSize-04383"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit int")))); INSTANTIATE_TEST_SUITE_P( SubgroupInvocationIdAndSizeNotInput, ValidateVulkanSubgroupBuiltIns, - Combine(Values("SubgroupLocalInvocationId", "SubgroupSize"), - Values("GLCompute"), Values("Output", "Workgroup", "Private"), - Values("%u32"), - Values("VUID-SubgroupLocalInvocationId-SubgroupLocalInvocationId-" - "04380 VUID-SubgroupSize-SubgroupSize-04382"), - Values(TestResult( - SPV_ERROR_INVALID_DATA, - "to be only used for variables with Input storage class")))); + Combine( + Values("SubgroupLocalInvocationId", "SubgroupSize"), + Values("GLCompute"), Values("Output", "Workgroup", "Private"), + Values("%u32"), + Values("VUID-SubgroupLocalInvocationId-SubgroupLocalInvocationId-04380 " + "VUID-SubgroupSize-SubgroupSize-04382"), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "to be only used for variables with Input storage class")))); INSTANTIATE_TEST_SUITE_P( SubgroupInvocationIdAndSizeOk, ValidateVulkanSubgroupBuiltIns, diff --git a/test/val/val_cfg_test.cpp b/test/val/val_cfg_test.cpp index 9698fb1c..6ae2ee6d 100644 --- a/test/val/val_cfg_test.cpp +++ b/test/val/val_cfg_test.cpp @@ -3520,7 +3520,10 @@ OpFunctionEnd CompileSuccessfully(text); EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpSwitch must be preceeded by an OpSelectionMerge instruction")); } TEST_F(ValidateCFG, MissingMergeSwitchBad2) { @@ -3544,7 +3547,10 @@ OpFunctionEnd CompileSuccessfully(text); EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); - EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpSwitch must be preceeded by an OpSelectionMerge instruction")); } TEST_F(ValidateCFG, MissingMergeOneBranchToMergeGood) { @@ -3594,7 +3600,7 @@ OpFunctionEnd EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); } -TEST_F(ValidateCFG, MissingMergeOneTargetSwitchGood) { +TEST_F(ValidateCFG, MissingMergeOneTargetSwitchBad) { const std::string text = R"( OpCapability Shader OpCapability Linkage @@ -3612,10 +3618,14 @@ OpFunctionEnd )"; CompileSuccessfully(text); - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpSwitch must be preceeded by an OpSelectionMerge instruction")); } -TEST_F(ValidateCFG, MissingMergeOneUnseenTargetSwitchGood) { +TEST_F(ValidateCFG, MissingMergeOneUnseenTargetSwitchBad) { const std::string text = R"( OpCapability Shader OpCapability Linkage @@ -3640,7 +3650,11 @@ OpFunctionEnd )"; CompileSuccessfully(text); - EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpSwitch must be preceeded by an OpSelectionMerge instruction")); } TEST_F(ValidateCFG, MissingMergeLoopBreakGood) { @@ -4350,6 +4364,41 @@ TEST_F(ValidateCFG, PhiOnVoid) { HasSubstr("OpPhi must not have void result type")); } +TEST_F(ValidateCFG, InvalidExitSingleBlockLoop) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %5 "BAD" +%void = OpTypeVoid +%bool = OpTypeBool +%undef = OpUndef %bool +%void_fn = OpTypeFunction %void +%fn = OpFunction %void None %void_fn +%1 = OpLabel +OpBranch %2 +%2 = OpLabel +OpLoopMerge %3 %4 None +OpBranchConditional %undef %3 %5 +%5 = OpLabel +OpLoopMerge %6 %5 None +OpBranchConditional %undef %5 %4 +%6 = OpLabel +OpReturn +%4 = OpLabel +OpBranch %2 +%3 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("block <ID> 1[%BAD] exits the continue headed by <ID> " + "1[%BAD], but not via a structured exit")); +} + } // namespace } // namespace val } // namespace spvtools diff --git a/test/val/val_decoration_test.cpp b/test/val/val_decoration_test.cpp index cbf6d7c1..096fd172 100644 --- a/test/val/val_decoration_test.cpp +++ b/test/val/val_decoration_test.cpp @@ -4992,6 +4992,8 @@ TEST_F(ValidateDecorations, UniformDecorationWithScopeIdV14VulkanEnv) { EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4)); EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-None-04636")); + EXPECT_THAT(getDiagnosticString(), HasSubstr(": in Vulkan environment Execution Scope is limited to " "Workgroup and Subgroup")); } diff --git a/test/val/val_ext_inst_test.cpp b/test/val/val_ext_inst_test.cpp index 683a76f5..b73ec341 100644 --- a/test/val/val_ext_inst_test.cpp +++ b/test/val/val_ext_inst_test.cpp @@ -5204,6 +5204,49 @@ TEST_F(ValidateExtInst, GlslStd450InterpolateAtCentroidSuccess) { ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } +TEST_F(ValidateExtInst, GlslStd450InterpolateAtCentroidInternalSuccess) { + const std::string body = R"( +%ld1 = OpLoad %f32 %f32_input +%val1 = OpExtInst %f32 %extinst InterpolateAtCentroid %ld1 +%ld2 = OpLoad %f32vec2 %f32vec2_input +%val2 = OpExtInst %f32vec2 %extinst InterpolateAtCentroid %ld2 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "OpCapability InterpolationFunction\n")); + getValidatorOptions()->before_hlsl_legalization = true; + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtInst, GlslStd450InterpolateAtCentroidInternalInvalidDataF32) { + const std::string body = R"( +%ld1 = OpLoad %f32 %f32_input +%val1 = OpExtInst %f32 %extinst InterpolateAtCentroid %ld1 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "OpCapability InterpolationFunction\n")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 InterpolateAtCentroid: " + "expected Interpolant to be a pointer")); +} + +TEST_F(ValidateExtInst, + GlslStd450InterpolateAtCentroidInternalInvalidDataF32Vec2) { + const std::string body = R"( +%ld2 = OpLoad %f32vec2 %f32vec2_input +%val2 = OpExtInst %f32vec2 %extinst InterpolateAtCentroid %ld2 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "OpCapability InterpolationFunction\n")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 InterpolateAtCentroid: " + "expected Interpolant to be a pointer")); +} + TEST_F(ValidateExtInst, GlslStd450InterpolateAtCentroidNoCapability) { const std::string body = R"( %val1 = OpExtInst %f32 %extinst InterpolateAtCentroid %f32_input @@ -5308,6 +5351,49 @@ TEST_F(ValidateExtInst, GlslStd450InterpolateAtSampleSuccess) { ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } +TEST_F(ValidateExtInst, GlslStd450InterpolateAtSampleInternalSuccess) { + const std::string body = R"( +%ld1 = OpLoad %f32 %f32_input +%val1 = OpExtInst %f32 %extinst InterpolateAtSample %ld1 %u32_1 +%ld2 = OpLoad %f32vec2 %f32vec2_input +%val2 = OpExtInst %f32vec2 %extinst InterpolateAtSample %ld2 %u32_1 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "OpCapability InterpolationFunction\n")); + getValidatorOptions()->before_hlsl_legalization = true; + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtInst, GlslStd450InterpolateAtSampleInternalInvalidDataF32) { + const std::string body = R"( +%ld1 = OpLoad %f32 %f32_input +%val1 = OpExtInst %f32 %extinst InterpolateAtSample %ld1 %u32_1 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "OpCapability InterpolationFunction\n")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 InterpolateAtSample: " + "expected Interpolant to be a pointer")); +} + +TEST_F(ValidateExtInst, + GlslStd450InterpolateAtSampleInternalInvalidDataF32Vec2) { + const std::string body = R"( +%ld2 = OpLoad %f32vec2 %f32vec2_input +%val2 = OpExtInst %f32vec2 %extinst InterpolateAtSample %ld2 %u32_1 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "OpCapability InterpolationFunction\n")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 InterpolateAtSample: " + "expected Interpolant to be a pointer")); +} + TEST_F(ValidateExtInst, GlslStd450InterpolateAtSampleNoCapability) { const std::string body = R"( %val1 = OpExtInst %f32 %extinst InterpolateAtSample %f32_input %u32_1 @@ -5438,6 +5524,48 @@ TEST_F(ValidateExtInst, GlslStd450InterpolateAtOffsetSuccess) { ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } +TEST_F(ValidateExtInst, GlslStd450InterpolateAtOffsetInternalSuccess) { + const std::string body = R"( +%ld1 = OpLoad %f32 %f32_input +%val1 = OpExtInst %f32 %extinst InterpolateAtOffset %ld1 %f32vec2_01 +%ld2 = OpLoad %f32vec2 %f32vec2_input +%val2 = OpExtInst %f32vec2 %extinst InterpolateAtOffset %ld2 %f32vec2_01 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "OpCapability InterpolationFunction\n")); + getValidatorOptions()->before_hlsl_legalization = true; + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtInst, GlslStd450InterpolateAtOffsetInternalInvalidDataF32) { + const std::string body = R"( +%ld1 = OpLoad %f32 %f32_input +%val1 = OpExtInst %f32 %extinst InterpolateAtOffset %ld1 %f32vec2_01 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "OpCapability InterpolationFunction\n")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 InterpolateAtOffset: " + "expected Interpolant to be a pointer")); +} + +TEST_F(ValidateExtInst, GlslStd450InterpolateAtOffsetInternalInvalidDataF32Vec2) { + const std::string body = R"( +%ld2 = OpLoad %f32vec2 %f32vec2_input +%val2 = OpExtInst %f32vec2 %extinst InterpolateAtOffset %ld2 %f32vec2_01 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "OpCapability InterpolationFunction\n")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 InterpolateAtOffset: " + "expected Interpolant to be a pointer")); +} + TEST_F(ValidateExtInst, GlslStd450InterpolateAtOffsetNoCapability) { const std::string body = R"( %val1 = OpExtInst %f32 %extinst InterpolateAtOffset %f32_input %f32vec2_01 diff --git a/test/val/val_extension_spv_khr_expect_assume.cpp b/test/val/val_extension_spv_khr_expect_assume.cpp new file mode 100644 index 00000000..6ece15d1 --- /dev/null +++ b/test/val/val_extension_spv_khr_expect_assume.cpp @@ -0,0 +1,310 @@ +// Copyright (c) 2020 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. + +// Tests for OpExtension validator rules. + +#include <string> +#include <vector> + +#include "gmock/gmock.h" +#include "source/enum_string_mapping.h" +#include "source/extensions.h" +#include "source/spirv_target_env.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::HasSubstr; +using ::testing::Values; +using ::testing::ValuesIn; + +using ValidateSpvExpectAssumeKHR = spvtest::ValidateBase<bool>; + +TEST_F(ValidateSpvExpectAssumeKHR, Valid) { + const std::string str = R"( + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpCapability ExpectAssumeKHR + OpExtension "SPV_KHR_expect_assume" + OpMemoryModel Physical32 OpenCL + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + + %bool = OpTypeBool + %true = OpConstantTrue %bool + %undef = OpUndef %bool + + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + + %v2bool = OpTypeVector %bool 2 + %v2uint = OpTypeVector %uint 2 + + %null_v2bool = OpConstantNull %v2bool + %null_v2uint = OpConstantNull %v2uint + + %main = OpFunction %void None %voidfn + %entry = OpLabel + OpAssumeTrueKHR %true + OpAssumeTrueKHR %undef ; probably undefined behaviour + %bool_val = OpExpectKHR %bool %true %true + %uint_val = OpExpectKHR %uint %uint_1 %uint_2 ; a bad expectation + %v2bool_val = OpExpectKHR %v2bool %null_v2bool %null_v2bool + %v2uint_val = OpExpectKHR %v2uint %null_v2uint %null_v2uint + OpReturn + OpFunctionEnd + +)"; + CompileSuccessfully(str.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateSpvExpectAssumeKHR, RequiresExtension) { + const std::string str = R"( + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpCapability ExpectAssumeKHR + OpMemoryModel Physical32 OpenCL + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + + %bool = OpTypeBool + %true = OpConstantTrue %bool + %undef = OpUndef %bool + + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + + %main = OpFunction %void None %voidfn + %entry = OpLabel + OpAssumeTrueKHR %true + OpAssumeTrueKHR %undef ; probably undefined behaviour + %val = OpExpectKHR %uint %uint_1 %uint_2 ; a bad expectation + OpReturn + OpFunctionEnd + +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Capability: operand ExpectAssumeKHR(5629) requires " + "one of these extensions: SPV_KHR_expect_assume")); +} + +TEST_F(ValidateSpvExpectAssumeKHR, + AssumeTrueKHR_RequiresExpectAssumeCapability) { + const std::string str = R"( + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpExtension "SPV_KHR_expect_assume" + OpMemoryModel Physical32 OpenCL + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + + %bool = OpTypeBool + %true = OpConstantTrue %bool + %undef = OpUndef %bool + + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + + %main = OpFunction %void None %voidfn + %entry = OpLabel + OpAssumeTrueKHR %true + OpAssumeTrueKHR %undef ; probably undefined behaviour + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Opcode AssumeTrueKHR requires one of these " + "capabilities: ExpectAssumeKHR \n" + " OpAssumeTrueKHR %true\n")); +} + +TEST_F(ValidateSpvExpectAssumeKHR, AssumeTrueKHR_OperandMustBeBool) { + const std::string str = R"( + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpCapability ExpectAssumeKHR + OpExtension "SPV_KHR_expect_assume" + OpMemoryModel Physical32 OpenCL + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + + %bool = OpTypeBool + %true = OpConstantTrue %bool + %undef = OpUndef %bool + + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + + %main = OpFunction %void None %voidfn + %entry = OpLabel + OpAssumeTrueKHR %uint_1 ; bad type + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Value operand of OpAssumeTrueKHR must be a boolean scalar\n" + " OpAssumeTrueKHR %uint_1\n")); +} + +TEST_F(ValidateSpvExpectAssumeKHR, ExpectKHR_RequiresExpectAssumeCapability) { + const std::string str = R"( + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpExtension "SPV_KHR_expect_assume" + OpMemoryModel Physical32 OpenCL + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + + %bool = OpTypeBool + %true = OpConstantTrue %bool + %undef = OpUndef %bool + + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + + %main = OpFunction %void None %voidfn + %entry = OpLabel + %val = OpExpectKHR %uint %uint_1 %uint_2 ; a bad expectation + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Opcode ExpectKHR requires one of these capabilities: " + "ExpectAssumeKHR \n" + " %11 = OpExpectKHR %uint %uint_1 %uint_2\n")); +} + +TEST_F(ValidateSpvExpectAssumeKHR, ExpectKHR_ResultMustBeBoolOrIntScalar) { + const std::string str = R"( + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpCapability ExpectAssumeKHR + OpExtension "SPV_KHR_expect_assume" + OpMemoryModel Physical32 OpenCL + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + + %float = OpTypeFloat 32 + + %float_0 = OpConstant %float 0 + + %main = OpFunction %void None %voidfn + %entry = OpLabel + %val = OpExpectKHR %float %float_0 %float_0 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result of OpExpectKHR must be a scalar or vector of " + "integer type or boolean type\n" + " %7 = OpExpectKHR %float %float_0 %float_0\n")); +} + +TEST_F(ValidateSpvExpectAssumeKHR, ExpectKHR_Value0MustMatchResultType) { + const std::string str = R"( + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpCapability ExpectAssumeKHR + OpExtension "SPV_KHR_expect_assume" + OpMemoryModel Physical32 OpenCL + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + + %uint = OpTypeInt 32 0 + %float = OpTypeFloat 32 + %float_0 = OpConstant %float 0 + + %main = OpFunction %void None %voidfn + %entry = OpLabel + %val = OpExpectKHR %uint %float_0 %float_0 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Type of Value operand of OpExpectKHR does not match " + "the result type \n" + " %8 = OpExpectKHR %uint %float_0 %float_0\n")); +} + +TEST_F(ValidateSpvExpectAssumeKHR, ExpectKHR_Value1MustMatchResultType) { + const std::string str = R"( + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpCapability ExpectAssumeKHR + OpExtension "SPV_KHR_expect_assume" + OpMemoryModel Physical32 OpenCL + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %float = OpTypeFloat 32 + %float_0 = OpConstant %float 0 + + %main = OpFunction %void None %voidfn + %entry = OpLabel + %val = OpExpectKHR %uint %uint_0 %float_0 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Type of ExpectedValue operand of OpExpectKHR does not " + "match the result type \n" + " %9 = OpExpectKHR %uint %uint_0 %float_0\n")); +} + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/test/val/val_extension_spv_khr_integer_dot_product.cpp b/test/val/val_extension_spv_khr_integer_dot_product.cpp new file mode 100644 index 00000000..e0e6896c --- /dev/null +++ b/test/val/val_extension_spv_khr_integer_dot_product.cpp @@ -0,0 +1,1330 @@ +// Copyright (c) 2020 Google Inc. +// Copyright (c) 2021 Arm Ltd. +// +// 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 <ostream> +#include <sstream> +#include <string> +#include <vector> + +#include "gmock/gmock.h" +#include "source/enum_string_mapping.h" +#include "source/extensions.h" +#include "source/spirv_target_env.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::HasSubstr; +using ::testing::Values; + +struct Case { + std::vector<std::string> caps; + std::string inst; + std::string result_type; + std::string op0_type; + std::string op1_type; + std::string acc_type; // can be empty + bool packed; + std::string expected_error; // empty for no error. +}; + +inline std::ostream& operator<<(std::ostream& out, Case c) { + out << "\nSPV_KHR_integer_dot_product Case{{"; + bool first = true; + for (const auto& cap : c.caps) { + if (!first) { + out << " "; + } + first = false; + out << cap; + } + out << "} "; + out << c.inst << " "; + out << c.result_type << " "; + out << c.op0_type << " "; + out << c.op1_type << " "; + out << "'" << c.acc_type << "' "; + out << (c.packed ? "packed " : "unpacked "); + out << "err'" << c.expected_error << "'"; + return out; +} + +std::string AssemblyForCase(const Case& c) { + std::ostringstream ss; + ss << "OpCapability Shader\n"; + for (auto& cap : c.caps) { + ss << "OpCapability " << cap << "\n"; + } + ss << R"( + OpExtension "SPV_KHR_integer_dot_product" + OpMemoryModel Logical Simple + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %int = OpTypeInt 32 1 + + %v2uint = OpTypeVector %uint 2 + %v3uint = OpTypeVector %uint 3 + %v4uint = OpTypeVector %uint 4 + %v2int = OpTypeVector %int 2 + %v3int = OpTypeVector %int 3 + %v4int = OpTypeVector %int 4 + + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + + %v2uint_0 = OpConstantComposite %v2uint %uint_0 %uint_0 + %v2uint_1 = OpConstantComposite %v2uint %uint_1 %uint_1 + %v3uint_0 = OpConstantComposite %v3uint %uint_0 %uint_0 %uint_0 + %v3uint_1 = OpConstantComposite %v3uint %uint_1 %uint_1 %uint_1 + %v4uint_0 = OpConstantComposite %v4uint %uint_0 %uint_0 %uint_0 %uint_0 + %v4uint_1 = OpConstantComposite %v4uint %uint_1 %uint_1 %uint_1 %uint_1 + + %v2int_0 = OpConstantComposite %v2int %int_0 %int_0 + %v2int_1 = OpConstantComposite %v2int %int_1 %int_1 + %v3int_0 = OpConstantComposite %v3int %int_0 %int_0 %int_0 + %v3int_1 = OpConstantComposite %v3int %int_1 %int_1 %int_1 + %v4int_0 = OpConstantComposite %v4int %int_0 %int_0 %int_0 %int_0 + %v4int_1 = OpConstantComposite %v4int %int_1 %int_1 %int_1 %int_1 +)"; + + bool use8bit = false; + for (auto& cap : c.caps) { + if (cap == "DotProductInput4x8BitKHR") { + use8bit = true; + } + if (cap == "Int8") { + use8bit = true; + } + } + if (use8bit) { + ss << R"( + %uchar = OpTypeInt 8 0 + %char = OpTypeInt 8 1 + + %v4uchar = OpTypeVector %uchar 4 + %v4char = OpTypeVector %char 4 + + %uchar_0 = OpConstant %uchar 0 + %uchar_1 = OpConstant %uchar 1 + %char_0 = OpConstant %char 0 + %char_1 = OpConstant %char 1 + + %v4uchar_0 = OpConstantComposite %v4uchar %uchar_0 %uint_0 %uchar_0 %uchar_0 + %v4uchar_1 = OpConstantComposite %v4uchar %uchar_1 %uchar_1 %uchar_1 %uchar_1 + %v4char_0 = OpConstantComposite %v4char %char_0 %char_0 %char_0 %char_0 + %v4char_1 = OpConstantComposite %v4char %char_1 %char_1 %char_1 %char_1 + + )"; + } + + ss << R"( + + %main = OpFunction %void None %voidfn + %entry = OpLabel + %result = )" + << c.inst << " " << c.result_type << " "; + ss << c.op0_type << "_0 "; + ss << c.op1_type << "_1 "; + if (!c.acc_type.empty()) { + ss << c.acc_type << "_0 "; + } + if (c.packed) { + ss << "PackedVectorFormat4x8BitKHR"; + } + ss << "\nOpReturn\nOpFunctionEnd\n\n"; + return ss.str(); +} + +using ValidateSpvKHRIntegerDotProduct = spvtest::ValidateBase<Case>; + +TEST_P(ValidateSpvKHRIntegerDotProduct, Valid) { + const auto& c = GetParam(); + const auto& assembly = AssemblyForCase(c); + CompileSuccessfully(assembly); + if (c.expected_error.empty()) { + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString(); + } else { + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(c.expected_error)); + } +} + +// UDot +INSTANTIATE_TEST_SUITE_P( + Valid_UDot, ValidateSpvKHRIntegerDotProduct, + ::testing::Values(Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpUDotKHR", + "%uint", + "%v2uint", + "%v2uint", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpUDotKHR", + "%uint", + "%v3uint", + "%v3uint", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpUDotKHR", + "%uint", + "%v4uint", + "%v4uint", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpUDotKHR", + "%uchar", // match width + "%v4uchar", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpUDotKHR", + "%uint", // wider width + "%v4uchar", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpUDotKHR", + "%uchar", // match width + "%v4uchar", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpUDotKHR", + "%uint", // wider width + "%v4uchar", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR", + "Int8"}, + "OpUDotKHR", + "%uchar", // matches packed component type + "%uint", + "%uint", + "", + true, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR"}, + "OpUDotKHR", + "%uint", + "%uint", + "%uint", + "", + true, + ""})); + +// SDot result signed args signed signed +INSTANTIATE_TEST_SUITE_P( + Valid_SDot_signed_signed_signed, ValidateSpvKHRIntegerDotProduct, + ::testing::Values(Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSDotKHR", + "%int", + "%v2int", + "%v2int", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSDotKHR", + "%int", + "%v3int", + "%v3int", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSDotKHR", + "%int", + "%v4int", + "%v4int", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSDotKHR", + "%char", // match width + "%v4char", + "%v4char", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSDotKHR", + "%int", // wider width + "%v4char", + "%v4char", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSDotKHR", + "%char", // match width + "%v4char", + "%v4char", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSDotKHR", + "%int", // wider width + "%v4char", + "%v4char", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR", + "Int8"}, + "OpSDotKHR", + "%char", // matches packed component type + "%int", + "%int", + "", + true, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR"}, + "OpSDotKHR", + "%int", + "%int", + "%int", + "", + true, + ""})); + +// SDot result unsigned args signed unsigned +INSTANTIATE_TEST_SUITE_P( + Valid_SDot_unsigned_signed_unsigned, ValidateSpvKHRIntegerDotProduct, + ::testing::Values(Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSDotKHR", + "%uint", + "%v2int", + "%v2uint", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSDotKHR", + "%uint", + "%v3int", + "%v3uint", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSDotKHR", + "%uint", + "%v4int", + "%v4uint", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSDotKHR", + "%uchar", // match width + "%v4char", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSDotKHR", + "%uint", // wider width + "%v4char", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSDotKHR", + "%uchar", // match width + "%v4char", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSDotKHR", + "%uint", // wider width + "%v4char", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR", + "Int8"}, + "OpSDotKHR", + "%uchar", // matches packed component type + "%int", + "%uint", + "", + true, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR"}, + "OpSDotKHR", + "%uint", + "%int", + "%uint", + "", + true, + ""})); + +// SDot result signed args signed unsigned +INSTANTIATE_TEST_SUITE_P( + Valid_SDot_signed_signed_unsigned, ValidateSpvKHRIntegerDotProduct, + ::testing::Values(Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSDotKHR", + "%int", + "%v2int", + "%v2uint", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSDotKHR", + "%int", + "%v3int", + "%v3uint", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSDotKHR", + "%int", + "%v4int", + "%v4uint", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSDotKHR", + "%char", // match width + "%v4char", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSDotKHR", + "%int", // wider width + "%v4char", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSDotKHR", + "%char", // match width + "%v4char", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSDotKHR", + "%int", // wider width + "%v4char", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR", + "Int8"}, + "OpSDotKHR", + "%char", // matches packed component type + "%int", + "%uint", + "", + true, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR"}, + "OpSDotKHR", + "%int", + "%int", + "%uint", + "", + true, + ""})); + +// SUDot result signed args unsigned unsigned +INSTANTIATE_TEST_SUITE_P( + Valid_SUDot_signed_unsigned_unsigned, ValidateSpvKHRIntegerDotProduct, + ::testing::Values(Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSUDotKHR", + "%int", + "%v2uint", + "%v2uint", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSUDotKHR", + "%int", + "%v3uint", + "%v3uint", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSUDotKHR", + "%int", + "%v4uint", + "%v4uint", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSUDotKHR", + "%char", // match width + "%v4uchar", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSUDotKHR", + "%int", // wider width + "%v4uchar", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSUDotKHR", + "%char", // match width + "%v4uchar", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSUDotKHR", + "%int", // wider width + "%v4uchar", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR", + "Int8"}, + "OpSUDotKHR", + "%char", // matches packed component type + "%uint", + "%uint", + "", + true, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR"}, + "OpSUDotKHR", + "%int", + "%uint", + "%uint", + "", + true, + ""})); + +// SUDot result signed args signed unsigned +INSTANTIATE_TEST_SUITE_P( + Valid_SUDot_signed_signed_unsigned, ValidateSpvKHRIntegerDotProduct, + ::testing::Values(Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSUDotKHR", + "%int", + "%v2int", + "%v2uint", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSUDotKHR", + "%int", + "%v3int", + "%v3uint", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSUDotKHR", + "%int", + "%v4int", + "%v4uint", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSUDotKHR", + "%char", // match width + "%v4char", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSUDotKHR", + "%int", // wider width + "%v4char", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSUDotKHR", + "%char", // match width + "%v4char", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSUDotKHR", + "%int", // wider width + "%v4char", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR", + "Int8"}, + "OpSUDotKHR", + "%char", // matches packed component type + "%int", + "%uint", + "", + true, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR"}, + "OpSUDotKHR", + "%int", + "%int", + "%uint", + "", + true, + ""})); + +// SUDot result unsigned args unsigned unsigned +INSTANTIATE_TEST_SUITE_P( + Valid_SUDot_unsigned_unsigned_unsigned, ValidateSpvKHRIntegerDotProduct, + ::testing::Values(Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSUDotKHR", + "%uint", + "%v2uint", + "%v2uint", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSUDotKHR", + "%uint", + "%v3uint", + "%v3uint", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSUDotKHR", + "%uint", + "%v4uint", + "%v4uint", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSUDotKHR", + "%uchar", // match width + "%v4uchar", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSUDotKHR", + "%uint", // wider width + "%v4uchar", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSUDotKHR", + "%uchar", // match width + "%v4uchar", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSUDotKHR", + "%uint", // wider width + "%v4uchar", + "%v4uchar", + "", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR", + "Int8"}, + "OpSUDotKHR", + "%uchar", // matches packed component type + "%uint", + "%uint", + "", + true, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR"}, + "OpSUDotKHR", + "%uint", + "%uint", + "%uint", + "", + true, + ""})); + +// UDotAccSat +INSTANTIATE_TEST_SUITE_P( + Valid_UDotAccSat, ValidateSpvKHRIntegerDotProduct, + ::testing::Values(Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpUDotAccSatKHR", + "%uint", + "%v2uint", + "%v2uint", + "%uint", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpUDotAccSatKHR", + "%uint", + "%v3uint", + "%v3uint", + "%uint", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpUDotAccSatKHR", + "%uint", + "%v4uint", + "%v4uint", + "%uint", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpUDotAccSatKHR", + "%uchar", // match width + "%v4uchar", + "%v4uchar", + "%uint", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpUDotAccSatKHR", + "%uint", // wider width + "%v4uchar", + "%v4uchar", + "%uint", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpUDotAccSatKHR", + "%uchar", // match width + "%v4uchar", + "%v4uchar", + "%uchar", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpUDotAccSatKHR", + "%uint", // wider width + "%v4uchar", + "%v4uchar", + "%uint", + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR", + "Int8"}, + "OpUDotAccSatKHR", + "%uchar", // matches packed component type + "%uint", + "%uint", + "%uchar", + true, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR"}, + "OpUDotAccSatKHR", + "%uint", + "%uint", + "%uint", + "%uint", + true, + ""})); + +// SDotAccSat result signed args signed signed +INSTANTIATE_TEST_SUITE_P( + Valid_SDotAccSat_signed_signed_signed, ValidateSpvKHRIntegerDotProduct, + ::testing::Values(Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSDotAccSatKHR", + "%int", + "%v2int", + "%v2int", + "%int", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSDotAccSatKHR", + "%int", + "%v3int", + "%v3int", + "%int", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSDotAccSatKHR", + "%int", + "%v4int", + "%v4int", + "%int", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSDotAccSatKHR", + "%char", // match width + "%v4char", + "%v4char", + "%char", // match width + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSDotAccSatKHR", + "%int", // wider width + "%v4char", + "%v4char", + "%int", // wider width + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSDotAccSatKHR", + "%char", // match width + "%v4char", + "%v4char", + "%char", // match width + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSDotAccSatKHR", + "%int", // wider width + "%v4char", + "%v4char", + "%int", // wider width + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR", + "Int8"}, + "OpSDotAccSatKHR", + "%char", // matches packed component type + "%int", + "%int", + "%char", // matches packed component type + true, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR"}, + "OpSDotAccSatKHR", + "%int", + "%int", + "%int", + "%int", + true, + ""})); + +// SDotAccSat result unsigned args signed unsigned +INSTANTIATE_TEST_SUITE_P( + Valid_SDotAccSat_unsigned_signed_unsigned, ValidateSpvKHRIntegerDotProduct, + ::testing::Values(Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSDotAccSatKHR", + "%uint", + "%v2int", + "%v2uint", + "%uint", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSDotAccSatKHR", + "%uint", + "%v3int", + "%v3uint", + "%uint", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSDotAccSatKHR", + "%uint", + "%v4int", + "%v4uint", + "%uint", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSDotAccSatKHR", + "%uchar", // match width + "%v4char", + "%v4uchar", + "%uchar", // match width + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSDotAccSatKHR", + "%uint", // wider width + "%v4char", + "%v4uchar", + "%uint", // wider width + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSDotAccSatKHR", + "%uchar", // match width + "%v4char", + "%v4uchar", + "%uchar", // match width + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSDotAccSatKHR", + "%uint", // wider width + "%v4char", + "%v4uchar", + "%uint", // wider width + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR", + "Int8"}, + "OpSDotAccSatKHR", + "%uchar", // matches packed component type + "%int", + "%uint", + "%uchar", // matches packed component type + true, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR"}, + "OpSDotAccSatKHR", + "%uint", + "%int", + "%uint", + "%uint", + true, + ""})); + +// SDotAccSat result signed args signed unsigned +INSTANTIATE_TEST_SUITE_P( + Valid_SDotAccSat_signed_signed_unsigned, ValidateSpvKHRIntegerDotProduct, + ::testing::Values(Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSDotAccSatKHR", + "%int", + "%v2int", + "%v2uint", + "%int", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSDotAccSatKHR", + "%int", + "%v3int", + "%v3uint", + "%int", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSDotAccSatKHR", + "%int", + "%v4int", + "%v4uint", + "%int", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSDotAccSatKHR", + "%char", // match width + "%v4char", + "%v4uchar", + "%char", // match width + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSDotAccSatKHR", + "%int", // wider width + "%v4char", + "%v4uchar", + "%int", // wider width + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSDotAccSatKHR", + "%char", // match width + "%v4char", + "%v4uchar", + "%char", // match width + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSDotAccSatKHR", + "%int", // wider width + "%v4char", + "%v4uchar", + "%int", // wider width + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR", + "Int8"}, + "OpSDotAccSatKHR", + "%char", // matches packed component type + "%int", + "%uint", + "%char", // matches packed component type + true, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR"}, + "OpSDotAccSatKHR", + "%int", + "%int", + "%uint", + "%int", + true, + ""})); + +// SUDotAccSat result signed args unsigned unsigned +INSTANTIATE_TEST_SUITE_P( + Valid_SUDotAccSat_signed_unsigned_unsigned, ValidateSpvKHRIntegerDotProduct, + ::testing::Values(Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSUDotAccSatKHR", + "%int", + "%v2uint", + "%v2uint", + "%int", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSUDotAccSatKHR", + "%int", + "%v3uint", + "%v3uint", + "%int", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSUDotAccSatKHR", + "%int", + "%v4uint", + "%v4uint", + "%int", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSUDotAccSatKHR", + "%char", // match width + "%v4uchar", + "%v4uchar", + "%char", // match width + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSUDotAccSatKHR", + "%int", // wider width + "%v4uchar", + "%v4uchar", + "%int", // wider width + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSUDotAccSatKHR", + "%char", // match width + "%v4uchar", + "%v4uchar", + "%char", // match width + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSUDotAccSatKHR", + "%int", // wider width + "%v4uchar", + "%v4uchar", + "%int", // wider width + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR", + "Int8"}, + "OpSUDotAccSatKHR", + "%char", // matches packed component type + "%uint", + "%uint", + "%char", // matches packed component type + true, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR"}, + "OpSUDotAccSatKHR", + "%int", + "%uint", + "%uint", + "%int", + true, + ""})); + +// SUDotAccSat result signed args signed unsigned +INSTANTIATE_TEST_SUITE_P( + Valid_SUDotAccSat_signed_signed_unsigned, ValidateSpvKHRIntegerDotProduct, + ::testing::Values(Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSUDotAccSatKHR", + "%int", + "%v2int", + "%v2uint", + "%int", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSUDotAccSatKHR", + "%int", + "%v3int", + "%v3uint", + "%int", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSUDotAccSatKHR", + "%int", + "%v4int", + "%v4uint", + "%int", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSUDotAccSatKHR", + "%char", // match width + "%v4char", + "%v4uchar", + "%char", // match width + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSUDotAccSatKHR", + "%int", // wider width + "%v4char", + "%v4uchar", + "%int", // wider width + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSUDotAccSatKHR", + "%char", // match width + "%v4char", + "%v4uchar", + "%char", // match width + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSUDotAccSatKHR", + "%int", // wider width + "%v4char", + "%v4uchar", + "%int", // wider width + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR", + "Int8"}, + "OpSUDotAccSatKHR", + "%char", // matches packed component type + "%int", + "%uint", + "%char", // matches packed component type + true, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR"}, + "OpSUDotAccSatKHR", + "%int", + "%int", + "%uint", + "%int", + true, + ""})); + +// SUDotAccSat result unsigned args unsigned unsigned +INSTANTIATE_TEST_SUITE_P( + Valid_SUDotAccSat_unsigned_unsigned_unsigned, + ValidateSpvKHRIntegerDotProduct, + ::testing::Values(Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSUDotAccSatKHR", + "%uint", + "%v2uint", + "%v2uint", + "%uint", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSUDotAccSatKHR", + "%uint", + "%v3uint", + "%v3uint", + "%uint", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR"}, + "OpSUDotAccSatKHR", + "%uint", + "%v4uint", + "%v4uint", + "%uint", + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSUDotAccSatKHR", + "%uchar", // match width + "%v4uchar", + "%v4uchar", + "%uchar", // match width + false, + ""}, + Case{{"DotProductKHR", "DotProductInputAllKHR", "Int8"}, + "OpSUDotAccSatKHR", + "%uint", // wider width + "%v4uchar", + "%v4uchar", + "%uint", // wider width + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSUDotAccSatKHR", + "%uchar", // match width + "%v4uchar", + "%v4uchar", + "%uchar", // match width + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitKHR"}, + "OpSUDotAccSatKHR", + "%uint", // wider width + "%v4uchar", + "%v4uchar", + "%uint", // wider width + false, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR", + "Int8"}, + "OpSUDotAccSatKHR", + "%uchar", // matches packed component type + "%uint", + "%uint", + "%uchar", // matches packed component type + true, + ""}, + Case{{"DotProductKHR", "DotProductInput4x8BitPackedKHR"}, + "OpSUDotAccSatKHR", + "%uint", + "%uint", + "%uint", + "%uint", + true, + ""})); + +using ValidateSpvKHRIntegerDotProductSimple = ::testing::Test; + +TEST(ValidateSpvKHRIntegerDotProductSimple, DISABLED_RequiresExtension) { + FAIL(); +} + +TEST(ValidateSpvKHRIntegerDotProductSimple, DISABLED_Invalid_ResultTooNarrow) { + // Test across all the instructions. + FAIL(); +} + +TEST(ValidateSpvKHRIntegerDotProductSimple, + DISABLED_Invalid_UDot_OperandTypesMatch) { + FAIL(); +} + +TEST(ValidateSpvKHRIntegerDotProductSimple, + DISABLED_Invalid_SDot_OperandTypesMatchExceptSignedness) { + FAIL(); +} + +TEST(ValidateSpvKHRIntegerDotProductSimple, + DISABLED_Invalid_SUDot_OperandTypesMatchExceptSignedness) { + FAIL(); +} + +TEST(ValidateSpvKHRIntegerDotProductSimple, + DISABLED_Invalid_UDotAccSat_OperandTypesMatch) { + FAIL(); +} + +TEST(ValidateSpvKHRIntegerDotProductSimple, + DISABLED_Invalid_SDotAccSat_OperandTypesMatchExceptSignedness) { + FAIL(); +} + +TEST(ValidateSpvKHRIntegerDotProductSimple, + DISABLED_Invalid_SUDotAccSat_OperandTypesMatchExceptSignedness) { + FAIL(); +} + +TEST(ValidateSpvKHRIntegerDotProductSimple, + DISABLED_Invalid_UDot_RequiresUnsigned) { + FAIL(); +} + +TEST(ValidateSpvKHRIntegerDotProductSimple, + DISABLED_Invalid_SUDot_RequiresUnsignedSecondArg) { + FAIL(); +} + +TEST(ValidateSpvKHRIntegerDotProductSimple, + DISABLED_Invalid_UDotAccSat_RequiresUnsigned) { + FAIL(); +} + +TEST(ValidateSpvKHRIntegerDotProductSimple, + DISABLED_Invalid_SUDotAccSat_RequiresUnsignedSecondArg) { + FAIL(); +} + +TEST(ValidateSpvKHRIntegerDotProductSimple, + DISABLED_Invalid_VectorOperandsDisallowPackedFormat) { + FAIL(); +} + +TEST(ValidateSpvKHRIntegerDotProductSimple, + DISABLED_Invalid_ScalarOperandsRequirePackedFormat) { + FAIL(); +} + +// TODO(dneto): Test valid cases with other scalar integer types +// TODO(dneto): Test valid cases of length-8 vectors +// TODO(dneto): Test valid cases of length-16 vectors + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/test/val/val_extension_spv_khr_linkonce_odr.cpp b/test/val/val_extension_spv_khr_linkonce_odr.cpp new file mode 100644 index 00000000..ac15558b --- /dev/null +++ b/test/val/val_extension_spv_khr_linkonce_odr.cpp @@ -0,0 +1,100 @@ +// Copyright (c) 2020 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. + +// Tests for OpExtension validator rules. + +#include <string> +#include <vector> + +#include "gmock/gmock.h" +#include "source/enum_string_mapping.h" +#include "source/extensions.h" +#include "source/spirv_target_env.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::HasSubstr; +using ::testing::Values; +using ::testing::ValuesIn; + +using ValidateSpvKHRLinkOnceODR = spvtest::ValidateBase<bool>; + +TEST_F(ValidateSpvKHRLinkOnceODR, Valid) { + const std::string str = R"( + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpExtension "SPV_KHR_linkonce_odr" + OpMemoryModel Physical32 OpenCL + OpDecorate %var LinkageAttributes "foobar" LinkOnceODR + + %uint = OpTypeInt 32 0 + %ptr = OpTypePointer CrossWorkgroup %uint + %var = OpVariable %ptr CrossWorkgroup + +)"; + CompileSuccessfully(str.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateSpvKHRLinkOnceODR, RequiresExtension) { + const std::string str = R"( + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Physical32 OpenCL + OpDecorate %var LinkageAttributes "foobar" LinkOnceODR + + %uint = OpTypeInt 32 0 + %ptr = OpTypePointer CrossWorkgroup %uint + %var = OpVariable %ptr CrossWorkgroup +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("4th operand of Decorate: operand LinkOnceODR(2) requires one " + "of these extensions: SPV_KHR_linkonce_odr \n" + " OpDecorate %1 LinkageAttributes \"foobar\" LinkOnceODR\n")); +} + +TEST_F(ValidateSpvKHRLinkOnceODR, RequiresLinkageCapability) { + const std::string str = R"( + OpCapability Kernel + OpCapability Addresses + OpExtension "SPV_KHR_linkonce_odr" + OpMemoryModel Physical32 OpenCL + OpDecorate %var LinkageAttributes "foobar" LinkOnceODR + + %uint = OpTypeInt 32 0 + %ptr = OpTypePointer CrossWorkgroup %uint + %var = OpVariable %ptr CrossWorkgroup +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Operand 2 of Decorate requires one of these capabilities: Linkage \n" + " OpDecorate %1 LinkageAttributes \"foobar\" LinkOnceODR")); +} + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/test/val/val_extension_spv_khr_subgroup_uniform_control_flow.cpp b/test/val/val_extension_spv_khr_subgroup_uniform_control_flow.cpp new file mode 100644 index 00000000..f528cb9e --- /dev/null +++ b/test/val/val_extension_spv_khr_subgroup_uniform_control_flow.cpp @@ -0,0 +1,110 @@ +// Copyright (c) 2021 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. + +// Tests for OpExtension validator rules. + +#include <string> +#include <vector> + +#include "gmock/gmock.h" +#include "source/enum_string_mapping.h" +#include "source/extensions.h" +#include "source/spirv_target_env.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::HasSubstr; +using ::testing::Values; +using ::testing::ValuesIn; + +using ValidateSpvKHRSubgroupUniformControlFlow = spvtest::ValidateBase<bool>; + +TEST_F(ValidateSpvKHRSubgroupUniformControlFlow, Valid) { + const std::string str = R"( + OpCapability Shader + OpExtension "SPV_KHR_subgroup_uniform_control_flow" + OpMemoryModel Logical Simple + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpExecutionMode %main SubgroupUniformControlFlowKHR + + %void = OpTypeVoid + %void_fn = OpTypeFunction %void + + %main = OpFunction %void None %void_fn + %entry = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateSpvKHRSubgroupUniformControlFlow, RequiresExtension) { + const std::string str = R"( + OpCapability Shader + OpMemoryModel Logical Simple + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpExecutionMode %main SubgroupUniformControlFlowKHR + + %void = OpTypeVoid + %void_fn = OpTypeFunction %void + + %main = OpFunction %void None %void_fn + %entry = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("2nd operand of ExecutionMode: operand " + "SubgroupUniformControlFlowKHR(4421) " + "requires one of these extensions: " + "SPV_KHR_subgroup_uniform_control_flow")); +} + +TEST_F(ValidateSpvKHRSubgroupUniformControlFlow, RequiresShaderCapability) { + const std::string str = R"( + OpCapability Kernel + OpCapability Addresses + OpExtension "SPV_KHR_subgroup_uniform_control_flow" + OpMemoryModel Physical32 OpenCL + OpEntryPoint Kernel %main "main" + OpExecutionMode %main SubgroupUniformControlFlowKHR + + %void = OpTypeVoid + %void_fn = OpTypeFunction %void + + %main = OpFunction %void None %void_fn + %entry = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Operand 2 of ExecutionMode requires one of these " + "capabilities: Shader")); +} + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/test/val/val_id_test.cpp b/test/val/val_id_test.cpp index c65d1715..dd4c952c 100644 --- a/test/val/val_id_test.cpp +++ b/test/val/val_id_test.cpp @@ -835,7 +835,7 @@ class OpTypeArrayLengthTest position_(spv_position_t{0, 0, 0}), diagnostic_(spvDiagnosticCreate(&position_, "")) {} - ~OpTypeArrayLengthTest() { spvDiagnosticDestroy(diagnostic_); } + ~OpTypeArrayLengthTest() override { spvDiagnosticDestroy(diagnostic_); } // Runs spvValidate() on v, printing any errors via spvDiagnosticPrint(). spv_result_t Val(const SpirvVector& v, const std::string& expected_err = "") { @@ -1056,6 +1056,8 @@ TEST_F(ValidateIdWithMessage, OpTypeStructOpaqueTypeBad) { CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_0); EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-None-04667")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("OpTypeStruct must not contain an opaque type")); } diff --git a/test/val/val_image_test.cpp b/test/val/val_image_test.cpp index 5030e411..701e35e1 100644 --- a/test/val/val_image_test.cpp +++ b/test/val/val_image_test.cpp @@ -80,6 +80,7 @@ OpCapability ImageBuffer %private_image_u32_buffer_0002_r32ui %private_image_u32_spd_0002 %private_image_f32_buffer_0002_r32ui +%input_flat_u32 )"; ss << capabilities_and_extensions; @@ -121,6 +122,8 @@ OpDecorate %uniform_image_f32_cube_0102_rgba32f DescriptorSet 2 OpDecorate %uniform_image_f32_cube_0102_rgba32f Binding 3 OpDecorate %uniform_sampler DescriptorSet 3 OpDecorate %uniform_sampler Binding 0 +OpDecorate %input_flat_u32 Flat +OpDecorate %input_flat_u32 Location 0 )"; } @@ -294,6 +297,9 @@ OpDecorate %uniform_sampler Binding 0 %ptr_Image_f32 = OpTypePointer Image %f32 %ptr_image_f32_buffer_0002_r32ui = OpTypePointer Private %type_image_f32_buffer_0002_r32ui %private_image_f32_buffer_0002_r32ui = OpVariable %ptr_image_f32_buffer_0002_r32ui Private + +%ptr_input_flat_u32 = OpTypePointer Input %u32 +%input_flat_u32 = OpVariable %ptr_input_flat_u32 Input )"; if (env == SPV_ENV_UNIVERSAL_1_0) { @@ -1785,6 +1791,20 @@ TEST_F(ValidateImage, SampleImplicitLodVulkanOffsetWrongSize) { "OpImage*Gather operations")); } +TEST_F(ValidateImage, SampleImplicitLodVulkanOffsetWrongBeforeLegalization) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res4 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec4_0000 Offset %s32vec2_01 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "", "Fragment", "", SPV_ENV_VULKAN_1_0).c_str()); + getValidatorOptions()->before_hlsl_legalization = true; + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + TEST_F(ValidateImage, SampleImplicitLodMoreThanOneOffset) { const std::string body = R"( %img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 @@ -3016,6 +3036,40 @@ TEST_F(ValidateImage, GatherComponentNot32Bit) { HasSubstr("Expected Component to be 32-bit int scalar")); } +TEST_F(ValidateImage, GatherComponentSuccessVulkan) { + const std::string body = R"( +%img = OpLoad %type_image_f32_cube_0101 %uniform_image_f32_cube_0101 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_cube_0101 %img %sampler +%res1 = OpImageGather %f32vec4 %simg %f32vec4_0000 %u32_0 +)"; + + spv_target_env env = SPV_ENV_VULKAN_1_0; + CompileSuccessfully(GenerateShaderCode(body, "", "Fragment", "", env).c_str(), + env); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(env)); +} + +TEST_F(ValidateImage, GatherComponentNotConstantVulkan) { + const std::string body = R"( +%input_u32 = OpLoad %u32 %input_flat_u32 +%img = OpLoad %type_image_f32_cube_0101 %uniform_image_f32_cube_0101 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_cube_0101 %img %sampler +%res1 = OpImageGather %f32vec4 %simg %f32vec4_0000 %input_u32 +)"; + + spv_target_env env = SPV_ENV_VULKAN_1_0; + CompileSuccessfully(GenerateShaderCode(body, "", "Fragment", "", env).c_str(), + env); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-OpImageGather-04664")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Component Operand to be a const object for " + "Vulkan environment")); +} + TEST_F(ValidateImage, GatherDimCube) { const std::string body = R"( %img = OpLoad %type_image_f32_cube_0101 %uniform_image_f32_cube_0101 @@ -3335,6 +3389,8 @@ TEST_F(ValidateImage, ReadWrongNumComponentsResultType_Vulkan) { .c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Result-04780")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("Expected Result Type to have 4 components")); } @@ -5775,6 +5831,11 @@ static const std::string capabilities_and_extensions_image64 = R"( OpCapability Int64ImageEXT OpExtension "SPV_EXT_shader_image_int64" )"; +static const std::string capabilities_and_extensions_image64_atomic = R"( +OpCapability Int64Atomics +OpCapability Int64ImageEXT +OpExtension "SPV_EXT_shader_image_int64" +)"; static const std::string declarations_image64 = R"( %type_image_u64_buffer_0002_r64ui = OpTypeImage %u64 Buffer 0 0 0 2 R64ui %ptr_Image_u64 = OpTypePointer Image %u64 @@ -5814,11 +5875,11 @@ TEST_F(ValidateImage, ImageTexelPointer64Success) { %sum = OpAtomicIAdd %u64 %texel_ptr %u32_1 %u32_0 %u64_1 )"; - CompileSuccessfully(GenerateShaderCode(body, - capabilities_and_extensions_image64, - "Fragment", "", SPV_ENV_UNIVERSAL_1_3, - "GLSL450", declarations_image64) - .c_str()); + CompileSuccessfully( + GenerateShaderCode(body, capabilities_and_extensions_image64_atomic, + "Fragment", "", SPV_ENV_UNIVERSAL_1_3, "GLSL450", + declarations_image64) + .c_str()); ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } @@ -5828,11 +5889,11 @@ TEST_F(ValidateImage, ImageTexelPointer64ResultTypeNotPointer) { %sum = OpAtomicIAdd %u64 %texel_ptr %u32_1 %u32_0 %u64_1 )"; - CompileSuccessfully(GenerateShaderCode(body, - capabilities_and_extensions_image64, - "Fragment", "", SPV_ENV_UNIVERSAL_1_3, - "GLSL450", declarations_image64) - .c_str()); + CompileSuccessfully( + GenerateShaderCode(body, capabilities_and_extensions_image64_atomic, + "Fragment", "", SPV_ENV_UNIVERSAL_1_3, "GLSL450", + declarations_image64) + .c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Expected Result Type to be OpTypePointer")); @@ -5844,11 +5905,11 @@ TEST_F(ValidateImage, ImageTexelPointer64ResultTypeNotImageClass) { %sum = OpAtomicIAdd %u64 %texel_ptr %u32_1 %u32_0 %u64_1 )"; - CompileSuccessfully(GenerateShaderCode(body, - capabilities_and_extensions_image64, - "Fragment", "", SPV_ENV_UNIVERSAL_1_3, - "GLSL450", declarations_image64) - .c_str()); + CompileSuccessfully( + GenerateShaderCode(body, capabilities_and_extensions_image64_atomic, + "Fragment", "", SPV_ENV_UNIVERSAL_1_3, "GLSL450", + declarations_image64) + .c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Expected Result Type to be OpTypePointer whose " @@ -5861,11 +5922,11 @@ TEST_F(ValidateImage, ImageTexelPointer64SampleNotZeroForImageWithMSZero) { %sum = OpAtomicIAdd %u64 %texel_ptr %u32_1 %u32_0 %u64_1 )"; - CompileSuccessfully(GenerateShaderCode(body, - capabilities_and_extensions_image64, - "Fragment", "", SPV_ENV_UNIVERSAL_1_3, - "GLSL450", declarations_image64) - .c_str()); + CompileSuccessfully( + GenerateShaderCode(body, capabilities_and_extensions_image64_atomic, + "Fragment", "", SPV_ENV_UNIVERSAL_1_3, "GLSL450", + declarations_image64) + .c_str()); ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); EXPECT_THAT(getDiagnosticString(), HasSubstr("Expected Sample for Image with MS 0 to be a valid " diff --git a/test/val/val_limits_test.cpp b/test/val/val_limits_test.cpp index 0ef61e21..8fb80a46 100644 --- a/test/val/val_limits_test.cpp +++ b/test/val/val_limits_test.cpp @@ -212,6 +212,7 @@ TEST_F(ValidateLimits, SwitchNumBranchesGood) { %5 = OpFunction %1 None %2 %7 = OpLabel %8 = OpIAdd %3 %4 %4 + OpSelectionMerge %10 None OpSwitch %4 %10)"; // Now add the (literal, label) pairs @@ -240,6 +241,7 @@ TEST_F(ValidateLimits, SwitchNumBranchesBad) { %5 = OpFunction %1 None %2 %7 = OpLabel %8 = OpIAdd %3 %4 %4 + OpSelectionMerge %10 None OpSwitch %4 %10)"; // Now add the (literal, label) pairs @@ -271,6 +273,7 @@ TEST_F(ValidateLimits, CustomizedSwitchNumBranchesGood) { %5 = OpFunction %1 None %2 %7 = OpLabel %8 = OpIAdd %3 %4 %4 + OpSelectionMerge %10 None OpSwitch %4 %10)"; // Now add the (literal, label) pairs @@ -301,6 +304,7 @@ TEST_F(ValidateLimits, CustomizedSwitchNumBranchesBad) { %5 = OpFunction %1 None %2 %7 = OpLabel %8 = OpIAdd %3 %4 %4 + OpSelectionMerge %10 None OpSwitch %4 %10)"; // Now add the (literal, label) pairs diff --git a/test/val/val_memory_test.cpp b/test/val/val_memory_test.cpp index 958476cb..9799b404 100644 --- a/test/val/val_memory_test.cpp +++ b/test/val/val_memory_test.cpp @@ -61,14 +61,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-UniformConstant-04655")); EXPECT_THAT( getDiagnosticString(), - HasSubstr("From Vulkan spec, section 14.5.2:\n" - "Variables identified with the UniformConstant storage class " + HasSubstr("Variables identified with the UniformConstant storage class " "are used only as handles to refer to opaque resources. Such " "variables must be typed as OpTypeImage, OpTypeSampler, " - "OpTypeSampledImage, OpTypeAccelerationStructureNV, " - "OpTypeAccelerationStructureKHR, OpTypeRayQueryKHR, " + "OpTypeSampledImage, OpTypeAccelerationStructureKHR, " "or an array of one of these types.")); } @@ -115,14 +115,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-UniformConstant-04655")); EXPECT_THAT( getDiagnosticString(), - HasSubstr("From Vulkan spec, section 14.5.2:\n" - "Variables identified with the UniformConstant storage class " + HasSubstr("Variables identified with the UniformConstant storage class " "are used only as handles to refer to opaque resources. Such " "variables must be typed as OpTypeImage, OpTypeSampler, " - "OpTypeSampledImage, OpTypeAccelerationStructureNV, " - "OpTypeAccelerationStructureKHR, OpTypeRayQueryKHR, " + "OpTypeSampledImage, OpTypeAccelerationStructureKHR, " "or an array of one of these types.")); } diff --git a/test/val/val_misc_test.cpp b/test/val/val_misc_test.cpp index 499b5b28..b0e46bf9 100644 --- a/test/val/val_misc_test.cpp +++ b/test/val/val_misc_test.cpp @@ -273,6 +273,30 @@ TEST_F(ValidateMisc, UndefVoid) { EXPECT_THAT(getDiagnosticString(), HasSubstr("Cannot create undefined values with void type")); } + +TEST_F(ValidateMisc, VulkanInvalidStorageClass) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %func "shader" +%int = OpTypeInt 32 0 +%ptr = OpTypePointer CrossWorkgroup %int +%var = OpVariable %ptr CrossWorkgroup +%void = OpTypeVoid +%void_f = OpTypeFunction %void +%func = OpFunction %void None %void_f +%label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-None-04643")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Invalid storage class for target environment")); +} } // namespace } // namespace val } // namespace spvtools diff --git a/test/val/val_state_test.cpp b/test/val/val_state_test.cpp index b2d26045..65cb1c3a 100644 --- a/test/val/val_state_test.cpp +++ b/test/val/val_state_test.cpp @@ -43,7 +43,7 @@ class ValidationStateTest : public testing::Test { options_(spvValidatorOptionsCreate()), state_(context_, options_, kFakeBinary, 0, 1) {} - ~ValidationStateTest() { + ~ValidationStateTest() override { spvContextDestroy(context_); spvValidatorOptionsDestroy(options_); } diff --git a/test/val/val_storage_test.cpp b/test/val/val_storage_test.cpp index e6f98bff..35f6a8d5 100644 --- a/test/val/val_storage_test.cpp +++ b/test/val/val_storage_test.cpp @@ -30,6 +30,7 @@ using ::testing::Values; using ValidateStorage = spvtest::ValidateBase<std::string>; using ValidateStorageClass = spvtest::ValidateBase<std::tuple<std::string, bool, bool, std::string>>; +using ValidateStorageExecutionModel = spvtest::ValidateBase<std::string>; TEST_F(ValidateStorage, FunctionStorageInsideFunction) { char str[] = R"( @@ -250,6 +251,46 @@ TEST_F(ValidateStorage, RelaxedLogicalPointerFunctionParamBad) { HasSubstr("OpFunctionCall Argument <id> '")); } +TEST_P(ValidateStorageExecutionModel, VulkanOutsideStoreFailure) { + std::stringstream ss; + ss << R"( + OpCapability Shader + OpCapability RayTracingKHR + OpExtension "SPV_KHR_ray_tracing" + OpMemoryModel Logical GLSL450 + OpEntryPoint )" + << GetParam() << R"( %func "func" %output + OpDecorate %output Location 0 +%intt = OpTypeInt 32 0 +%int0 = OpConstant %intt 0 +%voidt = OpTypeVoid +%vfunct = OpTypeFunction %voidt +%outputptrt = OpTypePointer Output %intt +%output = OpVariable %outputptrt Output +%func = OpFunction %voidt None %vfunct +%funcl = OpLabel + OpStore %output %int0 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(ss.str(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-None-04644")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("in Vulkan evironment, Output Storage Class must not be used " + "in GLCompute, RayGenerationKHR, IntersectionKHR, AnyHitKHR, " + "ClosestHitKHR, MissKHR, or CallableKHR execution models")); +} + +INSTANTIATE_TEST_SUITE_P(MatrixExecutionModel, ValidateStorageExecutionModel, + ::testing::Values("RayGenerationKHR", + "IntersectionKHR", "AnyHitKHR", + "ClosestHitKHR", "MissKHR", + "CallableKHR")); + } // namespace } // namespace val } // namespace spvtools diff --git a/test/val/val_validation_state_test.cpp b/test/val/val_validation_state_test.cpp index 7a38d3a1..3dd9e64a 100644 --- a/test/val/val_validation_state_test.cpp +++ b/test/val/val_validation_state_test.cpp @@ -257,6 +257,8 @@ TEST_F(ValidationStateTest, CheckVulkanDirectlyRecursiveBodyBad) { EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-None-04634")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("Entry points may not have a call graph with cycles.\n " " %1 = OpFunction %void Pure|Const %3\n")); } @@ -274,6 +276,8 @@ TEST_F(ValidationStateTest, CheckVulkanIndirectlyRecursiveBodyBad) { EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-None-04634")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("Entry points may not have a call graph with cycles.\n " " %1 = OpFunction %void Pure|Const %3\n")); } diff --git a/tools/as/as.cpp b/tools/as/as.cpp index f6e96294..c8a44456 100644 --- a/tools/as/as.cpp +++ b/tools/as/as.cpp @@ -129,7 +129,7 @@ int main(int argc, char** argv) { } std::vector<char> contents; - if (!ReadFile<char>(inFile, "r", &contents)) return 1; + if (!ReadTextFile<char>(inFile, &contents)) return 1; spv_binary binary; spv_diagnostic diagnostic = nullptr; diff --git a/tools/cfg/cfg.cpp b/tools/cfg/cfg.cpp index ce7f1c22..a93488d5 100644 --- a/tools/cfg/cfg.cpp +++ b/tools/cfg/cfg.cpp @@ -104,7 +104,7 @@ int main(int argc, char** argv) { // Read the input binary. std::vector<uint32_t> contents; - if (!ReadFile<uint32_t>(inFile, "rb", &contents)) return 1; + if (!ReadBinaryFile<uint32_t>(inFile, &contents)) return 1; spv_context context = spvContextCreate(kDefaultEnvironment); spv_diagnostic diagnostic = nullptr; diff --git a/tools/dis/dis.cpp b/tools/dis/dis.cpp index bdeeef11..64380db0 100644 --- a/tools/dis/dis.cpp +++ b/tools/dis/dis.cpp @@ -180,7 +180,7 @@ int main(int argc, char** argv) { // Read the input binary. std::vector<uint32_t> contents; - if (!ReadFile<uint32_t>(inFile, "rb", &contents)) return 1; + if (!ReadBinaryFile<uint32_t>(inFile, &contents)) return 1; // If printing to standard output, then spvBinaryToText should // do the printing. In particular, colour printing on Windows is diff --git a/tools/fuzz/fuzz.cpp b/tools/fuzz/fuzz.cpp index 5ee0fb18..422bea53 100644 --- a/tools/fuzz/fuzz.cpp +++ b/tools/fuzz/fuzz.cpp @@ -39,6 +39,8 @@ namespace { +enum class FuzzingTarget { kSpirv, kWgsl }; + // Check that the std::system function can actually be used. bool CheckExecuteCommand() { int res = std::system(nullptr); @@ -141,6 +143,12 @@ Options (in lexicographical order): that was used previously. - simple: each time a fuzzer pass is requested, one is provided at random from the set of enabled passes. + --fuzzing-target= + This option will adjust probabilities of applying certain + transformations s.t. the module always remains valid according + to the semantics of some fuzzing target. Available targets: + - spir-v - module is valid according to the SPIR-V spec. + - wgsl - module is valid according to the WGSL spec. --replay File from which to read a sequence of transformations to replay (instead of fuzzing) @@ -204,15 +212,15 @@ FuzzStatus ParseFlags( std::vector<std::string>* interestingness_test, std::string* shrink_transformations_file, std::string* shrink_temp_file_prefix, - spvtools::fuzz::Fuzzer::RepeatedPassStrategy* repeated_pass_strategy, - spvtools::FuzzerOptions* fuzzer_options, + spvtools::fuzz::RepeatedPassStrategy* repeated_pass_strategy, + FuzzingTarget* fuzzing_target, spvtools::FuzzerOptions* fuzzer_options, spvtools::ValidatorOptions* validator_options) { uint32_t positional_arg_index = 0; bool only_positional_arguments_remain = false; bool force_render_red = false; *repeated_pass_strategy = - spvtools::fuzz::Fuzzer::RepeatedPassStrategy::kLoopedWithRecommendations; + spvtools::fuzz::RepeatedPassStrategy::kLoopedWithRecommendations; for (int argi = 1; argi < argc; ++argi) { const char* cur_arg = argv[argi]; @@ -250,14 +258,14 @@ FuzzStatus ParseFlags( sizeof("--repeated-pass-strategy=") - 1)) { std::string strategy = spvtools::utils::SplitFlagArgs(cur_arg).second; if (strategy == "looped") { - *repeated_pass_strategy = spvtools::fuzz::Fuzzer:: - RepeatedPassStrategy::kLoopedWithRecommendations; + *repeated_pass_strategy = + spvtools::fuzz::RepeatedPassStrategy::kLoopedWithRecommendations; } else if (strategy == "random") { - *repeated_pass_strategy = spvtools::fuzz::Fuzzer:: - RepeatedPassStrategy::kRandomWithRecommendations; + *repeated_pass_strategy = + spvtools::fuzz::RepeatedPassStrategy::kRandomWithRecommendations; } else if (strategy == "simple") { *repeated_pass_strategy = - spvtools::fuzz::Fuzzer::RepeatedPassStrategy::kSimple; + spvtools::fuzz::RepeatedPassStrategy::kSimple; } else { std::stringstream ss; ss << "Unknown repeated pass strategy '" << strategy << "'" @@ -266,6 +274,20 @@ FuzzStatus ParseFlags( spvtools::Error(FuzzDiagnostic, nullptr, {}, ss.str().c_str()); return {FuzzActions::STOP, 1}; } + } else if (0 == strncmp(cur_arg, "--fuzzing-target=", + sizeof("--fuzzing-target=") - 1)) { + std::string target = spvtools::utils::SplitFlagArgs(cur_arg).second; + if (target == "spir-v") { + *fuzzing_target = FuzzingTarget::kSpirv; + } else if (target == "wgsl") { + *fuzzing_target = FuzzingTarget::kWgsl; + } else { + std::stringstream ss; + ss << "Unknown fuzzing target '" << target << "'" << std::endl; + ss << "Valid options are 'spir-v' and 'wgsl'."; + spvtools::Error(FuzzDiagnostic, nullptr, {}, ss.str().c_str()); + return {FuzzActions::STOP, 1}; + } } else if (0 == strncmp(cur_arg, "--replay-range=", sizeof("--replay-range=") - 1)) { const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg); @@ -549,8 +571,8 @@ bool Fuzz(const spv_target_env& target_env, const std::vector<uint32_t>& binary_in, const spvtools::fuzz::protobufs::FactSequence& initial_facts, const std::string& donors, - spvtools::fuzz::Fuzzer::RepeatedPassStrategy repeated_pass_strategy, - std::vector<uint32_t>* binary_out, + spvtools::fuzz::RepeatedPassStrategy repeated_pass_strategy, + FuzzingTarget fuzzing_target, std::vector<uint32_t>* binary_out, spvtools::fuzz::protobufs::TransformationSequence* transformations_applied) { auto message_consumer = spvtools::utils::CLIMessageConsumer; @@ -568,8 +590,8 @@ bool Fuzz(const spv_target_env& target_env, [donor_filename, message_consumer, target_env]() -> std::unique_ptr<spvtools::opt::IRContext> { std::vector<uint32_t> donor_binary; - if (!ReadFile<uint32_t>(donor_filename.c_str(), "rb", - &donor_binary)) { + if (!ReadBinaryFile<uint32_t>(donor_filename.c_str(), + &donor_binary)) { return nullptr; } return spvtools::BuildModule(target_env, message_consumer, @@ -578,24 +600,46 @@ bool Fuzz(const spv_target_env& target_env, }); } - auto fuzz_result = - spvtools::fuzz::Fuzzer( - target_env, message_consumer, binary_in, initial_facts, - donor_suppliers, - spvtools::MakeUnique<spvtools::fuzz::PseudoRandomGenerator>( - fuzzer_options->has_random_seed - ? fuzzer_options->random_seed - : static_cast<uint32_t>(std::random_device()())), - fuzzer_options->all_passes_enabled, repeated_pass_strategy, - fuzzer_options->fuzzer_pass_validation_enabled, validator_options) - .Run(); - *binary_out = std::move(fuzz_result.transformed_binary); - *transformations_applied = std::move(fuzz_result.applied_transformations); - if (fuzz_result.status != - spvtools::fuzz::Fuzzer::FuzzerResultStatus::kComplete) { + std::unique_ptr<spvtools::opt::IRContext> ir_context; + if (!spvtools::fuzz::fuzzerutil::BuildIRContext(target_env, message_consumer, + binary_in, validator_options, + &ir_context)) { + spvtools::Error(FuzzDiagnostic, nullptr, {}, "Initial binary is invalid"); + return false; + } + + assert((fuzzing_target == FuzzingTarget::kWgsl || + fuzzing_target == FuzzingTarget::kSpirv) && + "Not all fuzzing targets are handled"); + auto fuzzer_context = spvtools::MakeUnique<spvtools::fuzz::FuzzerContext>( + spvtools::MakeUnique<spvtools::fuzz::PseudoRandomGenerator>( + fuzzer_options->has_random_seed + ? fuzzer_options->random_seed + : static_cast<uint32_t>(std::random_device()())), + spvtools::fuzz::FuzzerContext::GetMinFreshId(ir_context.get()), + fuzzing_target == FuzzingTarget::kWgsl); + + auto transformation_context = + spvtools::MakeUnique<spvtools::fuzz::TransformationContext>( + spvtools::MakeUnique<spvtools::fuzz::FactManager>(ir_context.get()), + validator_options); + transformation_context->GetFactManager()->AddInitialFacts(message_consumer, + initial_facts); + + spvtools::fuzz::Fuzzer fuzzer( + std::move(ir_context), std::move(transformation_context), + std::move(fuzzer_context), message_consumer, donor_suppliers, + fuzzer_options->all_passes_enabled, repeated_pass_strategy, + fuzzer_options->fuzzer_pass_validation_enabled, validator_options); + auto fuzz_result = fuzzer.Run(0); + if (fuzz_result.status == + spvtools::fuzz::Fuzzer::Status::kFuzzerPassLedToInvalidModule) { spvtools::Error(FuzzDiagnostic, nullptr, {}, "Error running fuzzer"); return false; } + + fuzzer.GetIRContext()->module()->ToBinary(binary_out, true); + *transformations_applied = fuzzer.GetTransformationSequence(); return true; } @@ -656,7 +700,8 @@ int main(int argc, const char** argv) { std::vector<std::string> interestingness_test; std::string shrink_transformations_file; std::string shrink_temp_file_prefix = "temp_"; - spvtools::fuzz::Fuzzer::RepeatedPassStrategy repeated_pass_strategy; + spvtools::fuzz::RepeatedPassStrategy repeated_pass_strategy; + auto fuzzing_target = FuzzingTarget::kSpirv; spvtools::FuzzerOptions fuzzer_options; spvtools::ValidatorOptions validator_options; @@ -665,14 +710,15 @@ int main(int argc, const char** argv) { ParseFlags(argc, argv, &in_binary_file, &out_binary_file, &donors_file, &replay_transformations_file, &interestingness_test, &shrink_transformations_file, &shrink_temp_file_prefix, - &repeated_pass_strategy, &fuzzer_options, &validator_options); + &repeated_pass_strategy, &fuzzing_target, &fuzzer_options, + &validator_options); if (status.action == FuzzActions::STOP) { return status.code; } std::vector<uint32_t> binary_in; - if (!ReadFile<uint32_t>(in_binary_file.c_str(), "rb", &binary_in)) { + if (!ReadBinaryFile<uint32_t>(in_binary_file.c_str(), &binary_in)) { return 1; } @@ -703,16 +749,16 @@ int main(int argc, const char** argv) { switch (status.action) { case FuzzActions::FORCE_RENDER_RED: - if (!spvtools::fuzz::ForceRenderRed(target_env, validator_options, - binary_in, initial_facts, - &binary_out)) { + if (!spvtools::fuzz::ForceRenderRed( + target_env, validator_options, binary_in, initial_facts, + spvtools::utils::CLIMessageConsumer, &binary_out)) { return 1; } break; case FuzzActions::FUZZ: if (!Fuzz(target_env, fuzzer_options, validator_options, binary_in, - initial_facts, donors_file, repeated_pass_strategy, &binary_out, - &transformations_applied)) { + initial_facts, donors_file, repeated_pass_strategy, + fuzzing_target, &binary_out, &transformations_applied)) { return 1; } break; @@ -20,45 +20,101 @@ #include <cstring> #include <vector> -// Appends the content from the file named as |filename| to |data|, assuming -// each element in the file is of type |T|. The file is opened with the given -// |mode|. If |filename| is nullptr or "-", reads from the standard input, but -// reopened with the given mode. If any error occurs, writes error messages to -// standard error and returns false. +#if defined(SPIRV_WINDOWS) +#include <fcntl.h> +#include <io.h> + +#define SET_STDIN_TO_BINARY_MODE() _setmode(_fileno(stdin), O_BINARY); +#define SET_STDIN_TO_TEXT_MODE() _setmode(_fileno(stdin), O_TEXT); +#else +#define SET_STDIN_TO_BINARY_MODE() +#define SET_STDIN_TO_TEXT_MODE() +#endif + +// Appends the contents of the |file| to |data|, assuming each element in the +// file is of type |T|. template <typename T> -bool ReadFile(const char* filename, const char* mode, std::vector<T>* data) { +void ReadFile(FILE* file, std::vector<T>* data) { + if (file == nullptr) return; + const int buf_size = 1024; - const bool use_file = filename && strcmp("-", filename); - if (FILE* fp = - (use_file ? fopen(filename, mode) : freopen(nullptr, mode, stdin))) { - T buf[buf_size]; - while (size_t len = fread(buf, sizeof(T), buf_size, fp)) { - data->insert(data->end(), buf, buf + len); - } - if (ftell(fp) == -1L) { - if (ferror(fp)) { - fprintf(stderr, "error: error reading file '%s'\n", filename); - if (use_file) fclose(fp); - return false; - } - } else { - if (sizeof(T) != 1 && (ftell(fp) % sizeof(T))) { - fprintf( - stderr, - "error: file size should be a multiple of %zd; file '%s' corrupt\n", - sizeof(T), filename); - if (use_file) fclose(fp); - return false; - } - } - if (use_file) fclose(fp); - } else { + T buf[buf_size]; + while (size_t len = fread(buf, sizeof(T), buf_size, file)) { + data->insert(data->end(), buf, buf + len); + } +} + +// Returns true if |file| has encountered an error opening the file or reading +// the file as a series of element of type |T|. If there was an error, writes an +// error message to standard error. +template <class T> +bool WasFileCorrectlyRead(FILE* file, const char* filename) { + if (file == nullptr) { fprintf(stderr, "error: file does not exist '%s'\n", filename); return false; } + + if (ftell(file) == -1L) { + if (ferror(file)) { + fprintf(stderr, "error: error reading file '%s'\n", filename); + return false; + } + } else { + if (sizeof(T) != 1 && (ftell(file) % sizeof(T))) { + fprintf( + stderr, + "error: file size should be a multiple of %zd; file '%s' corrupt\n", + sizeof(T), filename); + return false; + } + } return true; } +// Appends the contents of the file named |filename| to |data|, assuming +// each element in the file is of type |T|. The file is opened as a binary file +// If |filename| is nullptr or "-", reads from the standard input, but +// reopened as a binary file. If any error occurs, writes error messages to +// standard error and returns false. +template <typename T> +bool ReadBinaryFile(const char* filename, std::vector<T>* data) { + const bool use_file = filename && strcmp("-", filename); + FILE* fp = nullptr; + if (use_file) { + fp = fopen(filename, "rb"); + } else { + SET_STDIN_TO_BINARY_MODE(); + fp = stdin; + } + + ReadFile(fp, data); + bool succeeded = WasFileCorrectlyRead<T>(fp, filename); + if (use_file) fclose(fp); + return succeeded; +} + +// Appends the contents of the file named |filename| to |data|, assuming +// each element in the file is of type |T|. The file is opened as a text file +// If |filename| is nullptr or "-", reads from the standard input, but +// reopened as a text file. If any error occurs, writes error messages to +// standard error and returns false. +template <typename T> +bool ReadTextFile(const char* filename, std::vector<T>* data) { + const bool use_file = filename && strcmp("-", filename); + FILE* fp = nullptr; + if (use_file) { + fp = fopen(filename, "r"); + } else { + SET_STDIN_TO_TEXT_MODE(); + fp = stdin; + } + + ReadFile(fp, data); + bool succeeded = WasFileCorrectlyRead<T>(fp, filename); + if (use_file) fclose(fp); + return succeeded; +} + // Writes the given |data| into the file named as |filename| using the given // |mode|, assuming |data| is an array of |count| elements of type |T|. If // |filename| is nullptr or "-", writes to standard output. If any error occurs, diff --git a/tools/link/linker.cpp b/tools/link/linker.cpp index 1956f595..359e8030 100644 --- a/tools/link/linker.cpp +++ b/tools/link/linker.cpp @@ -130,7 +130,7 @@ int main(int argc, char** argv) { std::vector<std::vector<uint32_t>> contents(inFiles.size()); for (size_t i = 0u; i < inFiles.size(); ++i) { - if (!ReadFile<uint32_t>(inFiles[i], "rb", &contents[i])) return 1; + if (!ReadBinaryFile<uint32_t>(inFiles[i], &contents[i])) return 1; } const spvtools::MessageConsumer consumer = [](spv_message_level_t level, diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp index 6999b39a..b9339abe 100644 --- a/tools/opt/opt.cpp +++ b/tools/opt/opt.cpp @@ -807,7 +807,7 @@ int main(int argc, const char** argv) { } std::vector<uint32_t> binary; - if (!ReadFile<uint32_t>(in_file, "rb", &binary)) { + if (!ReadBinaryFile<uint32_t>(in_file, &binary)) { return 1; } diff --git a/tools/reduce/reduce.cpp b/tools/reduce/reduce.cpp index 49a5efef..4447b356 100644 --- a/tools/reduce/reduce.cpp +++ b/tools/reduce/reduce.cpp @@ -318,7 +318,7 @@ int main(int argc, const char** argv) { reducer.SetMessageConsumer(spvtools::utils::CLIMessageConsumer); std::vector<uint32_t> binary_in; - if (!ReadFile<uint32_t>(in_binary_file.c_str(), "rb", &binary_in)) { + if (!ReadBinaryFile<uint32_t>(in_binary_file.c_str(), &binary_in)) { return 1; } diff --git a/tools/sva/yarn.lock b/tools/sva/yarn.lock index c46b9017..4924690a 100644 --- a/tools/sva/yarn.lock +++ b/tools/sva/yarn.lock @@ -938,9 +938,9 @@ locate-path@^3.0.0: path-exists "^3.0.0" lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14: - version "4.17.19" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" - integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== log-symbols@2.2.0: version "2.2.0" diff --git a/tools/val/val.cpp b/tools/val/val.cpp index 5450023a..21a7d8f4 100644 --- a/tools/val/val.cpp +++ b/tools/val/val.cpp @@ -186,7 +186,7 @@ int main(int argc, char** argv) { } std::vector<uint32_t> contents; - if (!ReadFile<uint32_t>(inFile, "rb", &contents)) return 1; + if (!ReadBinaryFile<uint32_t>(inFile, &contents)) return 1; spvtools::SpirvTools tools(target_env); tools.SetMessageConsumer(spvtools::utils::CLIMessageConsumer); diff --git a/utils/check_copyright.py b/utils/check_copyright.py index b15bc206..c5251230 100755 --- a/utils/check_copyright.py +++ b/utils/check_copyright.py @@ -36,8 +36,11 @@ AUTHORS = ['The Khronos Group Inc.', 'André Perez Maselco', 'Vasyl Teliman', 'Advanced Micro Devices, Inc.', - 'Stefano Milizia'] -CURRENT_YEAR='2020' + 'Stefano Milizia', + 'Alastair F. Donaldson', + 'Mostafa Ashraf', + 'Shiyu Liu'] +CURRENT_YEAR='2021' YEARS = '(2014-2016|2015-2016|2015-2020|2016|2016-2017|2017|2017-2019|2018|2019|2020|2021)' COPYRIGHT_RE = re.compile( @@ -128,7 +131,7 @@ def insert_copyright(author, glob, comment_prefix): update_file = False for line in fileinput.input(file, inplace=1): emit = True - if state is 0: + if state == 0: if COPYRIGHT_RE.search(line): state = 1 elif skip(line): @@ -139,7 +142,7 @@ def insert_copyright(author, glob, comment_prefix): sys.stdout.write(licensed) # Assume there isn't a previous license notice. state = 1 - elif state is 1: + elif state == 1: if MIT_BEGIN_RE.search(line): state = 2 emit = False @@ -147,7 +150,7 @@ def insert_copyright(author, glob, comment_prefix): # Assume an Apache license is preceded by a copyright # notice. So just emit it like the rest of the file. state = 9 - elif state is 2: + elif state == 2: # Replace the MIT license with Apache 2 emit = False if MIT_END_RE.search(line): diff --git a/utils/check_symbol_exports.py b/utils/check_symbol_exports.py index bcd77da6..7795d72b 100755 --- a/utils/check_symbol_exports.py +++ b/utils/check_symbol_exports.py @@ -12,7 +12,13 @@ # 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. -"""Checks names of global exports from a library.""" +"""Ensures that all externally visible functions in the library have an appropriate name + +Appropriate function names are: + - names starting with spv, + - anything in a namespace, + - functions added by the protobuf compiler, + - and weak definitions of new and delete.""" import os.path import re @@ -46,14 +52,16 @@ def check_library(library): exports are namespaced or begin with spv (in either C or C++ styles) then return 0. Otherwise emit a message and return 1.""" - # The pattern for a global symbol record - symbol_pattern = re.compile(r'^[0-aA-Fa-f]+ g *F \.text.*[0-9A-Fa-f]+ +(.*)') + # The pattern for an externally visible symbol record + symbol_pattern = re.compile(r'^[0-aA-Fa-f]+ +([wg]) *F \.text.*[0-9A-Fa-f]+ +(.*)') # Ok patterns are as follows, assuming Itanium name mangling: # spv[A-Z] : extern "C" symbol starting with spv # _ZN : something in a namespace + # _ZSt : something in the standard namespace + # _ZZN : something in a local scope and namespace # _Z[0-9]+spv[A-Z_] : C++ symbol starting with spv[A-Z_] - symbol_ok_pattern = re.compile(r'^(spv[A-Z]|_ZN|_Z[0-9]+spv[A-Z_])') + symbol_ok_pattern = re.compile(r'^(spv[A-Z]|_ZN|_ZSt|_ZZN|_Z[0-9]+spv[A-Z_])') # In addition, the following pattern allowlists global functions that are added # by the protobuf compiler: @@ -61,18 +69,31 @@ def check_library(library): # - InitDefaults_spvtoolsfuzz_2eproto() symbol_allowlist_pattern = re.compile(r'_Z[0-9]+(InitDefaults|AddDescriptors)_spvtoolsfuzz_2eprotov') + symbol_is_new_or_delete = re.compile(r'^(_Zna|_Znw|_Zdl|_Zda)') + # Compilaion for Arm has various thunks for constructors, destructors, vtables. + # They are weak. + symbol_is_thunk = re.compile(r'^_ZT') + + # This occurs in NDK builds. + symbol_is_hidden = re.compile(r'^\.hidden ') + seen = set() result = 0 for line in command_output(['objdump', '-t', library], '.').split('\n'): match = symbol_pattern.search(line) if match: - symbol = match.group(1) + linkage = match.group(1) + symbol = match.group(2) if symbol not in seen: seen.add(symbol) #print("look at '{}'".format(symbol)) - if not (symbol_allowlist_pattern.match(symbol) or symbol_ok_pattern.match(symbol)): - print('{}: error: Unescaped exported symbol: {}'.format(PROG, symbol)) - result = 1 + if not (symbol_is_new_or_delete.match(symbol) and linkage == 'w'): + if not (symbol_is_thunk.match(symbol) and linkage == 'w'): + if not (symbol_allowlist_pattern.match(symbol) or + symbol_ok_pattern.match(symbol) or + symbol_is_hidden.match(symbol)): + print('{}: error: Unescaped exported symbol: {}'.format(PROG, symbol)) + result = 1 return result diff --git a/utils/generate_grammar_tables.py b/utils/generate_grammar_tables.py index 2a677336..9ccf410b 100755 --- a/utils/generate_grammar_tables.py +++ b/utils/generate_grammar_tables.py @@ -523,17 +523,17 @@ def generate_operand_kind_table(enums): enums = [generate_enum_operand_kind(e, exts) for e in enums] exts_arrays = generate_extension_arrays(exts) - # We have three operand kinds that requires their optional counterpart to + # We have a few operand kinds that require their optional counterpart to # exist in the operand info table. - three_optional_enums = ['ImageOperands', 'AccessQualifier', 'MemoryAccess'] - three_optional_enums = [e for e in enums if e[0] in three_optional_enums] - enums.extend(three_optional_enums) + optional_enums = ['ImageOperands', 'AccessQualifier', 'MemoryAccess', 'PackedVectorFormat'] + optional_enums = [e for e in enums if e[0] in optional_enums] + enums.extend(optional_enums) enum_kinds, enum_names, enum_entries = zip(*enums) - # Mark the last three as optional ones. - enum_quantifiers = [''] * (len(enums) - 3) + ['?'] * 3 + # Mark the last few as optional ones. + enum_quantifiers = [''] * (len(enums) - len(optional_enums)) + ['?'] * len(optional_enums) # And we don't want redefinition of them. - enum_entries = enum_entries[:-3] + enum_entries = enum_entries[:-len(optional_enums)] enum_kinds = [convert_operand_kind(e) for e in zip(enum_kinds, enum_quantifiers)] table_entries = zip(enum_kinds, enum_names, enum_names) @@ -601,7 +601,7 @@ def generate_extension_to_string_mapping(extensions): ' return "{extension}";\n' function += ''.join([template.format(extension=extension) for extension in extensions]) - function += ' };\n\n return "";\n}' + function += ' }\n\n return "";\n}' return function @@ -647,7 +647,7 @@ def generate_capability_to_string_mapping(operand_kinds): function += ' case SpvCapabilityMax:\n' \ ' assert(0 && "Attempting to convert SpvCapabilityMax to string");\n' \ ' return "";\n' - function += ' };\n\n return "";\n}' + function += ' }\n\n return "";\n}' return function diff --git a/utils/vscode/README.md b/utils/vscode/README.md index bc022112..d7aa2b41 100644 --- a/utils/vscode/README.md +++ b/utils/vscode/README.md @@ -1,12 +1,20 @@ # Visual Studio Code extension for SPIR-V disassembly files -This directory holds a Visual Studio Code extension adding syntax highlighting for SPIR-V assembly files (`.spvasm`) +This directory holds a Visual Studio Code language server extension for SPIR-V assembly files (`.spvasm`) + +The extension supports: +* Syntax highlighting +* Jump to definition +* Find all references +* Symbol renaming +* Operand hover information +* Formatting ## Dependencies In order to build and install the Visual Studio Code language server extension, you will need to install and have on your `PATH` the following dependencies: * [`npm`](https://www.npmjs.com/) -* [`golang`](https://golang.org/) +* [`golang 1.16+`](https://golang.org/) ## Installing (macOS / Linux) diff --git a/utils/vscode/go.mod b/utils/vscode/go.mod new file mode 100644 index 00000000..ea4901a4 --- /dev/null +++ b/utils/vscode/go.mod @@ -0,0 +1,8 @@ +module github.com/KhronosGroup/SPIRV-Tools/utils/vscode + +go 1.16 + +require ( + github.com/pkg/errors v0.9.1 + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 +) diff --git a/utils/vscode/go.sum b/utils/vscode/go.sum new file mode 100644 index 00000000..328c8578 --- /dev/null +++ b/utils/vscode/go.sum @@ -0,0 +1,4 @@ +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/utils/vscode/install.bat b/utils/vscode/install.bat index 21a52ecd..aa06fa91 100644 --- a/utils/vscode/install.bat +++ b/utils/vscode/install.bat @@ -23,7 +23,7 @@ copy %ROOT_PATH%\extension.js %EXT_PATH% copy %ROOT_PATH%\package.json %EXT_PATH% copy %ROOT_PATH%\spirv.json %EXT_PATH% -go build -o %EXT_PATH%\langsvr %ROOT_PATH%\src\langsvr.go +go build -o %EXT_PATH%\langsvr.exe %ROOT_PATH%\src\langsvr.go @pushd %EXT_PATH% call npm install diff --git a/utils/vscode/install.sh b/utils/vscode/install.sh index 01fc9140..de54c623 100755 --- a/utils/vscode/install.sh +++ b/utils/vscode/install.sh @@ -18,15 +18,17 @@ set -e # Fail on any error. EXT_PATH=~/.vscode/extensions/google.spirvls-0.0.1 ROOT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" -go run ${ROOT_PATH}/src/tools/gen-grammar.go --cache ${ROOT_PATH}/cache --template ${ROOT_PATH}/spirv.json.tmpl --out ${ROOT_PATH}/spirv.json -go run ${ROOT_PATH}/src/tools/gen-grammar.go --cache ${ROOT_PATH}/cache --template ${ROOT_PATH}/src/schema/schema.go.tmpl --out ${ROOT_PATH}/src/schema/schema.go +pushd ${ROOT_PATH} + go run ./src/tools/gen-grammar.go --cache ./cache --template ./spirv.json.tmpl --out ./spirv.json + go run ./src/tools/gen-grammar.go --cache ./cache --template ./src/schema/schema.go.tmpl --out ./src/schema/schema.go -mkdir -p ${EXT_PATH} -cp ${ROOT_PATH}/extension.js ${EXT_PATH} -cp ${ROOT_PATH}/package.json ${EXT_PATH} -cp ${ROOT_PATH}/spirv.json ${EXT_PATH} + mkdir -p ${EXT_PATH} + cp ./extension.js ${EXT_PATH} + cp ./package.json ${EXT_PATH} + cp ./spirv.json ${EXT_PATH} -go build -o ${EXT_PATH}/langsvr ${ROOT_PATH}/src/langsvr.go + go build -o ${EXT_PATH}/langsvr ./src/langsvr.go +popd cd ${EXT_PATH} npm install diff --git a/utils/vscode/spirv.json b/utils/vscode/spirv.json index 7999b523..2e88296e 100644 --- a/utils/vscode/spirv.json +++ b/utils/vscode/spirv.json @@ -1,7 +1,7 @@ { "scopeName": "source.spirv", "name": "SPIR-V", - "comment": "Generated by gen-grammar.go --template=../../spirv.json.tmpl --out=../../spirv.json. Do not modify this file directly.", + "comment": "Generated by gen-grammar.go --template=./spirv.json.tmpl --out=./spirv.json. Do not modify this file directly.", "patterns": [ { "include": "#BitEnum_ImageOperands" }, { "include": "#BitEnum_FPFastMathMode" }, @@ -11,6 +11,7 @@ { "include": "#BitEnum_MemorySemantics" }, { "include": "#BitEnum_MemoryAccess" }, { "include": "#BitEnum_KernelProfilingInfo" }, + { "include": "#BitEnum_RayFlags" }, { "include": "#ValueEnum_SourceLanguage" }, { "include": "#ValueEnum_ExecutionModel" }, { "include": "#ValueEnum_AddressingModel" }, @@ -33,6 +34,9 @@ { "include": "#ValueEnum_GroupOperation" }, { "include": "#ValueEnum_KernelEnqueueFlags" }, { "include": "#ValueEnum_Capability" }, + { "include": "#ValueEnum_RayQueryIntersection" }, + { "include": "#ValueEnum_RayQueryCommittedIntersectionType" }, + { "include": "#ValueEnum_RayQueryCandidateIntersectionType" }, { "include": "#BitEnum_DebugInfoFlags" }, { "include": "#ValueEnum_DebugBaseTypeAttributeEncoding" }, { "include": "#ValueEnum_DebugCompositeType" }, @@ -80,12 +84,16 @@ "match": "\\b(None|CmdExecTime)\\b", "name": "keyword.spirv" }, + "BitEnum_RayFlags": { + "match": "\\b(NoneKHR|OpaqueKHR|NoOpaqueKHR|TerminateOnFirstHitKHR|SkipClosestHitShaderKHR|CullBackFacingTrianglesKHR|CullFrontFacingTrianglesKHR|CullOpaqueKHR|CullNoOpaqueKHR|SkipTrianglesKHR|SkipAABBsKHR)\\b", + "name": "keyword.spirv" + }, "ValueEnum_SourceLanguage": { "match": "\\b(Unknown|ESSL|GLSL|OpenCL_C|OpenCL_CPP|HLSL)\\b", "name": "keyword.spirv" }, "ValueEnum_ExecutionModel": { - "match": "\\b(Vertex|TessellationControl|TessellationEvaluation|Geometry|Fragment|GLCompute|Kernel|TaskNV|MeshNV|RayGenerationNV|IntersectionNV|AnyHitNV|ClosestHitNV|MissNV|CallableNV)\\b", + "match": "\\b(Vertex|TessellationControl|TessellationEvaluation|Geometry|Fragment|GLCompute|Kernel|TaskNV|MeshNV|RayGenerationNV|RayGenerationKHR|IntersectionNV|IntersectionKHR|AnyHitNV|AnyHitKHR|ClosestHitNV|ClosestHitKHR|MissNV|MissKHR|CallableNV|CallableKHR)\\b", "name": "keyword.spirv" }, "ValueEnum_AddressingModel": { @@ -101,7 +109,7 @@ "name": "keyword.spirv" }, "ValueEnum_StorageClass": { - "match": "\\b(UniformConstant|Input|Uniform|Output|Workgroup|CrossWorkgroup|Private|Function|Generic|PushConstant|AtomicCounter|Image|StorageBuffer|CallableDataNV|IncomingCallableDataNV|RayPayloadNV|HitAttributeNV|IncomingRayPayloadNV|ShaderRecordBufferNV|PhysicalStorageBuffer|PhysicalStorageBufferEXT)\\b", + "match": "\\b(UniformConstant|Input|Uniform|Output|Workgroup|CrossWorkgroup|Private|Function|Generic|PushConstant|AtomicCounter|Image|StorageBuffer|CallableDataNV|CallableDataKHR|IncomingCallableDataNV|IncomingCallableDataKHR|RayPayloadNV|RayPayloadKHR|HitAttributeNV|HitAttributeKHR|IncomingRayPayloadNV|IncomingRayPayloadKHR|ShaderRecordBufferNV|ShaderRecordBufferKHR|PhysicalStorageBuffer|PhysicalStorageBufferEXT)\\b", "name": "keyword.spirv" }, "ValueEnum_Dim": { @@ -149,11 +157,11 @@ "name": "keyword.spirv" }, "ValueEnum_BuiltIn": { - "match": "\\b(Position|PointSize|ClipDistance|CullDistance|VertexId|InstanceId|PrimitiveId|InvocationId|Layer|ViewportIndex|TessLevelOuter|TessLevelInner|TessCoord|PatchVertices|FragCoord|PointCoord|FrontFacing|SampleId|SamplePosition|SampleMask|FragDepth|HelperInvocation|NumWorkgroups|WorkgroupSize|WorkgroupId|LocalInvocationId|GlobalInvocationId|LocalInvocationIndex|WorkDim|GlobalSize|EnqueuedWorkgroupSize|GlobalOffset|GlobalLinearId|SubgroupSize|SubgroupMaxSize|NumSubgroups|NumEnqueuedSubgroups|SubgroupId|SubgroupLocalInvocationId|VertexIndex|InstanceIndex|SubgroupEqMask|SubgroupGeMask|SubgroupGtMask|SubgroupLeMask|SubgroupLtMask|SubgroupEqMaskKHR|SubgroupGeMaskKHR|SubgroupGtMaskKHR|SubgroupLeMaskKHR|SubgroupLtMaskKHR|BaseVertex|BaseInstance|DrawIndex|DeviceIndex|ViewIndex|BaryCoordNoPerspAMD|BaryCoordNoPerspCentroidAMD|BaryCoordNoPerspSampleAMD|BaryCoordSmoothAMD|BaryCoordSmoothCentroidAMD|BaryCoordSmoothSampleAMD|BaryCoordPullModelAMD|FragStencilRefEXT|ViewportMaskNV|SecondaryPositionNV|SecondaryViewportMaskNV|PositionPerViewNV|ViewportMaskPerViewNV|FullyCoveredEXT|TaskCountNV|PrimitiveCountNV|PrimitiveIndicesNV|ClipDistancePerViewNV|CullDistancePerViewNV|LayerPerViewNV|MeshViewCountNV|MeshViewIndicesNV|BaryCoordNV|BaryCoordNoPerspNV|FragSizeEXT|FragmentSizeNV|FragInvocationCountEXT|InvocationsPerPixelNV|LaunchIdNV|LaunchSizeNV|WorldRayOriginNV|WorldRayDirectionNV|ObjectRayOriginNV|ObjectRayDirectionNV|RayTminNV|RayTmaxNV|InstanceCustomIndexNV|ObjectToWorldNV|WorldToObjectNV|HitTNV|HitKindNV|IncomingRayFlagsNV|WarpsPerSMNV|SMCountNV|WarpIDNV|SMIDNV)\\b", + "match": "\\b(Position|PointSize|ClipDistance|CullDistance|VertexId|InstanceId|PrimitiveId|InvocationId|Layer|ViewportIndex|TessLevelOuter|TessLevelInner|TessCoord|PatchVertices|FragCoord|PointCoord|FrontFacing|SampleId|SamplePosition|SampleMask|FragDepth|HelperInvocation|NumWorkgroups|WorkgroupSize|WorkgroupId|LocalInvocationId|GlobalInvocationId|LocalInvocationIndex|WorkDim|GlobalSize|EnqueuedWorkgroupSize|GlobalOffset|GlobalLinearId|SubgroupSize|SubgroupMaxSize|NumSubgroups|NumEnqueuedSubgroups|SubgroupId|SubgroupLocalInvocationId|VertexIndex|InstanceIndex|SubgroupEqMask|SubgroupGeMask|SubgroupGtMask|SubgroupLeMask|SubgroupLtMask|SubgroupEqMaskKHR|SubgroupGeMaskKHR|SubgroupGtMaskKHR|SubgroupLeMaskKHR|SubgroupLtMaskKHR|BaseVertex|BaseInstance|DrawIndex|DeviceIndex|ViewIndex|BaryCoordNoPerspAMD|BaryCoordNoPerspCentroidAMD|BaryCoordNoPerspSampleAMD|BaryCoordSmoothAMD|BaryCoordSmoothCentroidAMD|BaryCoordSmoothSampleAMD|BaryCoordPullModelAMD|FragStencilRefEXT|ViewportMaskNV|SecondaryPositionNV|SecondaryViewportMaskNV|PositionPerViewNV|ViewportMaskPerViewNV|FullyCoveredEXT|TaskCountNV|PrimitiveCountNV|PrimitiveIndicesNV|ClipDistancePerViewNV|CullDistancePerViewNV|LayerPerViewNV|MeshViewCountNV|MeshViewIndicesNV|BaryCoordNV|BaryCoordNoPerspNV|FragSizeEXT|FragmentSizeNV|FragInvocationCountEXT|InvocationsPerPixelNV|LaunchIdNV|LaunchIdKHR|LaunchSizeNV|LaunchSizeKHR|WorldRayOriginNV|WorldRayOriginKHR|WorldRayDirectionNV|WorldRayDirectionKHR|ObjectRayOriginNV|ObjectRayOriginKHR|ObjectRayDirectionNV|ObjectRayDirectionKHR|RayTminNV|RayTminKHR|RayTmaxNV|RayTmaxKHR|InstanceCustomIndexNV|InstanceCustomIndexKHR|ObjectToWorldNV|ObjectToWorldKHR|WorldToObjectNV|WorldToObjectKHR|HitTNV|HitTKHR|HitKindNV|HitKindKHR|IncomingRayFlagsNV|IncomingRayFlagsKHR|RayGeometryIndexKHR|WarpsPerSMNV|SMCountNV|WarpIDNV|SMIDNV)\\b", "name": "keyword.spirv" }, "ValueEnum_Scope": { - "match": "\\b(CrossDevice|Device|Workgroup|Subgroup|Invocation|QueueFamily|QueueFamilyKHR)\\b", + "match": "\\b(CrossDevice|Device|Workgroup|Subgroup|Invocation|QueueFamily|QueueFamilyKHR|ShaderCallKHR)\\b", "name": "keyword.spirv" }, "ValueEnum_GroupOperation": { @@ -165,7 +173,19 @@ "name": "keyword.spirv" }, "ValueEnum_Capability": { - "match": "\\b(Matrix|Shader|Geometry|Tessellation|Addresses|Linkage|Kernel|Vector16|Float16Buffer|Float16|Float64|Int64|Int64Atomics|ImageBasic|ImageReadWrite|ImageMipmap|Pipes|Groups|DeviceEnqueue|LiteralSampler|AtomicStorage|Int16|TessellationPointSize|GeometryPointSize|ImageGatherExtended|StorageImageMultisample|UniformBufferArrayDynamicIndexing|SampledImageArrayDynamicIndexing|StorageBufferArrayDynamicIndexing|StorageImageArrayDynamicIndexing|ClipDistance|CullDistance|ImageCubeArray|SampleRateShading|ImageRect|SampledRect|GenericPointer|Int8|InputAttachment|SparseResidency|MinLod|Sampled1D|Image1D|SampledCubeArray|SampledBuffer|ImageBuffer|ImageMSArray|StorageImageExtendedFormats|ImageQuery|DerivativeControl|InterpolationFunction|TransformFeedback|GeometryStreams|StorageImageReadWithoutFormat|StorageImageWriteWithoutFormat|MultiViewport|SubgroupDispatch|NamedBarrier|PipeStorage|GroupNonUniform|GroupNonUniformVote|GroupNonUniformArithmetic|GroupNonUniformBallot|GroupNonUniformShuffle|GroupNonUniformShuffleRelative|GroupNonUniformClustered|GroupNonUniformQuad|ShaderLayer|ShaderViewportIndex|SubgroupBallotKHR|DrawParameters|SubgroupVoteKHR|StorageBuffer16BitAccess|StorageUniformBufferBlock16|UniformAndStorageBuffer16BitAccess|StorageUniform16|StoragePushConstant16|StorageInputOutput16|DeviceGroup|MultiView|VariablePointersStorageBuffer|VariablePointers|AtomicStorageOps|SampleMaskPostDepthCoverage|StorageBuffer8BitAccess|UniformAndStorageBuffer8BitAccess|StoragePushConstant8|DenormPreserve|DenormFlushToZero|SignedZeroInfNanPreserve|RoundingModeRTE|RoundingModeRTZ|Float16ImageAMD|ImageGatherBiasLodAMD|FragmentMaskAMD|StencilExportEXT|ImageReadWriteLodAMD|ShaderClockKHR|SampleMaskOverrideCoverageNV|GeometryShaderPassthroughNV|ShaderViewportIndexLayerEXT|ShaderViewportIndexLayerNV|ShaderViewportMaskNV|ShaderStereoViewNV|PerViewAttributesNV|FragmentFullyCoveredEXT|MeshShadingNV|ImageFootprintNV|FragmentBarycentricNV|ComputeDerivativeGroupQuadsNV|FragmentDensityEXT|ShadingRateNV|GroupNonUniformPartitionedNV|ShaderNonUniform|ShaderNonUniformEXT|RuntimeDescriptorArray|RuntimeDescriptorArrayEXT|InputAttachmentArrayDynamicIndexing|InputAttachmentArrayDynamicIndexingEXT|UniformTexelBufferArrayDynamicIndexing|UniformTexelBufferArrayDynamicIndexingEXT|StorageTexelBufferArrayDynamicIndexing|StorageTexelBufferArrayDynamicIndexingEXT|UniformBufferArrayNonUniformIndexing|UniformBufferArrayNonUniformIndexingEXT|SampledImageArrayNonUniformIndexing|SampledImageArrayNonUniformIndexingEXT|StorageBufferArrayNonUniformIndexing|StorageBufferArrayNonUniformIndexingEXT|StorageImageArrayNonUniformIndexing|StorageImageArrayNonUniformIndexingEXT|InputAttachmentArrayNonUniformIndexing|InputAttachmentArrayNonUniformIndexingEXT|UniformTexelBufferArrayNonUniformIndexing|UniformTexelBufferArrayNonUniformIndexingEXT|StorageTexelBufferArrayNonUniformIndexing|StorageTexelBufferArrayNonUniformIndexingEXT|RayTracingNV|VulkanMemoryModel|VulkanMemoryModelKHR|VulkanMemoryModelDeviceScope|VulkanMemoryModelDeviceScopeKHR|PhysicalStorageBufferAddresses|PhysicalStorageBufferAddressesEXT|ComputeDerivativeGroupLinearNV|CooperativeMatrixNV|FragmentShaderSampleInterlockEXT|FragmentShaderShadingRateInterlockEXT|ShaderSMBuiltinsNV|FragmentShaderPixelInterlockEXT|DemoteToHelperInvocationEXT|SubgroupShuffleINTEL|SubgroupBufferBlockIOINTEL|SubgroupImageBlockIOINTEL|SubgroupImageMediaBlockIOINTEL|IntegerFunctions2INTEL|SubgroupAvcMotionEstimationINTEL|SubgroupAvcMotionEstimationIntraINTEL|SubgroupAvcMotionEstimationChromaINTEL)\\b", + "match": "\\b(Matrix|Shader|Geometry|Tessellation|Addresses|Linkage|Kernel|Vector16|Float16Buffer|Float16|Float64|Int64|Int64Atomics|ImageBasic|ImageReadWrite|ImageMipmap|Pipes|Groups|DeviceEnqueue|LiteralSampler|AtomicStorage|Int16|TessellationPointSize|GeometryPointSize|ImageGatherExtended|StorageImageMultisample|UniformBufferArrayDynamicIndexing|SampledImageArrayDynamicIndexing|StorageBufferArrayDynamicIndexing|StorageImageArrayDynamicIndexing|ClipDistance|CullDistance|ImageCubeArray|SampleRateShading|ImageRect|SampledRect|GenericPointer|Int8|InputAttachment|SparseResidency|MinLod|Sampled1D|Image1D|SampledCubeArray|SampledBuffer|ImageBuffer|ImageMSArray|StorageImageExtendedFormats|ImageQuery|DerivativeControl|InterpolationFunction|TransformFeedback|GeometryStreams|StorageImageReadWithoutFormat|StorageImageWriteWithoutFormat|MultiViewport|SubgroupDispatch|NamedBarrier|PipeStorage|GroupNonUniform|GroupNonUniformVote|GroupNonUniformArithmetic|GroupNonUniformBallot|GroupNonUniformShuffle|GroupNonUniformShuffleRelative|GroupNonUniformClustered|GroupNonUniformQuad|ShaderLayer|ShaderViewportIndex|SubgroupBallotKHR|DrawParameters|SubgroupVoteKHR|StorageBuffer16BitAccess|StorageUniformBufferBlock16|UniformAndStorageBuffer16BitAccess|StorageUniform16|StoragePushConstant16|StorageInputOutput16|DeviceGroup|MultiView|VariablePointersStorageBuffer|VariablePointers|AtomicStorageOps|SampleMaskPostDepthCoverage|StorageBuffer8BitAccess|UniformAndStorageBuffer8BitAccess|StoragePushConstant8|DenormPreserve|DenormFlushToZero|SignedZeroInfNanPreserve|RoundingModeRTE|RoundingModeRTZ|RayQueryProvisionalKHR|RayTraversalPrimitiveCullingProvisionalKHR|Float16ImageAMD|ImageGatherBiasLodAMD|FragmentMaskAMD|StencilExportEXT|ImageReadWriteLodAMD|ShaderClockKHR|SampleMaskOverrideCoverageNV|GeometryShaderPassthroughNV|ShaderViewportIndexLayerEXT|ShaderViewportIndexLayerNV|ShaderViewportMaskNV|ShaderStereoViewNV|PerViewAttributesNV|FragmentFullyCoveredEXT|MeshShadingNV|ImageFootprintNV|FragmentBarycentricNV|ComputeDerivativeGroupQuadsNV|FragmentDensityEXT|ShadingRateNV|GroupNonUniformPartitionedNV|ShaderNonUniform|ShaderNonUniformEXT|RuntimeDescriptorArray|RuntimeDescriptorArrayEXT|InputAttachmentArrayDynamicIndexing|InputAttachmentArrayDynamicIndexingEXT|UniformTexelBufferArrayDynamicIndexing|UniformTexelBufferArrayDynamicIndexingEXT|StorageTexelBufferArrayDynamicIndexing|StorageTexelBufferArrayDynamicIndexingEXT|UniformBufferArrayNonUniformIndexing|UniformBufferArrayNonUniformIndexingEXT|SampledImageArrayNonUniformIndexing|SampledImageArrayNonUniformIndexingEXT|StorageBufferArrayNonUniformIndexing|StorageBufferArrayNonUniformIndexingEXT|StorageImageArrayNonUniformIndexing|StorageImageArrayNonUniformIndexingEXT|InputAttachmentArrayNonUniformIndexing|InputAttachmentArrayNonUniformIndexingEXT|UniformTexelBufferArrayNonUniformIndexing|UniformTexelBufferArrayNonUniformIndexingEXT|StorageTexelBufferArrayNonUniformIndexing|StorageTexelBufferArrayNonUniformIndexingEXT|RayTracingNV|VulkanMemoryModel|VulkanMemoryModelKHR|VulkanMemoryModelDeviceScope|VulkanMemoryModelDeviceScopeKHR|PhysicalStorageBufferAddresses|PhysicalStorageBufferAddressesEXT|ComputeDerivativeGroupLinearNV|RayTracingProvisionalKHR|CooperativeMatrixNV|FragmentShaderSampleInterlockEXT|FragmentShaderShadingRateInterlockEXT|ShaderSMBuiltinsNV|FragmentShaderPixelInterlockEXT|DemoteToHelperInvocationEXT|SubgroupShuffleINTEL|SubgroupBufferBlockIOINTEL|SubgroupImageBlockIOINTEL|SubgroupImageMediaBlockIOINTEL|IntegerFunctions2INTEL|SubgroupAvcMotionEstimationINTEL|SubgroupAvcMotionEstimationIntraINTEL|SubgroupAvcMotionEstimationChromaINTEL)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_RayQueryIntersection": { + "match": "\\b(RayQueryCandidateIntersectionKHR|RayQueryCommittedIntersectionKHR)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_RayQueryCommittedIntersectionType": { + "match": "\\b(RayQueryCommittedIntersectionNoneKHR|RayQueryCommittedIntersectionTriangleKHR|RayQueryCommittedIntersectionGeneratedKHR)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_RayQueryCandidateIntersectionType": { + "match": "\\b(RayQueryCandidateIntersectionTriangleKHR|RayQueryCandidateIntersectionAABBKHR)\\b", "name": "keyword.spirv" }, "BitEnum_DebugInfoFlags": { diff --git a/utils/vscode/src/langsvr.go b/utils/vscode/src/langsvr.go index d1b80dca..b76e35f7 100644 --- a/utils/vscode/src/langsvr.go +++ b/utils/vscode/src/langsvr.go @@ -28,11 +28,15 @@ import ( "sync" "unicode/utf8" - "./parser" - "./schema" + "github.com/KhronosGroup/SPIRV-Tools/utils/vscode/src/parser" + "github.com/KhronosGroup/SPIRV-Tools/utils/vscode/src/schema" - "./lsp/jsonrpc2" - lsp "./lsp/protocol" + "github.com/KhronosGroup/SPIRV-Tools/utils/vscode/src/lsp/jsonrpc2" + lsp "github.com/KhronosGroup/SPIRV-Tools/utils/vscode/src/lsp/protocol" +) + +const ( + enableDebugLogging = false ) // rSpy is a reader 'spy' that wraps an io.Reader, and logs all data that passes @@ -63,12 +67,13 @@ func (s wSpy) Write(p []byte) (n int, err error) { // main entry point. func main() { - // create a log file in the executable's directory. - if logfile, err := os.Create(path.Join(path.Dir(os.Args[0]), "log.txt")); err == nil { - defer logfile.Close() - log.SetOutput(logfile) - } else { - log.SetOutput(ioutil.Discard) + log.SetOutput(ioutil.Discard) + if enableDebugLogging { + // create a log file in the executable's directory. + if logfile, err := os.Create(path.Join(path.Dir(os.Args[0]), "log.txt")); err == nil { + defer logfile.Close() + log.SetOutput(logfile) + } } log.Println("language server started") @@ -407,13 +412,15 @@ func (s *server) Formatting(ctx context.Context, p *lsp.DocumentFormattingParams } } - // Every good file ends with a new line. - sb.WriteString("\n") + formatted := sb.String() + + // Every good file ends with a single new line. + formatted = strings.TrimRight(formatted, "\n") + "\n" return []lsp.TextEdit{ - lsp.TextEdit{ + { Range: rangeToLSP(f.fullRange), - NewText: sb.String(), + NewText: formatted, }, }, nil } diff --git a/utils/vscode/src/lsp/protocol/log.go b/utils/vscode/src/lsp/protocol/log.go index f245881e..2fd7bbbd 100644 --- a/utils/vscode/src/lsp/protocol/log.go +++ b/utils/vscode/src/lsp/protocol/log.go @@ -23,7 +23,7 @@ import ( "sync" "time" - "../jsonrpc2" + "github.com/KhronosGroup/SPIRV-Tools/utils/vscode/src/lsp/jsonrpc2" ) type loggingStream struct { diff --git a/utils/vscode/src/lsp/protocol/protocol.go b/utils/vscode/src/lsp/protocol/protocol.go index e396c832..886b0aaa 100644 --- a/utils/vscode/src/lsp/protocol/protocol.go +++ b/utils/vscode/src/lsp/protocol/protocol.go @@ -19,7 +19,7 @@ import ( "encoding/json" "log" - "../jsonrpc2" + "github.com/KhronosGroup/SPIRV-Tools/utils/vscode/src/lsp/jsonrpc2" ) const ( diff --git a/utils/vscode/src/lsp/protocol/span.go b/utils/vscode/src/lsp/protocol/span.go index 33cc2a65..799c2288 100644 --- a/utils/vscode/src/lsp/protocol/span.go +++ b/utils/vscode/src/lsp/protocol/span.go @@ -19,7 +19,8 @@ package protocol import ( "fmt" - "../span" + "github.com/KhronosGroup/SPIRV-Tools/utils/vscode/src/lsp/span" + errors "golang.org/x/xerrors" ) diff --git a/utils/vscode/src/lsp/protocol/tsclient.go b/utils/vscode/src/lsp/protocol/tsclient.go index 2f9beef4..f68d63d0 100644 --- a/utils/vscode/src/lsp/protocol/tsclient.go +++ b/utils/vscode/src/lsp/protocol/tsclient.go @@ -19,7 +19,7 @@ import ( "encoding/json" "log" - "../jsonrpc2" + "github.com/KhronosGroup/SPIRV-Tools/utils/vscode/src/lsp/jsonrpc2" ) type Client interface { diff --git a/utils/vscode/src/lsp/protocol/tsserver.go b/utils/vscode/src/lsp/protocol/tsserver.go index d7605012..37e8c6a7 100644 --- a/utils/vscode/src/lsp/protocol/tsserver.go +++ b/utils/vscode/src/lsp/protocol/tsserver.go @@ -19,7 +19,7 @@ import ( "encoding/json" "log" - "../jsonrpc2" + "github.com/KhronosGroup/SPIRV-Tools/utils/vscode/src/lsp/jsonrpc2" ) type Server interface { diff --git a/utils/vscode/src/parser/parser.go b/utils/vscode/src/parser/parser.go index 260a616c..cc6f3332 100644 --- a/utils/vscode/src/parser/parser.go +++ b/utils/vscode/src/parser/parser.go @@ -23,7 +23,7 @@ import ( "unicode" "unicode/utf8" - "../schema" + "github.com/KhronosGroup/SPIRV-Tools/utils/vscode/src/schema" ) // Type is an enumerator of token types. diff --git a/utils/vscode/src/schema/schema.go b/utils/vscode/src/schema/schema.go index 0fde3fea..ed02de4e 100755 --- a/utils/vscode/src/schema/schema.go +++ b/utils/vscode/src/schema/schema.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Generated by gen-grammar.go --template=../schema/schema.go.tmpl --out=../schema/schema.go +// Generated by gen-grammar.go --template=./src/schema/schema.go.tmpl --out=./src/schema/schema.go // Do not modify this file directly. package schema @@ -477,7 +477,7 @@ var ( "OpTraceRayKHR": OpTraceRayKHR, "OpTypeAccelerationStructureNV": OpTypeAccelerationStructureNV, "OpTypeAccelerationStructureKHR": OpTypeAccelerationStructureKHR, - "OpTypeRayQueryKHR": OpTypeRayQueryKHR, + "OpTypeRayQueryProvisionalKHR": OpTypeRayQueryProvisionalKHR, "OpRayQueryInitializeKHR": OpRayQueryInitializeKHR, "OpRayQueryTerminateKHR": OpRayQueryTerminateKHR, "OpRayQueryGenerateIntersectionKHR": OpRayQueryGenerateIntersectionKHR, @@ -10807,8 +10807,8 @@ var ( }, }, } - OpTypeRayQueryKHR = &Opcode { - Opname: "OpTypeRayQueryKHR", + OpTypeRayQueryProvisionalKHR = &Opcode { + Opname: "OpTypeRayQueryProvisionalKHR", Class: "Reserved", Opcode: 4472, Operands: []Operand { @@ -20183,77 +20183,77 @@ var ( Enumerant{ Enumerant: "NoneKHR", Value: 0x0000, - Capabilities: []string{"RayQueryKHR","RayTracingKHR",}, + Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ Enumerant: "OpaqueKHR", Value: 0x0001, - Capabilities: []string{"RayQueryKHR","RayTracingKHR",}, + Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ Enumerant: "NoOpaqueKHR", Value: 0x0002, - Capabilities: []string{"RayQueryKHR","RayTracingKHR",}, + Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ Enumerant: "TerminateOnFirstHitKHR", Value: 0x0004, - Capabilities: []string{"RayQueryKHR","RayTracingKHR",}, + Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ Enumerant: "SkipClosestHitShaderKHR", Value: 0x0008, - Capabilities: []string{"RayQueryKHR","RayTracingKHR",}, + Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ Enumerant: "CullBackFacingTrianglesKHR", Value: 0x0010, - Capabilities: []string{"RayQueryKHR","RayTracingKHR",}, + Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ Enumerant: "CullFrontFacingTrianglesKHR", Value: 0x0020, - Capabilities: []string{"RayQueryKHR","RayTracingKHR",}, + Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ Enumerant: "CullOpaqueKHR", Value: 0x0040, - Capabilities: []string{"RayQueryKHR","RayTracingKHR",}, + Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ Enumerant: "CullNoOpaqueKHR", Value: 0x0080, - Capabilities: []string{"RayQueryKHR","RayTracingKHR",}, + Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ Enumerant: "SkipTrianglesKHR", Value: 0x0100, - Capabilities: []string{"RayTraversalPrimitiveCullingKHR",}, + Capabilities: []string{"RayTraversalPrimitiveCullingProvisionalKHR",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ Enumerant: "SkipAABBsKHR", Value: 0x0200, - Capabilities: []string{"RayTraversalPrimitiveCullingKHR",}, + Capabilities: []string{"RayTraversalPrimitiveCullingProvisionalKHR",}, Parameters: []Parameter{}, Version: "", }, @@ -20379,84 +20379,84 @@ var ( Enumerant{ Enumerant: "RayGenerationNV", Value: 5313, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "RayGenerationKHR", Value: 5313, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "IntersectionNV", Value: 5314, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "IntersectionKHR", Value: 5314, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "AnyHitNV", Value: 5315, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "AnyHitKHR", Value: 5315, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "ClosestHitNV", Value: 5316, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "ClosestHitKHR", Value: 5316, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "MissNV", Value: 5317, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "MissKHR", Value: 5317, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "CallableNV", Value: 5318, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "CallableKHR", Value: 5318, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, @@ -21044,84 +21044,84 @@ var ( Enumerant{ Enumerant: "CallableDataNV", Value: 5328, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "CallableDataKHR", Value: 5328, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "IncomingCallableDataNV", Value: 5329, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "IncomingCallableDataKHR", Value: 5329, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "RayPayloadNV", Value: 5338, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "RayPayloadKHR", Value: 5338, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "HitAttributeNV", Value: 5339, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "HitAttributeKHR", Value: 5339, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "IncomingRayPayloadNV", Value: 5342, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "IncomingRayPayloadKHR", Value: 5342, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "ShaderRecordBufferNV", Value: 5343, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "ShaderRecordBufferKHR", Value: 5343, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, @@ -22507,7 +22507,7 @@ var ( Enumerant{ Enumerant: "PrimitiveId", Value: 7, - Capabilities: []string{"Geometry","Tessellation","RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"Geometry","Tessellation","RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "", }, @@ -23053,203 +23053,203 @@ var ( Enumerant{ Enumerant: "LaunchIdNV", Value: 5319, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "LaunchIdKHR", Value: 5319, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "LaunchSizeNV", Value: 5320, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "LaunchSizeKHR", Value: 5320, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "WorldRayOriginNV", Value: 5321, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "WorldRayOriginKHR", Value: 5321, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "WorldRayDirectionNV", Value: 5322, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "WorldRayDirectionKHR", Value: 5322, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "ObjectRayOriginNV", Value: 5323, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "ObjectRayOriginKHR", Value: 5323, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "ObjectRayDirectionNV", Value: 5324, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "ObjectRayDirectionKHR", Value: 5324, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "RayTminNV", Value: 5325, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "RayTminKHR", Value: 5325, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "RayTmaxNV", Value: 5326, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "RayTmaxKHR", Value: 5326, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "InstanceCustomIndexNV", Value: 5327, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "InstanceCustomIndexKHR", Value: 5327, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "ObjectToWorldNV", Value: 5330, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "ObjectToWorldKHR", Value: 5330, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "WorldToObjectNV", Value: 5331, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "WorldToObjectKHR", Value: 5331, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "HitTNV", Value: 5332, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "HitTKHR", Value: 5332, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "HitKindNV", Value: 5333, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "HitKindKHR", Value: 5333, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "IncomingRayFlagsNV", Value: 5351, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "IncomingRayFlagsKHR", Value: 5351, - Capabilities: []string{"RayTracingNV","RayTracingKHR",}, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ Enumerant: "RayGeometryIndexKHR", Value: 5352, - Capabilities: []string{"RayTracingKHR",}, + Capabilities: []string{"RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, @@ -23340,7 +23340,7 @@ var ( Enumerant{ Enumerant: "ShaderCallKHR", Value: 6, - Capabilities: []string{"RayTracingKHR",}, + Capabilities: []string{"RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "", }, @@ -24080,16 +24080,16 @@ var ( Version: "1.4", }, Enumerant{ - Enumerant: "RayQueryKHR", + Enumerant: "RayQueryProvisionalKHR", Value: 4471, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, Version: "None", }, Enumerant{ - Enumerant: "RayTraversalPrimitiveCullingKHR", + Enumerant: "RayTraversalPrimitiveCullingProvisionalKHR", Value: 4478, - Capabilities: []string{"RayQueryKHR","RayTracingKHR",}, + Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",}, Parameters: []Parameter{}, Version: "None", }, @@ -24465,7 +24465,7 @@ var ( Version: "None", }, Enumerant{ - Enumerant: "RayTracingKHR", + Enumerant: "RayTracingProvisionalKHR", Value: 5353, Capabilities: []string{"Shader",}, Parameters: []Parameter{}, @@ -24579,14 +24579,14 @@ var ( Enumerant{ Enumerant: "RayQueryCandidateIntersectionKHR", Value: 0, - Capabilities: []string{"RayQueryKHR",}, + Capabilities: []string{"RayQueryProvisionalKHR",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ Enumerant: "RayQueryCommittedIntersectionKHR", Value: 1, - Capabilities: []string{"RayQueryKHR",}, + Capabilities: []string{"RayQueryProvisionalKHR",}, Parameters: []Parameter{}, Version: "", }, @@ -24600,21 +24600,21 @@ var ( Enumerant{ Enumerant: "RayQueryCommittedIntersectionNoneKHR", Value: 0, - Capabilities: []string{"RayQueryKHR",}, + Capabilities: []string{"RayQueryProvisionalKHR",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ Enumerant: "RayQueryCommittedIntersectionTriangleKHR", Value: 1, - Capabilities: []string{"RayQueryKHR",}, + Capabilities: []string{"RayQueryProvisionalKHR",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ Enumerant: "RayQueryCommittedIntersectionGeneratedKHR", Value: 2, - Capabilities: []string{"RayQueryKHR",}, + Capabilities: []string{"RayQueryProvisionalKHR",}, Parameters: []Parameter{}, Version: "", }, @@ -24628,14 +24628,14 @@ var ( Enumerant{ Enumerant: "RayQueryCandidateIntersectionTriangleKHR", Value: 0, - Capabilities: []string{"RayQueryKHR",}, + Capabilities: []string{"RayQueryProvisionalKHR",}, Parameters: []Parameter{}, Version: "", }, Enumerant{ Enumerant: "RayQueryCandidateIntersectionAABBKHR", Value: 1, - Capabilities: []string{"RayQueryKHR",}, + Capabilities: []string{"RayQueryProvisionalKHR",}, Parameters: []Parameter{}, Version: "", }, diff --git a/utils/vscode/src/tools/gen-grammar.go b/utils/vscode/src/tools/gen-grammar.go index 200f6959..a1289ef9 100644 --- a/utils/vscode/src/tools/gen-grammar.go +++ b/utils/vscode/src/tools/gen-grammar.go @@ -31,7 +31,7 @@ import ( "github.com/pkg/errors" - "../grammar" + "github.com/KhronosGroup/SPIRV-Tools/utils/vscode/src/grammar" ) type grammarDefinition struct { @@ -54,7 +54,7 @@ var ( url: "https://raw.githubusercontent.com/KhronosGroup/SPIRV-Headers/master/include/spirv/unified1/extinst.opencl.std.100.grammar.json", }, { name: "OpenCL.DebugInfo.100", - url: "https://raw.githubusercontent.com/KhronosGroup/SPIRV-Tools/master/source/extinst.opencl.debuginfo.100.grammar.json", + url: "https://raw.githubusercontent.com/KhronosGroup/SPIRV-Headers/master/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json", }, } @@ -106,7 +106,7 @@ func run() error { for _, ext := range extensionGrammars { root, err := parseGrammar(ext) if err != nil { - return errors.Wrap(err, "Failed to parse extension grammar file") + return errors.Wrap(err, "Failed to parse extension grammar file: "+ext.name) } args.Extensions = append(args.Extensions, extension{Root: root, Name: ext.name}) args.All.Instructions = append(args.All.Instructions, root.Instructions...) |